From f3bddf79ff717bfe56fdb77a71c5f0a42257eeca Mon Sep 17 00:00:00 2001 From: gtripoli Date: Sat, 7 Mar 2026 17:00:02 +0100 Subject: [PATCH 01/12] Add MySQL and MariaDB procedure CRUD with integration coverage AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- PROJECT_STATUS.md | 10 ++--- ROADMAP.md | 14 +++--- structures/engines/mariadb/context.py | 44 +++++++++++++++++-- structures/engines/mariadb/database.py | 37 +++++++++++++++- structures/engines/mysql/context.py | 44 +++++++++++++++++-- structures/engines/mysql/database.py | 36 +++++++++++++++ tests/engines/base_procedure_tests.py | 30 +++++++++++++ .../engines/mariadb/test_integration_suite.py | 12 +++++ tests/engines/mysql/test_integration_suite.py | 12 +++++ 9 files changed, 218 insertions(+), 21 deletions(-) diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index c2efc22..b8035a9 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -21,6 +21,8 @@ | Area | Current State | |------|---------------| +| **MySQL Procedure** | Class + CRUD methods exist, context introspection exists, integration tests added, broader validation still ongoing. | +| **MariaDB Procedure** | Class + CRUD methods exist, context introspection exists, integration tests added, broader validation still ongoing. | | **PostgreSQL Function** | Class + CRUD methods exist, context introspection exists, still considered under validation. | | **PostgreSQL Procedure** | Class + CRUD methods exist, context introspection exists, still considered under validation. | | **Check Constraints (MySQL/MariaDB/PostgreSQL)** | Engine classes and introspection exist, cross-version validation still needed. | @@ -69,7 +71,7 @@ | Table / Column / Index / FK / Record | ✅ | ✅ | ✅ | ✅ | Stable core workflow. | | View / Trigger / Function | ✅ | ✅ | ✅ | ✅ | Implemented in engine layer. | | Check Constraint | 🟡 | 🟡 | 🟡 | 🟡 | Implemented (`MySQLCheck` + `get_checks()`), validation ongoing. | -| Procedure | ❌ | ❌ | ❌ | ❌ | `build_empty_procedure` still not implemented. | +| Procedure | 🟡 | 🟡 | 🟡 | 🟡 | Implemented (`MySQLProcedure` + `get_procedures()`), broader validation ongoing. | | Database Create/Drop | ❌ | ✅ | ❌ | ❌ | Read-only listing in context. | --- @@ -81,7 +83,7 @@ | Table / Column / Index / FK / Record | ✅ | ✅ | ✅ | ✅ | Stable core workflow. | | View / Trigger / Function | ✅ | ✅ | ✅ | ✅ | Implemented in engine layer. | | Check Constraint | 🟡 | 🟡 | 🟡 | 🟡 | Implemented (`MariaDBCheck` + `get_checks()`), validation ongoing. | -| Procedure | ❌ | ❌ | ❌ | ❌ | `build_empty_procedure` still not implemented. | +| Procedure | 🟡 | 🟡 | 🟡 | 🟡 | Implemented (`MariaDBProcedure` + `get_procedures()`), broader validation ongoing. | | Database Create/Drop | ❌ | ✅ | ❌ | ❌ | Read-only listing in context. | --- @@ -144,9 +146,7 @@ ### Priority B — Close Engine Gaps -1. MySQL Procedure implementation. -2. MariaDB Procedure implementation. -3. Database create/drop methods in engine contexts. +1. Database create/drop methods in engine contexts. ### Priority C — UI Completeness diff --git a/ROADMAP.md b/ROADMAP.md index 2b898e7..11ae0cd 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -57,13 +57,13 @@ This roadmap reflects the current project state and separates: ## 🟡 P1 - Engine Gaps -- [ ] **MySQL Procedure implementation** - - **Current blocker:** `build_empty_procedure` not implemented in MySQL context. - - **Files:** `structures/engines/mysql/context.py`, `structures/engines/mysql/database.py` +- [x] **MySQL Procedure implementation** (PARTIAL) + - **Status:** Engine CRUD + introspection implemented, integration tests added. + - **Files:** `structures/engines/mysql/context.py`, `structures/engines/mysql/database.py`, `tests/engines/mysql/test_integration_suite.py`, `tests/engines/base_procedure_tests.py` -- [ ] **MariaDB Procedure implementation** - - **Current blocker:** `build_empty_procedure` not implemented in MariaDB context. - - **Files:** `structures/engines/mariadb/context.py`, `structures/engines/mariadb/database.py` +- [x] **MariaDB Procedure implementation** (PARTIAL) + - **Status:** Engine CRUD + introspection implemented, integration tests added. + - **Files:** `structures/engines/mariadb/context.py`, `structures/engines/mariadb/database.py`, `tests/engines/mariadb/test_integration_suite.py`, `tests/engines/base_procedure_tests.py` - [ ] **Database create/drop API parity** - **Current state:** list/read available, lifecycle operations missing in contexts. @@ -118,7 +118,7 @@ Before moving a PARTIAL item to DONE: ### Current Status - **P0 implemented (partial):** 4/4 -- **P1 gaps closed:** 0/3 +- **P1 gaps closed:** 2/3 - **P2 UI tasks complete:** 1/5 - **P3 advanced tasks complete:** 0/6 diff --git a/structures/engines/mariadb/context.py b/structures/engines/mariadb/context.py index 14238b8..5750d37 100755 --- a/structures/engines/mariadb/context.py +++ b/structures/engines/mariadb/context.py @@ -25,6 +25,7 @@ MariaDBColumn, MariaDBIndex, MariaDBForeignKey, + MariaDBProcedure, MariaDBRecord, MariaDBView, MariaDBTrigger, @@ -258,12 +259,36 @@ def get_databases(self) -> list[SQLDatabase]: total_bytes=float(row["total_bytes"]), context=self, get_tables_handler=self.get_tables, + get_procedures_handler=self.get_procedures, get_views_handler=self.get_views, get_triggers_handler=self.get_triggers, ) ) return results + def get_procedures(self, database: SQLDatabase) -> list[MariaDBProcedure]: + results: list[MariaDBProcedure] = [] + self.execute( + f""" + SELECT ROUTINE_NAME + FROM INFORMATION_SCHEMA.ROUTINES + WHERE ROUTINE_SCHEMA = '{database.name}' AND ROUTINE_TYPE = 'PROCEDURE' + ORDER BY ROUTINE_NAME + """ + ) + for i, result in enumerate(self.fetchall()): + results.append( + MariaDBProcedure( + id=i, + name=result["ROUTINE_NAME"], + database=database, + parameters="", + statement="", + ) + ) + + return results + def get_views(self, database: SQLDatabase): results: list[MariaDBView] = [] self.execute( @@ -442,7 +467,7 @@ def get_indexes(self, table: SQLTable) -> list[SQLIndex]: return results - def get_checks(self, table: MariaDBTable) -> list[MariaDBCheck]: + def get_checks(self, table: MariaDBTable) -> list["MariaDBCheck"]: from structures.engines.mariadb.database import MariaDBCheck if table is None or table.is_new: @@ -612,7 +637,7 @@ def build_empty_check( name: Optional[str] = None, expression: Optional[str] = None, **default_values, - ) -> MariaDBCheck: + ) -> "MariaDBCheck": from structures.engines.mariadb.database import MariaDBCheck id = MariaDBContext.get_temporary_id(table.checks) @@ -697,8 +722,19 @@ def build_empty_function( def build_empty_procedure( self, database: SQLDatabase, /, name: Optional[str] = None, **default_values - ): - raise NotImplementedError("MariaDB Procedure not implemented yet") + ) -> MariaDBProcedure: + id = MariaDBContext.get_temporary_id(database.procedures) + + if name is None: + name = f"procedure_{id}" + + return MariaDBProcedure( + id=id, + name=name, + database=database, + parameters=default_values.get("parameters", ""), + statement=default_values.get("statement", ""), + ) def build_empty_trigger( self, database: SQLDatabase, /, name: Optional[str] = None, **default_values diff --git a/structures/engines/mariadb/database.py b/structures/engines/mariadb/database.py index 2a0f2de..0a63388 100644 --- a/structures/engines/mariadb/database.py +++ b/structures/engines/mariadb/database.py @@ -5,7 +5,7 @@ from structures.helpers import merge_original_current from structures.engines.context import QUERY_LOGS -from structures.engines.database import SQLTable, SQLColumn, SQLIndex, SQLForeignKey, SQLRecord, SQLView, SQLTrigger, SQLFunction, SQLDatabase, SQLCheck +from structures.engines.database import SQLTable, SQLCheck, SQLColumn, SQLIndex, SQLView, SQLRecord, SQLTrigger, SQLFunction, SQLDatabase, SQLForeignKey, SQLProcedure from structures.engines.mariadb.indextype import MariaDBIndexType from structures.engines.mariadb.builder import MariaDBColumnBuilder, MariaDBIndexBuilder @@ -381,3 +381,38 @@ def drop(self) -> bool: def alter(self) -> bool: self.drop() return self.create() + + +@dataclasses.dataclass(eq=False) +class MariaDBProcedure(SQLProcedure): + parameters: str = "" + statement: str = "" + + @staticmethod + def _render_body(statement: str) -> str: + body = statement.strip() + if body.endswith(";"): + return body + + return f"{body};" + + def create(self) -> bool: + body = self._render_body(self.statement) + query = f""" + CREATE PROCEDURE {self.fully_qualified_name}({self.parameters}) + BEGIN + {body} + END + """ + self.database.context.set_database(self.database) + return self.database.context.execute(query) + + def drop(self) -> bool: + self.database.context.set_database(self.database) + return self.database.context.execute( + f"DROP PROCEDURE IF EXISTS {self.fully_qualified_name}" + ) + + def alter(self) -> bool: + self.drop() + return self.create() diff --git a/structures/engines/mysql/context.py b/structures/engines/mysql/context.py index 65a2aee..7af8478 100644 --- a/structures/engines/mysql/context.py +++ b/structures/engines/mysql/context.py @@ -25,6 +25,7 @@ MySQLDatabase, MySQLForeignKey, MySQLIndex, + MySQLProcedure, MySQLRecord, MySQLTable, MySQLTrigger, @@ -228,12 +229,36 @@ def get_databases(self) -> list[SQLDatabase]: total_bytes=float(row["total_bytes"]), context=self, get_tables_handler=self.get_tables, + get_procedures_handler=self.get_procedures, get_views_handler=self.get_views, get_triggers_handler=self.get_triggers, ) ) return results + def get_procedures(self, database: SQLDatabase) -> list[MySQLProcedure]: + results: list[MySQLProcedure] = [] + self.execute( + f""" + SELECT ROUTINE_NAME + FROM INFORMATION_SCHEMA.ROUTINES + WHERE ROUTINE_SCHEMA = '{database.name}' AND ROUTINE_TYPE = 'PROCEDURE' + ORDER BY ROUTINE_NAME + """ + ) + for i, result in enumerate(self.fetchall()): + results.append( + MySQLProcedure( + id=i, + name=result["ROUTINE_NAME"], + database=database, + parameters="", + statement="", + ) + ) + + return results + def get_views(self, database: SQLDatabase): results: list[MySQLView] = [] self.execute( @@ -412,7 +437,7 @@ def get_indexes(self, table: SQLTable) -> list[SQLIndex]: return results - def get_checks(self, table: MySQLTable) -> list[MySQLCheck]: + def get_checks(self, table: MySQLTable) -> list["MySQLCheck"]: from structures.engines.mysql.database import MySQLCheck if table is None or table.is_new: @@ -583,7 +608,7 @@ def build_empty_check( name: Optional[str] = None, expression: Optional[str] = None, **default_values, - ) -> MySQLCheck: + ) -> "MySQLCheck": from structures.engines.mysql.database import MySQLCheck id = MySQLContext.get_temporary_id(table.checks) @@ -666,8 +691,19 @@ def build_empty_function( def build_empty_procedure( self, database: SQLDatabase, /, name: Optional[str] = None, **default_values - ): - raise NotImplementedError("MySQL Procedure not implemented yet") + ) -> MySQLProcedure: + id = MySQLContext.get_temporary_id(database.procedures) + + if name is None: + name = f"procedure_{id}" + + return MySQLProcedure( + id=id, + name=name, + database=database, + parameters=default_values.get("parameters", ""), + statement=default_values.get("statement", ""), + ) def build_empty_trigger( self, database: SQLDatabase, /, name: Optional[str] = None, **default_values diff --git a/structures/engines/mysql/database.py b/structures/engines/mysql/database.py index e84de88..250ee33 100644 --- a/structures/engines/mysql/database.py +++ b/structures/engines/mysql/database.py @@ -12,6 +12,7 @@ SQLForeignKey, SQLFunction, SQLIndex, + SQLProcedure, SQLRecord, SQLTable, SQLTrigger, @@ -378,3 +379,38 @@ def drop(self) -> bool: def alter(self) -> bool: self.drop() return self.create() + + +@dataclasses.dataclass(eq=False) +class MySQLProcedure(SQLProcedure): + parameters: str = "" + statement: str = "" + + @staticmethod + def _render_body(statement: str) -> str: + body = statement.strip() + if body.endswith(";"): + return body + + return f"{body};" + + def create(self) -> bool: + body = self._render_body(self.statement) + query = f""" + CREATE PROCEDURE {self.fully_qualified_name}({self.parameters}) + BEGIN + {body} + END + """ + self.database.context.set_database(self.database) + return self.database.context.execute(query) + + def drop(self) -> bool: + self.database.context.set_database(self.database) + return self.database.context.execute( + f"DROP PROCEDURE IF EXISTS {self.fully_qualified_name}" + ) + + def alter(self) -> bool: + self.drop() + return self.create() diff --git a/tests/engines/base_procedure_tests.py b/tests/engines/base_procedure_tests.py index 075566f..b838afa 100644 --- a/tests/engines/base_procedure_tests.py +++ b/tests/engines/base_procedure_tests.py @@ -11,6 +11,9 @@ def get_procedure_statement(self) -> str: def get_procedure_parameters(self) -> str: return "" + + def get_updated_procedure_statement(self) -> str: + return self.get_procedure_statement() def test_procedure_create_and_drop(self, session: Session, database: SQLDatabase): procedure = session.context.build_empty_procedure( @@ -35,3 +38,30 @@ def test_procedure_create_and_drop(self, session: Session, database: SQLDatabase database.procedures.refresh() found = any(p.name == "test_procedure" for p in database.procedures.get_value()) assert found is False + + def test_procedure_alter(self, session: Session, database: SQLDatabase): + procedure = session.context.build_empty_procedure( + database, + name="test_procedure_alter", + parameters=self.get_procedure_parameters(), + statement=self.get_procedure_statement() + ) + + assert procedure.create() is True + + database.procedures.refresh() + found = any( + p.name == "test_procedure_alter" for p in database.procedures.get_value() + ) + assert found is True + + procedure.statement = self.get_updated_procedure_statement() + assert procedure.alter() is True + + database.procedures.refresh() + found = any( + p.name == "test_procedure_alter" for p in database.procedures.get_value() + ) + assert found is True + + assert procedure.drop() is True diff --git a/tests/engines/mariadb/test_integration_suite.py b/tests/engines/mariadb/test_integration_suite.py index b025c09..2d65fba 100644 --- a/tests/engines/mariadb/test_integration_suite.py +++ b/tests/engines/mariadb/test_integration_suite.py @@ -8,6 +8,7 @@ from tests.engines.base_index_tests import BaseIndexTests from tests.engines.base_foreignkey_tests import BaseForeignKeyTests from tests.engines.base_check_tests import BaseCheckTests +from tests.engines.base_procedure_tests import BaseProcedureTests from tests.engines.base_trigger_tests import BaseTriggerTests from tests.engines.base_view_tests import BaseViewSaveTests, BaseViewIsNewTests, BaseViewDefinerTests @@ -56,6 +57,17 @@ class TestMariaDBCheck(BaseCheckTests): pass +@pytest.mark.integration +@pytest.mark.xdist_group("mariadb") +class TestMariaDBProcedure(BaseProcedureTests): + + def get_procedure_statement(self) -> str: + return "SELECT 1" + + def get_updated_procedure_statement(self) -> str: + return "SELECT 2" + + @pytest.mark.integration @pytest.mark.xdist_group("mariadb") class TestMariaDBTrigger(BaseTriggerTests): diff --git a/tests/engines/mysql/test_integration_suite.py b/tests/engines/mysql/test_integration_suite.py index 953e72d..d8ee6c0 100644 --- a/tests/engines/mysql/test_integration_suite.py +++ b/tests/engines/mysql/test_integration_suite.py @@ -8,6 +8,7 @@ from tests.engines.base_index_tests import BaseIndexTests from tests.engines.base_foreignkey_tests import BaseForeignKeyTests from tests.engines.base_check_tests import BaseCheckTests +from tests.engines.base_procedure_tests import BaseProcedureTests from tests.engines.base_trigger_tests import BaseTriggerTests from tests.engines.base_view_tests import BaseViewSaveTests, BaseViewIsNewTests, BaseViewDefinerTests @@ -56,6 +57,17 @@ class TestMySQLCheck(BaseCheckTests): pass +@pytest.mark.integration +@pytest.mark.xdist_group("mysql") +class TestMySQLProcedure(BaseProcedureTests): + + def get_procedure_statement(self) -> str: + return "SELECT 1" + + def get_updated_procedure_statement(self) -> str: + return "SELECT 2" + + @pytest.mark.integration @pytest.mark.xdist_group("mysql") class TestMySQLTrigger(BaseTriggerTests): From 8d8c9d8d6a0c0fb62400178481d1461a1c240deb Mon Sep 17 00:00:00 2001 From: gtripoli Date: Sat, 7 Mar 2026 17:01:26 +0100 Subject: [PATCH 02/12] Update code style guide and MariaDB integration fixture AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- CODE_STYLE.md | 65 ++++++++++++++++++++++++++++--- tests/engines/mariadb/conftest.py | 17 ++++---- 2 files changed, 68 insertions(+), 14 deletions(-) diff --git a/CODE_STYLE.md b/CODE_STYLE.md index 5189a1f..b6190a5 100644 --- a/CODE_STYLE.md +++ b/CODE_STYLE.md @@ -1,14 +1,65 @@ -# Code Style Guidelines (v1.4) +# Code Style Guidelines (v1.5) These rules define the expected coding style for this project. -They apply to all contributors, including humans, AI-assisted tools, and automated systems. -They are mandatory unless explicitly stated otherwise. + +They apply to all contributors: + +- human developers +- AI-assisted tools +- coding agents +- automated systems + +These rules are **mandatory** unless explicitly stated otherwise. + +If a requested change conflicts with these rules, the change must **stop** and clarification must be requested. + +--- + +## Core Rules (Quick Reference) + +The following rules are the most critical and must always be respected: + +1. All code, comments, documentation, commit messages, and user-facing text MUST be written in English. +2. Python typing rules MUST be respected (PEP 585 generics; `Optional[T]`, not `T | None`). +3. Import ordering and grouping rules MUST be followed exactly. +4. Functions and methods MUST NOT exceed 50 lines. +5. Code changes MUST avoid modifying unrelated code. +6. Naming MUST remain explicit and descriptive (no aggressive abbreviations). +7. Code MUST remain mypy-friendly whenever possible. + +When generating or modifying code, tools and agents MUST consult this file before producing changes. + +--- + +## Mandatory Rules + +The following rules are strict and MUST NOT be violated: + +- English must always be used for code and documentation. +- `typing.Optional[T]` MUST be used instead of `T | None`. +- `from __future__ import annotations` MUST NOT be used. +- Import ordering rules MUST be respected exactly. +- Functions MUST NOT exceed the maximum size limit. + +--- + +## Usage by AI Tools + +AI-assisted tools and coding agents MUST read this file before generating or modifying code. + +If a requested change conflicts with these rules, the tool MUST: + +1. stop the modification +2. explain the conflict +3. request clarification + +Agents MUST prioritize deterministic rules over stylistic interpretation. --- ## Design Principles -#### These principles explain why the rules exist. They protect consistency over time. +These principles explain why the rules exist. They protect consistency over time. - Determinism over preference A rule must always produce the same result. Subjective rules lead to debates and inconsistency. @@ -476,6 +527,8 @@ def get_dialect() -> str: return connection.engine.value.dialect ``` +If exact ordering cannot be determined automatically, prefer consistency with the surrounding file. + --- ## 7. Variable Definition Order @@ -586,8 +639,8 @@ class Example: ## 10. Walrus Operator ( := ) -- Always try to use the walrus operator when it improves clarity and avoids redundant calls. -- Do NOT use it if it reduces readability. +- The walrus operator MAY be used when it improves clarity and avoids redundant calls. +- It MUST NOT be used when it makes the control flow harder to read. #### Good examples diff --git a/tests/engines/mariadb/conftest.py b/tests/engines/mariadb/conftest.py index 990ddb3..ff173db 100644 --- a/tests/engines/mariadb/conftest.py +++ b/tests/engines/mariadb/conftest.py @@ -1,6 +1,5 @@ import pytest - from testcontainers.mysql import MySqlContainer from structures.session import Session @@ -64,13 +63,15 @@ def pytest_generate_tests(metafunc): @pytest.fixture(scope="module") def mariadb_container(mariadb_version, worker_id): - container = MySqlContainer(mariadb_version, name=f"petersql_test_{worker_id}_{mariadb_version.replace(":", "_")}", - mem_limit="768m", - memswap_limit="1g", - nano_cpus=1_000_000_000, - shm_size="256m", - ) - + container = MySqlContainer( + mariadb_version, + name=f"petersql_test_{worker_id}_{mariadb_version.replace(':', '_')}", + mem_limit="768m", + memswap_limit="1g", + nano_cpus=1_000_000_000, + shm_size="256m", + ) + with container: yield container From 22be8764783a0a18d4b09641718711fc83801ee5 Mon Sep 17 00:00:00 2001 From: gtripoli Date: Sat, 7 Mar 2026 20:43:06 +0100 Subject: [PATCH 03/12] Finalize P0 reliability and test reporting AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- .github/workflows/ci.yml | 8 +- PROJECT_STATUS.md | 15 +- README.md | 50 +++++- ROADMAP.md | 11 +- scripts/runtest.py | 147 ++++++++++++++++- structures/connection.py | 25 +++ structures/engines/context.py | 11 +- structures/engines/mariadb/context.py | 3 +- structures/engines/mysql/context.py | 3 +- tests/core/test_connection.py | 149 +++++++++++------- tests/core/test_ssh_tunnel_contract.py | 145 +++++++++++++++++ tests/engines/base_check_tests.py | 4 + tests/engines/base_function_tests.py | 31 ++++ tests/engines/mariadb/test_context.py | 57 +++++-- tests/engines/mysql/test_context.py | 57 +++++-- .../postgresql/test_integration_suite.py | 6 + windows/dialogs/connections/view.py | 25 +-- 17 files changed, 622 insertions(+), 125 deletions(-) create mode 100644 tests/core/test_ssh_tunnel_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d66089..6962176 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ env: jobs: test: - if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref != 'refs/heads/main' && github.ref != 'refs/heads/nightly') + if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref != 'refs/heads/main') runs-on: ubuntu-latest steps: @@ -112,7 +112,7 @@ jobs: run: xvfb-run -a ./scripts/runtest.sh --all update: - if: github.event_name == 'push' && github.ref == 'refs/heads/nightly' + if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]') runs-on: ubuntu-latest steps: @@ -221,7 +221,7 @@ jobs: run: echo "Build scripts are not ready yet." release: - if: github.event_name == 'push' && github.ref == 'refs/heads/main' + if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]') runs-on: ubuntu-latest steps: @@ -267,4 +267,4 @@ jobs: echo "Release $tag already exists" else gh release create "$tag" --generate-notes --title "$tag" - fi + fi \ No newline at end of file diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index b8035a9..cbceeb6 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -23,8 +23,8 @@ |------|---------------| | **MySQL Procedure** | Class + CRUD methods exist, context introspection exists, integration tests added, broader validation still ongoing. | | **MariaDB Procedure** | Class + CRUD methods exist, context introspection exists, integration tests added, broader validation still ongoing. | -| **PostgreSQL Function** | Class + CRUD methods exist, context introspection exists, still considered under validation. | -| **PostgreSQL Procedure** | Class + CRUD methods exist, context introspection exists, still considered under validation. | +| **PostgreSQL Function** | Class + CRUD methods exist, context introspection exists, integration tests now cover create/alter/drop across supported PG versions; broader validation still ongoing. | +| **PostgreSQL Procedure** | Class + CRUD methods exist, context introspection exists, integration tests now cover create/alter/drop across supported PG versions; broader validation still ongoing. | | **Check Constraints (MySQL/MariaDB/PostgreSQL)** | Engine classes and introspection exist, cross-version validation still needed. | | **Connection Reliability Features** | Persistent connection statistics, empty DB password support, and TLS auto-retry are implemented and need longer real-world validation. | @@ -125,12 +125,14 @@ - Persistent connection statistics in connection model and dialog. - Empty database password accepted in connection validation. - Automatic TLS retry path for MySQL/MariaDB when server requires TLS. +- Unit reliability coverage for MySQL/MariaDB TLS auto-retry and SSH tunnel lifecycle contracts. - CI workflow split into `test`, `update` (nightly), and `release` jobs. ### Main Remaining Risks -- Newly implemented PostgreSQL Function/Procedure paths need broader integration validation. +- PostgreSQL Function/Procedure now have integration coverage for create/alter/drop, but still need broader long-run/manual validation. - Check constraints across MySQL/MariaDB/PostgreSQL need more cross-version coverage. +- SSH tunnel integration validation with testcontainers remains blocked (existing SSH integration suites are still skipped). - UI parity lags engine parity for Trigger/Function/Procedure editors. --- @@ -139,10 +141,9 @@ ### Priority A — Validate Newly Implemented Features -1. PostgreSQL Function integration validation (all supported PG variants). -2. PostgreSQL Procedure integration validation (all supported PG variants). -3. Check constraints validation matrix for MySQL, MariaDB, PostgreSQL. -4. Connection statistics + TLS auto-retry robustness checks. +1. PostgreSQL Function/Procedure long-run validation (manual workflows + regression suites after integration coverage). +2. Check constraints validation matrix for MySQL, MariaDB, PostgreSQL. +3. Connection statistics + TLS auto-retry robustness checks. ### Priority B — Close Engine Gaps diff --git a/README.md b/README.md index 577a352..b651e06 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ ![status: unstable](https://img.shields.io/badge/status-unstable-orange) -![Coverage](https://img.shields.io/badge/coverage-54%25-brightgreen) +![Coverage](https://img.shields.io/badge/coverage-56%25-brightgreen) +![Tests](https://img.shields.io/badge/tests-2495-blue) ![SQLite](https://img.shields.io/badge/SQLite-tested-lightgrey) ![MySQL](https://img.shields.io/badge/MySQL-5.7%20%7C%208.0%20%7C%20latest-lightgrey) @@ -111,13 +112,28 @@ You can change the language in the application settings (Settings → General ## 🧪 Test Coverage -PeterSQL has **comprehensive integration tests** across all supported database engines covering Tables, Records, Columns, Indexes, Foreign Keys, Triggers, Views, and SSH tunnels. +PeterSQL has a structured test suite with both **unit tests** and **integration tests** across supported database engines. - 🏗️ **Granular base class architecture** - zero code duplication - 🐛 **Bug detection** - tests have found multiple API inconsistencies - ✅ **Full CRUD coverage** for core database objects -For detailed test coverage matrix, statistics, architecture, and running instructions, see **[tests/README.md](tests/README.md)**. +For detailed test coverage matrix, statistics, and architecture, see **[tests/README.md](tests/README.md)**. + + +### Suite status (passed / skipped) + +| Suite | Passed | Skipped | +|-------|--------|---------| +| autocomplete | ![passed](https://img.shields.io/badge/passed-1944-brightgreen) | ![skipped](https://img.shields.io/badge/skipped-0-lightgrey) | +| core | ![passed](https://img.shields.io/badge/passed-122-brightgreen) | ![skipped](https://img.shields.io/badge/skipped-0-lightgrey) | +| ui | ![passed](https://img.shields.io/badge/passed-57-brightgreen) | ![skipped](https://img.shields.io/badge/skipped-0-lightgrey) | +| mysql | ![passed](https://img.shields.io/badge/passed-59-brightgreen) | ![skipped](https://img.shields.io/badge/skipped-1-lightgrey) | +| mariadb | ![passed](https://img.shields.io/badge/passed-115-brightgreen) | ![skipped](https://img.shields.io/badge/skipped-3-lightgrey) | +| postgresql | ![passed](https://img.shields.io/badge/passed-132-brightgreen) | ![skipped](https://img.shields.io/badge/skipped-0-lightgrey) | +| sqlite | ![passed](https://img.shields.io/badge/passed-21-brightgreen) | ![skipped](https://img.shields.io/badge/skipped-5-lightgrey) | + + --- @@ -153,10 +169,10 @@ PeterSQL uses [uv](https://github.com/astral-sh/uv) for fast and reliable depend uv sync --extra dev ``` -To run tests: +Run tests with the project runner script: ```bash -uv run pytest +./scripts/runtest.py ``` ### Troubleshooting installation @@ -172,6 +188,30 @@ uv pip install -U --reinstall wxPython==4.2.5 --no-binary wxPython ###### Once the build finishes, rerun `uv sync` so the refreshed environment picks up the manually installed wxPython. +--- + +## 🧪 Running Tests + +- **Unit tests only** (default): + Uses `-m "not integration"`, so integration tests are excluded. + + ```bash + ./scripts/runtest.py + ``` + +- **Unit + integration tests**: + Runs the full suite, including integration tests. + + ```bash + ./scripts/runtest.py --all + ``` + +- **Unit + integration tests + README badge update** (engine badges + coverage badge): + + ```bash + ./scripts/runtest.py --update + ``` + ## 📸 Screenshot

diff --git a/ROADMAP.md b/ROADMAP.md index 11ae0cd..33291b6 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -32,11 +32,13 @@ This roadmap reflects the current project state and separates: - [x] **PostgreSQL Function engine implementation** (PARTIAL) - **Files:** `structures/engines/postgresql/database.py`, `structures/engines/postgresql/context.py` - - **Next:** validate behavior across supported PostgreSQL variants. + - **Status:** integration coverage for create/alter/drop is now in place across supported PostgreSQL variants. + - **Next:** long-run/manual workflow validation + broader regression checks. - [x] **PostgreSQL Procedure engine implementation** (PARTIAL) - **Files:** `structures/engines/postgresql/database.py`, `structures/engines/postgresql/context.py` - - **Next:** validate create/alter/drop and introspection consistency. + - **Status:** integration coverage for create/alter/drop is now in place across supported PostgreSQL variants. + - **Next:** long-run/manual workflow validation + introspection consistency checks. - [x] **Check constraint implementations for MySQL/MariaDB/PostgreSQL** (PARTIAL) - **Files:** @@ -51,7 +53,8 @@ This roadmap reflects the current project state and separates: - `structures/connection.py` - `windows/dialogs/connections/model.py` - `windows/dialogs/connections/view.py` - - **Next:** long-run behavioral validation. + - **Validation status:** unit tests now cover connection statistics updates, MySQL/MariaDB TLS retry behavior, and SSH tunnel context lifecycle contracts. + - **Next:** unblock and run SSH testcontainers integration validation (currently skipped) + long-run behavioral validation. --- @@ -125,8 +128,10 @@ Before moving a PARTIAL item to DONE: ### Recent Highlights - PostgreSQL Function and Procedure engine classes added. +- PostgreSQL Function/Procedure integration tests now include ALTER coverage (create/alter/drop). - Check constraint support added for MySQL, MariaDB, PostgreSQL. - Connection statistics and TLS auto-retry behavior added in connection manager. +- SSH tunnel unit contract tests added for context lifecycle and process stop behavior. - CI workflow split into test/nightly-update/release lanes. --- diff --git a/scripts/runtest.py b/scripts/runtest.py index 79ceca3..35f862a 100755 --- a/scripts/runtest.py +++ b/scripts/runtest.py @@ -7,10 +7,11 @@ """ import argparse -import subprocess import os import re +import subprocess import sys +import xml.etree.ElementTree as ET # Add project root to Python path for imports sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -20,6 +21,111 @@ README = "README.md" TESTS_DIR = "tests/engines" RESULTS_FILE = "/tmp/pytest_results.txt" +JUNIT_FILE = "/tmp/pytest_results.xml" + +SUITE_BADGES_START = "" +SUITE_BADGES_END = "" + +SUITE_ORDER = [ + "autocomplete", + "core", + "ui", + "mysql", + "mariadb", + "postgresql", + "sqlite", +] + + +def _build_suite_badges_table(suite_stats: dict[str, dict[str, int]]) -> str: + lines = [ + SUITE_BADGES_START, + "### Suite status (passed / skipped)", + "", + "| Suite | Passed | Skipped |", + "|-------|--------|---------|", + ] + + for suite_name in SUITE_ORDER: + passed = suite_stats[suite_name]["passed"] + skipped = suite_stats[suite_name]["skipped"] + passed_badge = f"![passed](https://img.shields.io/badge/passed-{passed}-brightgreen)" + skipped_badge = f"![skipped](https://img.shields.io/badge/skipped-{skipped}-lightgrey)" + lines.append(f"| {suite_name} | {passed_badge} | {skipped_badge} |") + + lines += ["", SUITE_BADGES_END] + return "\n".join(lines) + + +def _extract_suite_name(case_node: ET.Element) -> str: + file_path = case_node.attrib.get("file", "") + class_name = case_node.attrib.get("classname", "") + source = file_path or class_name.replace(".", "/") + + mapping = [ + ("tests/autocomplete/", "autocomplete"), + ("tests/core/", "core"), + ("tests/ui/", "ui"), + ("tests/engines/mysql/", "mysql"), + ("tests/engines/mariadb/", "mariadb"), + ("tests/engines/postgresql/", "postgresql"), + ("tests/engines/sqlite/", "sqlite"), + ] + + for prefix, suite_name in mapping: + if source.startswith(prefix): + return suite_name + + return "" + + +def _load_suite_stats(junit_file: str) -> dict[str, dict[str, int]]: + stats = { + suite_name: {"passed": 0, "skipped": 0} + for suite_name in SUITE_ORDER + } + + if not os.path.exists(junit_file): + return stats + + try: + tree = ET.parse(junit_file) + except ET.ParseError: + return stats + + root = tree.getroot() + for case in root.findall(".//testcase"): + suite_name = _extract_suite_name(case) + if not suite_name: + continue + + if case.find("skipped") is not None: + stats[suite_name]["skipped"] += 1 + continue + + if case.find("failure") is not None or case.find("error") is not None: + continue + + stats[suite_name]["passed"] += 1 + + return stats + + +def _update_suite_badges_block(content: str, suite_stats: dict[str, dict[str, int]]) -> str: + replacement = _build_suite_badges_table(suite_stats) + block_pattern = re.compile( + rf"{re.escape(SUITE_BADGES_START)}.*?{re.escape(SUITE_BADGES_END)}", + re.DOTALL, + ) + + if block_pattern.search(content): + return block_pattern.sub(replacement, content) + + anchor = "For detailed test coverage matrix, statistics, and architecture, see **[tests/README.md](tests/README.md)**." + if anchor in content: + return content.replace(anchor, f"{anchor}\n\n{replacement}") + + return f"{content}\n\n{replacement}\n" def get_engine_color(engine, results_content): @@ -95,6 +201,10 @@ def update_badges(): match = re.search(r'TOTAL\s+\d+\s+\d+\s+(\d+)%', results_content) coverage = match.group(1) if match else "0" + tests_total_match = re.search(r'\[(\d+)\s+items\]', results_content) + tests_total = tests_total_match.group(1) if tests_total_match else "unknown" + suite_stats = _load_suite_stats(JUNIT_FILE) + print(f"\nCoverage: {coverage}%") print("\nAnalyzing results for badge updates...") @@ -114,6 +224,26 @@ def update_badges(): content = re.sub(r'coverage-\d+%', f'coverage-{coverage}%', content) print(f" Coverage badge updated: {coverage}%") + # Update total tests badge + content = re.sub( + r'!\[Tests\]\(https://img\.shields\.io/badge/tests-[^\)]*\)', + f'![Tests](https://img.shields.io/badge/tests-{tests_total}-blue)', + content, + ) + + if "![Tests](https://img.shields.io/badge/tests-" not in content: + content = re.sub( + r'(!\[Coverage\]\(https://img\.shields\.io/badge/coverage-[^\)]*\))', + rf'\1\n![Tests](https://img.shields.io/badge/tests-{tests_total}-blue)', + content, + count=1, + ) + + print(f" Tests badge updated: {tests_total}") + + content = _update_suite_badges_block(content, suite_stats) + print(" Suite matrix badges updated") + # Update engine badges for engine, color in colors.items(): @@ -138,6 +268,11 @@ def update_badges(): except OSError: pass + try: + os.remove(JUNIT_FILE) + except OSError: + pass + def main(): parser = argparse.ArgumentParser(description='Unified test runner') @@ -160,7 +295,15 @@ def main(): try: with open(RESULTS_FILE, 'w') as f: process = subprocess.Popen( - ['uv', 'run', 'pytest', 'tests/', '--tb=no'], + [ + 'uv', + 'run', + 'pytest', + 'tests/', + '--tb=no', + '--junitxml', + JUNIT_FILE, + ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True diff --git a/structures/connection.py b/structures/connection.py index ab1c10e..2fcc64f 100755 --- a/structures/connection.py +++ b/structures/connection.py @@ -150,3 +150,28 @@ def is_new(self): def has_enabled_tunnel(self) -> bool: return bool(self.ssh_tunnel and self.ssh_tunnel.is_enabled) + + def record_connection_attempt( + self, + timestamp: str, + success: bool, + duration_ms: int, + failure_reason: Optional[str] = None, + ) -> None: + self.total_connection_attempts += 1 + self.last_connection_at = timestamp + self.most_recent_connection_duration_ms = duration_ms + + if success: + self.successful_connections += 1 + self.last_successful_connection_at = timestamp + self.last_failure_reason = None + else: + self.unsuccessful_connections += 1 + self.last_failure_reason = failure_reason + + previous_average = self.average_connection_time_ms or 0 + total_attempts = self.total_connection_attempts + self.average_connection_time_ms = int( + ((previous_average * (total_attempts - 1)) + duration_ms) / total_attempts + ) diff --git a/structures/engines/context.py b/structures/engines/context.py index a0f965d..1728107 100755 --- a/structures/engines/context.py +++ b/structures/engines/context.py @@ -55,7 +55,8 @@ def __init__(self, connection: Connection): self.databases = ObservableLazyList(self.get_databases) def __del__(self): - self.disconnect() + with contextlib.suppress(Exception): + self.disconnect() def before_connect(self, *args, **kwargs): # SSH tunnel support via connection configuration @@ -509,11 +510,15 @@ def disconnect(self) -> None: self.before_disconnect() if self._cursor is not None: - self._cursor.close() + close_cursor = getattr(self._cursor, "close", None) + if callable(close_cursor): + close_cursor() self._cursor = None if self._connection is not None: - self._connection.close() + close_connection = getattr(self._connection, "close", None) + if callable(close_connection): + close_connection() self._connection = None @contextlib.contextmanager diff --git a/structures/engines/mariadb/context.py b/structures/engines/mariadb/context.py index 5750d37..af4284b 100755 --- a/structures/engines/mariadb/context.py +++ b/structures/engines/mariadb/context.py @@ -198,7 +198,8 @@ def connect(self, **connect_kwargs) -> None: except Exception as e: logger.error(f"Failed to connect to MariaDB: {e}", exc_info=True) raise - else: + + if self._cursor is not None: self.after_connect() def disconnect(self) -> None: diff --git a/structures/engines/mysql/context.py b/structures/engines/mysql/context.py index 7af8478..ffd1012 100644 --- a/structures/engines/mysql/context.py +++ b/structures/engines/mysql/context.py @@ -183,7 +183,8 @@ def connect(self, **connect_kwargs) -> None: except Exception as e: logger.error(f"Failed to connect to MySQL: {e}") raise - else: + + if self._cursor is not None: self.after_connect() def set_database(self, database: SQLDatabase) -> None: diff --git a/tests/core/test_connection.py b/tests/core/test_connection.py index e2d6b6e..7030712 100644 --- a/tests/core/test_connection.py +++ b/tests/core/test_connection.py @@ -1,5 +1,3 @@ -import pytest - from structures.connection import Connection, ConnectionEngine from structures.configurations import ( CredentialsConfiguration, @@ -9,111 +7,102 @@ class TestConnection: - """Tests for Connection dataclass.""" - def test_connection_mysql(self): - """Test MySQL connection creation.""" config = CredentialsConfiguration( hostname="localhost", username="root", password="secret", port=3306, ) - conn = Connection( + connection = Connection( id=1, name="MySQL Test", engine=ConnectionEngine.MYSQL, configuration=config, ) - assert conn.id == 1 - assert conn.name == "MySQL Test" - assert conn.engine == ConnectionEngine.MYSQL - assert conn.configuration.hostname == "localhost" - assert conn.configuration.port == 3306 + assert connection.id == 1 + assert connection.name == "MySQL Test" + assert connection.engine == ConnectionEngine.MYSQL + assert connection.configuration.hostname == "localhost" + assert connection.configuration.port == 3306 def test_connection_sqlite(self): - """Test SQLite connection creation.""" config = SourceConfiguration(filename=":memory:") - conn = Connection( + connection = Connection( id=2, name="SQLite Test", engine=ConnectionEngine.SQLITE, configuration=config, ) - assert conn.engine == ConnectionEngine.SQLITE - assert conn.configuration.filename == ":memory:" + assert connection.engine == ConnectionEngine.SQLITE + assert connection.configuration.filename == ":memory:" def test_connection_is_new(self): - """Test is_new property.""" config = SourceConfiguration(filename=":memory:") - conn = Connection( + connection = Connection( id=-1, name="New", engine=ConnectionEngine.SQLITE, configuration=config, ) - assert conn.is_new is True + assert connection.is_new is True def test_connection_is_not_new(self): - """Test is_new property when id >= 0.""" config = SourceConfiguration(filename=":memory:") - conn = Connection( + connection = Connection( id=0, name="Existing", engine=ConnectionEngine.SQLITE, configuration=config, ) - assert conn.is_new is False + assert connection.is_new is False def test_connection_copy(self): - """Test connection copy.""" config = CredentialsConfiguration( hostname="localhost", username="root", password="", port=3306, ) - conn = Connection( + connection = Connection( id=1, name="Original", engine=ConnectionEngine.MYSQL, configuration=config, ) - copy = conn.copy() + copy = connection.copy() - assert copy.id == conn.id - assert copy.name == conn.name - assert copy is not conn + assert copy.id == connection.id + assert copy.name == connection.name + assert copy is not connection def test_connection_to_dict(self): - """Test connection serialization to dict.""" config = CredentialsConfiguration( hostname="localhost", username="root", password="secret", port=3306, ) - conn = Connection( + connection = Connection( id=1, name="Test", engine=ConnectionEngine.MYSQL, configuration=config, ) - d = conn.to_dict() + data = connection.to_dict() - assert d["id"] == 1 - assert d["name"] == "Test" - assert d["engine"] == "MySQL" - assert d["configuration"]["hostname"] == "localhost" + assert data["id"] == 1 + assert data["name"] == "Test" + assert data["engine"] == "MySQL" + assert data["configuration"]["hostname"] == "localhost" def test_connection_with_ssh_tunnel(self): - """Test connection with SSH tunnel.""" config = CredentialsConfiguration( hostname="localhost", username="root", @@ -129,7 +118,7 @@ def test_connection_with_ssh_tunnel(self): password="sshpass", local_port=3307, ) - conn = Connection( + connection = Connection( id=1, name="With SSH", engine=ConnectionEngine.MYSQL, @@ -137,9 +126,9 @@ def test_connection_with_ssh_tunnel(self): ssh_tunnel=ssh, ) - assert conn.ssh_tunnel is not None - assert conn.ssh_tunnel.enabled is True - assert conn.ssh_tunnel.hostname == "bastion.example.com" + assert connection.ssh_tunnel is not None + assert connection.ssh_tunnel.enabled is True + assert connection.ssh_tunnel.hostname == "bastion.example.com" def test_connection_is_valid_with_empty_password(self): config = CredentialsConfiguration( @@ -148,60 +137,110 @@ def test_connection_is_valid_with_empty_password(self): password="", port=3306, ) - conn = Connection( + connection = Connection( id=1, name="Local", engine=ConnectionEngine.MYSQL, configuration=config, ) - assert conn.is_valid is True + assert connection.is_valid is True + def test_record_connection_attempt_success_updates_statistics(self): + config = CredentialsConfiguration( + hostname="localhost", + username="root", + password="", + port=3306, + ) + connection = Connection( + id=1, + name="Stats Success", + engine=ConnectionEngine.MYSQL, + configuration=config, + ) -class TestConnectionEngine: - """Tests for ConnectionEngine enum.""" + connection.record_connection_attempt( + timestamp="2026-03-07 17:00:00", + success=True, + duration_ms=120, + ) + + assert connection.total_connection_attempts == 1 + assert connection.successful_connections == 1 + assert connection.unsuccessful_connections == 0 + assert connection.last_connection_at == "2026-03-07 17:00:00" + assert connection.last_successful_connection_at == "2026-03-07 17:00:00" + assert connection.last_failure_reason is None + assert connection.most_recent_connection_duration_ms == 120 + assert connection.average_connection_time_ms == 120 + def test_record_connection_attempt_failure_updates_statistics(self): + config = CredentialsConfiguration( + hostname="localhost", + username="root", + password="", + port=3306, + ) + connection = Connection( + id=1, + name="Stats Failure", + engine=ConnectionEngine.MYSQL, + configuration=config, + average_connection_time_ms=100, + total_connection_attempts=1, + successful_connections=1, + ) + + connection.record_connection_attempt( + timestamp="2026-03-07 17:01:00", + success=False, + duration_ms=300, + failure_reason="Authentication failed", + ) + + assert connection.total_connection_attempts == 2 + assert connection.successful_connections == 1 + assert connection.unsuccessful_connections == 1 + assert connection.last_connection_at == "2026-03-07 17:01:00" + assert connection.last_successful_connection_at is None + assert connection.last_failure_reason == "Authentication failed" + assert connection.most_recent_connection_duration_ms == 300 + assert connection.average_connection_time_ms == 200 + + +class TestConnectionEngine: def test_engine_mysql(self): - """Test MySQL engine.""" engine = ConnectionEngine.MYSQL assert engine.value.name == "MySQL" def test_engine_mariadb(self): - """Test MariaDB engine.""" engine = ConnectionEngine.MARIADB assert engine.value.name == "MariaDB" def test_engine_sqlite(self): - """Test SQLite engine.""" engine = ConnectionEngine.SQLITE assert engine.value.name == "SQLite" def test_engine_postgresql(self): - """Test PostgreSQL engine.""" engine = ConnectionEngine.POSTGRESQL assert engine.value.name == "PostgreSQL" def test_engine_from_name_mysql(self): - """Test from_name with MySQL.""" engine = ConnectionEngine.from_name("MySQL") assert engine == ConnectionEngine.MYSQL def test_engine_from_name_sqlite(self): - """Test from_name with SQLite.""" engine = ConnectionEngine.from_name("SQLite") assert engine == ConnectionEngine.SQLITE def test_engine_from_name_case_insensitive(self): - """Test from_name is case sensitive.""" engine = ConnectionEngine.from_name("MariaDB") assert engine == ConnectionEngine.MARIADB class TestCredentialsConfiguration: - """Tests for CredentialsConfiguration.""" - def test_credentials_config(self): - """Test credentials configuration.""" config = CredentialsConfiguration( hostname="db.example.com", username="admin", @@ -216,16 +255,12 @@ def test_credentials_config(self): class TestSourceConfiguration: - """Tests for SourceConfiguration.""" - def test_source_config(self): - """Test source configuration.""" config = SourceConfiguration(filename="/path/to/database.db") assert config.filename == "/path/to/database.db" def test_source_config_memory(self): - """Test source configuration with memory.""" config = SourceConfiguration(filename=":memory:") assert config.filename == ":memory:" diff --git a/tests/core/test_ssh_tunnel_contract.py b/tests/core/test_ssh_tunnel_contract.py new file mode 100644 index 0000000..5ec1146 --- /dev/null +++ b/tests/core/test_ssh_tunnel_contract.py @@ -0,0 +1,145 @@ +import signal + +from structures.connection import Connection, ConnectionEngine +from structures.ssh_tunnel import SSHTunnel +from structures.configurations import CredentialsConfiguration, SSHTunnelConfiguration +from structures.engines.mysql.context import MySQLContext +from structures.engines.mariadb.context import MariaDBContext + + +class _FakeTunnel: + def __init__(self, *args, **kwargs): + self.local_port = kwargs["local_bind_address"][1] or 4406 + self.call_log: list[str] = kwargs["extra_args"] + + def start(self): + self.call_log.append("start") + + def stop(self): + self.call_log.append("stop") + + +class _FakeProcess: + def __init__(self): + self.pid = 999 + + def poll(self): + return None + + def wait(self, timeout=None): + return 0 + + def terminate(self): + return None + + +class TestSSHTunnelContextContract: + def test_mariadb_context_manages_tunnel_lifecycle(self, monkeypatch): + call_log: list[str] = [] + configuration = CredentialsConfiguration("db.internal", "root", "secret", 3306) + ssh_tunnel = SSHTunnelConfiguration( + True, + "ssh", + "bastion.internal", + 22, + "sshuser", + "sshpass", + 0, + extra_args=call_log, + ) + connection = Connection( + 1, + "mariadb_tunnel", + ConnectionEngine.MARIADB, + configuration, + ssh_tunnel=ssh_tunnel, + ) + context = MariaDBContext(connection) + monkeypatch.setattr("structures.engines.context.SSHTunnel", _FakeTunnel) + + context.before_connect() + assert context.host == "127.0.0.1" + assert context.port == 4406 + assert call_log == ["start"] + + context.before_disconnect() + assert context.host == "db.internal" + assert context.port == 3306 + assert call_log == ["start", "stop"] + + def test_mysql_context_manages_tunnel_lifecycle(self, monkeypatch): + call_log: list[str] = [] + configuration = CredentialsConfiguration("db.internal", "root", "secret", 3306) + ssh_tunnel = SSHTunnelConfiguration( + True, + "ssh", + "bastion.internal", + 22, + "sshuser", + "sshpass", + 4407, + extra_args=call_log, + ) + connection = Connection( + 2, + "mysql_tunnel", + ConnectionEngine.MYSQL, + configuration, + ssh_tunnel=ssh_tunnel, + ) + context = MySQLContext(connection) + monkeypatch.setattr("structures.engines.context.SSHTunnel", _FakeTunnel) + + context.before_connect() + assert context.host == "127.0.0.1" + assert context.port == 4407 + assert call_log == ["start"] + + context.before_disconnect() + assert context.host == "db.internal" + assert context.port == 3306 + assert call_log == ["start", "stop"] + + +class TestSSHTunnelStopContract: + def test_mariadb_disconnect_stops_active_tunnel(self): + call_log: list[str] = [] + configuration = CredentialsConfiguration("db.internal", "root", "secret", 3306) + connection = Connection( + 3, + "mariadb_disconnect", + ConnectionEngine.MARIADB, + configuration, + ) + context = MariaDBContext(connection) + + class FakeActiveTunnel: + def stop(self): + call_log.append("stop") + + context._ssh_tunnel = FakeActiveTunnel() + context.disconnect() + + assert call_log == ["stop"] + assert context._ssh_tunnel is None + + def test_ssh_tunnel_stop_terminates_posix_process_group(self, monkeypatch): + kill_calls: list[tuple[int, int]] = [] + tunnel = SSHTunnel("bastion.internal") + tunnel._process = _FakeProcess() + + monkeypatch.setattr("os.killpg", lambda pid, sig: kill_calls.append((pid, sig))) + monkeypatch.setattr("os.name", "posix", raising=False) + + tunnel.stop() + + assert kill_calls == [(999, signal.SIGTERM)] + assert tunnel._process is None + + def test_ssh_tunnel_stop_returns_when_process_is_missing(self): + tunnel = SSHTunnel("bastion.internal") + tunnel._process = None + + tunnel.stop() + + assert tunnel._process is None \ No newline at end of file diff --git a/tests/engines/base_check_tests.py b/tests/engines/base_check_tests.py index ae30832..691a677 100644 --- a/tests/engines/base_check_tests.py +++ b/tests/engines/base_check_tests.py @@ -96,4 +96,8 @@ def test_check_alter(self, session, database, create_users_table, datatype_class check.expression = "age >= 18" assert check.alter() is True + table.checks.refresh() + checks = table.checks.get_value() + assert any(c.name == "age_check" for c in checks) + table.drop() diff --git a/tests/engines/base_function_tests.py b/tests/engines/base_function_tests.py index e4ebb55..9eefeb5 100644 --- a/tests/engines/base_function_tests.py +++ b/tests/engines/base_function_tests.py @@ -14,6 +14,9 @@ def get_function_parameters(self) -> str: def get_function_returns(self) -> str: return "integer" + + def get_updated_function_statement(self) -> str: + return self.get_function_statement() def test_function_create_and_drop(self, session: Session, database: SQLDatabase): function = session.context.build_empty_function( @@ -39,3 +42,31 @@ def test_function_create_and_drop(self, session: Session, database: SQLDatabase) database.functions.refresh() found = any(f.name == "test_function" for f in database.functions.get_value()) assert found is False + + def test_function_alter(self, session: Session, database: SQLDatabase): + function = session.context.build_empty_function( + database, + name="test_function_alter", + parameters=self.get_function_parameters(), + returns=self.get_function_returns(), + statement=self.get_function_statement(), + ) + + assert function.create() is True + + database.functions.refresh() + found = any( + f.name == "test_function_alter" for f in database.functions.get_value() + ) + assert found is True + + function.statement = self.get_updated_function_statement() + assert function.alter() is True + + database.functions.refresh() + found = any( + f.name == "test_function_alter" for f in database.functions.get_value() + ) + assert found is True + + assert function.drop() is True diff --git a/tests/engines/mariadb/test_context.py b/tests/engines/mariadb/test_context.py index eb7514f..fcdd160 100644 --- a/tests/engines/mariadb/test_context.py +++ b/tests/engines/mariadb/test_context.py @@ -1,48 +1,83 @@ +import pymysql import pytest +from structures.connection import Connection, ConnectionEngine +from structures.configurations import CredentialsConfiguration +from structures.engines.mariadb.context import MariaDBContext + + +class TestMariaDBContextReliability: + def test_connect_retries_with_tls_on_auth_error(self, monkeypatch): + config = CredentialsConfiguration( + hostname="localhost", + username="root", + password="secret", + port=3306, + ) + connection = Connection( + id=1, + name="mariadb_tls_retry", + engine=ConnectionEngine.MARIADB, + configuration=config, + ) + context = MariaDBContext(connection) + monkeypatch.setattr(context, "before_connect", lambda *args, **kwargs: None) + monkeypatch.setattr(context, "after_connect", lambda *args, **kwargs: None) + + calls = [] + + class FakeConnection: + def cursor(self): + return object() + + def fake_connect(**kwargs): + calls.append(kwargs) + if len(calls) == 1: + raise pymysql.err.OperationalError(1045, "Access denied") + return FakeConnection() + + monkeypatch.setattr(pymysql, "connect", fake_connect) + + context.connect(connect_timeout=1) + + assert len(calls) == 2 + assert "ssl" not in calls[0] + assert "ssl" in calls[1] + assert connection.configuration.use_tls_enabled is True + @pytest.mark.integration @pytest.mark.xdist_group("mariadb") class TestMariaDBContext: - """Tests for MariaDB context methods.""" - def test_context_connection(self, mariadb_session): - """Test context is connected.""" ctx = mariadb_session.context assert ctx.is_connected is True def test_context_execute_query(self, mariadb_session): - """Test executing a query.""" ctx = mariadb_session.context ctx.execute("SELECT 1 as test") result = ctx.fetchone() assert result["test"] == 1 def test_context_fetchall(self, mariadb_session): - """Test fetchall method.""" ctx = mariadb_session.context ctx.execute("SELECT 1 as val UNION SELECT 2 UNION SELECT 3") results = ctx.fetchall() assert len(results) == 3 def test_context_get_server_version(self, mariadb_session): - """Test getting server version.""" version = mariadb_session.context.get_server_version() assert version is not None assert len(version) > 0 def test_context_quote_identifier(self, mariadb_session): - """Test building SQL safe names uses IDENTIFIER_QUOTE_CHAR.""" ctx = mariadb_session.context quote = ctx.IDENTIFIER_QUOTE_CHAR - # Simple names don't need quoting assert ctx.quote_identifier("normal") == "normal" - # Names with spaces are quoted using IDENTIFIER_QUOTE_CHAR assert ctx.quote_identifier("with space") == f'{quote}with space{quote}' def test_context_transaction(self, mariadb_session, mariadb_database): - """Test transaction context manager.""" ctx = mariadb_session.context db_name = mariadb_database.name @@ -57,10 +92,8 @@ def test_context_transaction(self, mariadb_session, mariadb_database): ctx.execute(f"DROP TABLE {db_name}.test_tx") def test_context_databases_list(self, mariadb_session): - """Test getting databases list.""" ctx = mariadb_session.context databases = ctx.databases.get_value() assert len(databases) > 0 - # Should have at least information_schema db_names = [db.name for db in databases] assert "information_schema" in db_names diff --git a/tests/engines/mysql/test_context.py b/tests/engines/mysql/test_context.py index 13817a0..523e7e2 100644 --- a/tests/engines/mysql/test_context.py +++ b/tests/engines/mysql/test_context.py @@ -1,48 +1,83 @@ +import pymysql import pytest +from structures.connection import Connection, ConnectionEngine +from structures.configurations import CredentialsConfiguration +from structures.engines.mysql.context import MySQLContext + + +class TestMySQLContextReliability: + def test_connect_retries_with_tls_on_auth_error(self, monkeypatch): + config = CredentialsConfiguration( + hostname="localhost", + username="root", + password="secret", + port=3306, + ) + connection = Connection( + id=1, + name="mysql_tls_retry", + engine=ConnectionEngine.MYSQL, + configuration=config, + ) + context = MySQLContext(connection) + monkeypatch.setattr(context, "before_connect", lambda *args, **kwargs: None) + monkeypatch.setattr(context, "after_connect", lambda *args, **kwargs: None) + + calls = [] + + class FakeConnection: + def cursor(self): + return object() + + def fake_connect(**kwargs): + calls.append(kwargs) + if len(calls) == 1: + raise pymysql.err.OperationalError(1045, "Access denied") + return FakeConnection() + + monkeypatch.setattr(pymysql, "connect", fake_connect) + + context.connect(connect_timeout=1) + + assert len(calls) == 2 + assert "ssl" not in calls[0] + assert "ssl" in calls[1] + assert connection.configuration.use_tls_enabled is True + @pytest.mark.integration @pytest.mark.xdist_group("mysql") class TestMySQLContext: - """Tests for MySQL context methods.""" - def test_context_connection(self, mysql_session): - """Test context is connected.""" ctx = mysql_session.context assert ctx.is_connected is True def test_context_execute_query(self, mysql_session): - """Test executing a query.""" ctx = mysql_session.context ctx.execute("SELECT 1 as test") result = ctx.fetchone() assert result["test"] == 1 def test_context_fetchall(self, mysql_session): - """Test fetchall method.""" ctx = mysql_session.context ctx.execute("SELECT 1 as val UNION SELECT 2 UNION SELECT 3") results = ctx.fetchall() assert len(results) == 3 def test_context_get_server_version(self, mysql_session): - """Test getting server version.""" version = mysql_session.context.get_server_version() assert version is not None assert len(version) > 0 def test_context_quote_identifier(self, mysql_session): - """Test building SQL safe names uses IDENTIFIER_QUOTE_CHAR.""" ctx = mysql_session.context quote = ctx.IDENTIFIER_QUOTE_CHAR - # Simple names don't need quoting assert ctx.quote_identifier("normal") == "normal" - # Names with spaces are quoted using IDENTIFIER_QUOTE_CHAR assert ctx.quote_identifier("with space") == f'{quote}with space{quote}' def test_context_transaction(self, mysql_session, mysql_database): - """Test transaction context manager.""" ctx = mysql_session.context db_name = mysql_database.name @@ -57,10 +92,8 @@ def test_context_transaction(self, mysql_session, mysql_database): ctx.execute(f"DROP TABLE {db_name}.test_tx") def test_context_databases_list(self, mysql_session): - """Test getting databases list.""" ctx = mysql_session.context databases = ctx.databases.get_value() assert len(databases) > 0 - # Should have at least information_schema db_names = [db.name for db in databases] assert "information_schema" in db_names diff --git a/tests/engines/postgresql/test_integration_suite.py b/tests/engines/postgresql/test_integration_suite.py index e218e9b..4a9443a 100644 --- a/tests/engines/postgresql/test_integration_suite.py +++ b/tests/engines/postgresql/test_integration_suite.py @@ -112,6 +112,9 @@ def get_function_parameters(self) -> str: def get_function_returns(self) -> str: return "integer" + def get_updated_function_statement(self) -> str: + return "RETURN x + 2;" + @pytest.mark.integration @pytest.mark.xdist_group("postgresql") @@ -123,6 +126,9 @@ def get_procedure_statement(self) -> str: def get_procedure_parameters(self) -> str: return "" + def get_updated_procedure_statement(self) -> str: + return "RAISE NOTICE 'Updated from procedure';" + @pytest.mark.integration @pytest.mark.xdist_group("postgresql") diff --git a/windows/dialogs/connections/view.py b/windows/dialogs/connections/view.py index f15ea6d..c2a9792 100644 --- a/windows/dialogs/connections/view.py +++ b/windows/dialogs/connections/view.py @@ -112,24 +112,13 @@ def _record_connection_attempt( duration_ms: int, failure_reason: Optional[str] = None, ) -> None: - connection.total_connection_attempts += 1 - connection.last_connection_at = self._current_timestamp() - connection.most_recent_connection_duration_ms = duration_ms - - if success: - connection.successful_connections += 1 - connection.last_successful_connection_at = connection.last_connection_at - connection.last_failure_reason = None - else: - connection.unsuccessful_connections += 1 - connection.last_failure_reason = failure_reason or _("Unknown error") - - if connection.total_connection_attempts > 0: - previous_total = connection.average_connection_time_ms or 0 - attempt_count = connection.total_connection_attempts - connection.average_connection_time_ms = int( - ((previous_total * (attempt_count - 1)) + duration_ms) / attempt_count - ) + reason = failure_reason if success else failure_reason or _("Unknown error") + connection.record_connection_attempt( + timestamp=self._current_timestamp(), + success=success, + duration_ms=duration_ms, + failure_reason=reason, + ) if not connection.is_new: self._repository.save_connection(connection) From 005988627a5568fff916fa0455dbe5c95d1ea098 Mon Sep 17 00:00:00 2001 From: gtripoli Date: Mon, 9 Mar 2026 17:15:34 +0100 Subject: [PATCH 04/12] feat(database-options): add apply/cancel, encryption checkbox and related UI updates Also includes updates to helpers/dataview.py, helpers/observables.py and PeterSQL.fbp. AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- PeterSQL.fbp | 4469 ++++++++++++++++++--- helpers/dataview.py | 16 - helpers/observables.py | 7 +- structures/engines/database.py | 4 + structures/engines/mariadb/database.py | 22 + structures/engines/mysql/builder.py | 4 +- structures/engines/mysql/database.py | 22 + structures/engines/postgresql/database.py | 24 +- structures/engines/sqlite/database.py | 3 +- windows/main/controller.py | 62 +- windows/main/tabs/database_options.py | 366 ++ windows/views.py | 458 ++- 12 files changed, 4855 insertions(+), 602 deletions(-) create mode 100644 windows/main/tabs/database_options.py diff --git a/PeterSQL.fbp b/PeterSQL.fbp index 594082d..e118717 100755 --- a/PeterSQL.fbp +++ b/PeterSQL.fbp @@ -31,7 +31,7 @@ 1 0 0 - + 0 wxAUI_MGR_DEFAULT @@ -60,16 +60,16 @@ on_close - + bSizer34 wxVERTICAL none - + 5 wxEXPAND 1 - + 1 1 1 @@ -127,8 +127,8 @@ - - + + 1 1 1 @@ -180,7 +180,7 @@ wxTAB_TRAVERSAL - + bSizer35 wxVERTICAL @@ -385,8 +385,8 @@ - - + + 1 1 1 @@ -438,16 +438,16 @@ wxTAB_TRAVERSAL - + bSizer36 wxVERTICAL none - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -3325,11 +3325,11 @@ - + Statistics 0 - + 1 1 1 @@ -3381,16 +3381,16 @@ wxTAB_TRAVERSAL - + bSizer361 wxVERTICAL none - + 5 wxEXPAND 0 - + bSizer37 wxHORIZONTAL @@ -3521,11 +3521,11 @@ - + 5 wxEXPAND 0 - + bSizer371 wxHORIZONTAL @@ -3656,11 +3656,11 @@ - + 5 wxEXPAND 0 - + bSizer3711 wxHORIZONTAL @@ -3791,20 +3791,20 @@ - + 5 wxEXPAND 0 - + bSizer371111 wxHORIZONTAL none - + 5 wxALL 0 - + 1 1 1 @@ -3862,11 +3862,11 @@ -1 - + 5 wxALL 1 - + 1 1 1 @@ -3926,11 +3926,11 @@ - + 5 wxEXPAND 0 - + bSizer37111 wxHORIZONTAL @@ -4061,20 +4061,20 @@ - + 5 wxEXPAND 0 - + bSizer371112 wxHORIZONTAL none - + 5 wxALL 0 - + 1 1 1 @@ -4132,11 +4132,11 @@ -1 - + 5 wxALL 1 - + 1 1 1 @@ -4196,20 +4196,20 @@ - + 5 wxEXPAND 0 - + bSizer3711121 wxHORIZONTAL none - + 5 wxALL 0 - + 1 1 1 @@ -4267,11 +4267,11 @@ -1 - + 5 wxALL 1 - + 1 1 1 @@ -4331,20 +4331,20 @@ - + 5 wxEXPAND 0 - + bSizer37111211 wxHORIZONTAL none - + 5 wxALL 0 - + 1 1 1 @@ -4402,11 +4402,11 @@ -1 - + 5 wxALL 1 - + 1 1 1 @@ -4466,20 +4466,20 @@ - + 5 wxEXPAND 0 - + bSizer371112111 wxHORIZONTAL none - + 5 wxALL 0 - + 1 1 1 @@ -4537,11 +4537,11 @@ -1 - + 5 wxALL 1 - + 1 1 1 @@ -4670,16 +4670,16 @@ - + 0 wxEXPAND 0 - + bSizer28 wxHORIZONTAL none - + 5 wxEXPAND 1 @@ -4761,11 +4761,11 @@ on_create - + MenuCreate m_menu12 protected - + 0 1 @@ -4778,7 +4778,7 @@ - + 0 1 @@ -4957,11 +4957,11 @@ none - + 5 wxEXPAND 0 - + bSizer29 wxHORIZONTAL @@ -5271,7 +5271,7 @@ - + 0 wxAUI_MGR_DEFAULT @@ -5990,7 +5990,7 @@ - + 0 @@ -6020,7 +6020,7 @@ wxTAB_TRAVERSAL 1 do_close - + 1 @@ -6046,8 +6046,22 @@ File m_menu2 protected + + + 0 + 1 + + wxID_ANY + wxITEM_NORMAL + Settings + m_menuItem22 + none + + + on_settings + - + Help m_menu4 protected @@ -6188,16 +6202,16 @@ - + bSizer19 wxVERTICAL none - + 0 wxEXPAND | wxALL 1 - + 1 1 1 @@ -6249,16 +6263,16 @@ wxTAB_TRAVERSAL - + bSizer21 wxVERTICAL none - + 5 wxEXPAND 1 - + 1 1 1 @@ -6316,8 +6330,8 @@ - - + + 1 1 1 @@ -6369,16 +6383,16 @@ wxTAB_TRAVERSAL - + bSizer72 wxVERTICAL none - + 5 wxEXPAND 1 - + 1 1 1 @@ -6606,8 +6620,8 @@ - - + + 1 1 1 @@ -6659,16 +6673,16 @@ wxTAB_TRAVERSAL - + bSizer25 wxVERTICAL none - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -6932,11 +6946,11 @@ - + Load From File; icons/16x16/database.png Database 0 - + 1 1 1 @@ -6988,16 +7002,16 @@ wxTAB_TRAVERSAL - + bSizer27 wxVERTICAL none - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -7051,11 +7065,11 @@ - + - Tables + Options 0 - + 1 1 1 @@ -7107,25 +7121,75 @@ wxTAB_TRAVERSAL - + bSizer80 wxVERTICAL none - + 5 wxEXPAND - 0 - + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 - bSizer531 - wxHORIZONTAL - none - - 5 - wxALIGN_CENTER_VERTICAL|wxALL - 0 - + 1 + m_splitter7 + 1 + + + protected + 1 + + Resizable + 0.0 + -1 + -1 + 1 + + wxSPLIT_HORIZONTAL + wxSP_3D + ; ; forward_declare + 0 + + + + + + 1 1 1 @@ -7154,16 +7218,14 @@ 0 0 wxID_ANY - Table: - 0 0 0 - -1,-1 + 1 - m_staticText391 + m_panel54 1 @@ -7173,106 +7235,3200 @@ Resizable 1 - ; ; forward_declare 0 - - -1 - - - - 5 - wxEXPAND - 0 - - 0 - protected - 100 - - - - 2 - wxALL|wxEXPAND - 0 - - 1 - 1 - 1 - 1 - 0 - - 0 - 0 - 0 - - - Load From File; icons/16x16/add.png - - 1 - 0 - 1 - - 1 - - 0 - 0 - - Dock - 0 - Left - 0 - 1 - - 1 - - - 0 - 0 - wxID_ANY - Insert - - 0 - - 0 - - - 0 - - 1 - btn_insert_table - 1 - - - protected - 1 - - - - Resizable - 1 - - wxBORDER_NONE - ; ; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - + wxTAB_TRAVERSAL + + + bSizer158 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + + bSizer159 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Name + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText90 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + database_name + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer1481111 + wxHORIZONTAL + none + + + + 5 + wxEXPAND + 0 + + + bSizer142 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_character_set_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer139 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Character set + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText70 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_character_set + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_collation_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer1392 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Collation + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText702 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_collation + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer13911 + wxHORIZONTAL + none + + 0 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_encryption_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer1391 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Encryption + + 0 + + + 0 + + 1 + database_encryption + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + 5 + wxEXPAND | wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_read_only_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer148 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Read Only + + 0 + + + 0 + + 1 + database_read_only + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer92 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_tablespace_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer13912 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Tablespace + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText7012 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_tablespace + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_connection_limit_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer139111 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Connection limit + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText70111 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + 0 + 0 + + 0 + + 0 + + 0 + + 1 + database_connection_limit + 1 + + + protected + 1 + + Resizable + 1 + + wxSP_ARROW_KEYS + ; ; forward_declare + 0 + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer1481 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_password_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer139121 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Password + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText70121 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + m_textCtrl36 + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PASSWORD + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_profile_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer1391111 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Profile + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText701111 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_profile + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer96 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_default_tablespace_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer1391212 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Default tablespace + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText701212 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_default_tablespace + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_temporary_tablespace_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer13912121 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Temporary tablespace + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText7012121 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_temporary_tablespace + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer14811 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_quota_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer1391211 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Quota + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText701211 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + database_quota + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_unlimited_quota_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer13911111 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Unlimited quota + + 0 + + + 0 + + 1 + database_unlimited_quota + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer148111 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_account_status_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer13912111 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Account status + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText7012111 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_account_status + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + 5 + wxALIGN_CENTER + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_password_expire_panel + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer139111111 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Password expire + + 0 + + + 0 + + 1 + database_password_expire + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_panel55 + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + - - on_insert_table + wxTAB_TRAVERSAL + + + bSizer154 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + + bSizer531 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Table: + 0 + + 0 + + + 0 + -1,-1 + 1 + m_staticText391 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxEXPAND + 0 + + 0 + protected + 100 + + + + 2 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + 0 + + + Load From File; icons/16x16/add.png + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 0 + 1 + + 1 + + + 0 + 0 + wxID_ANY + Insert + + 0 + + 0 + + + 0 + + 1 + btn_insert_table + 1 + + + protected + 1 + + + + Resizable + 1 + + wxBORDER_NONE + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + on_insert_table + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + 0 + + + Load From File; icons/16x16/table_multiple.png + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 0 + 0 + + 1 + + + 0 + 0 + wxID_ANY + Clone + + 0 + + 0 + + + 0 + + 1 + btn_clone_table + 1 + + + protected + 1 + + + + Resizable + 1 + + wxBORDER_NONE + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + on_clone_table + + + + 2 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + 0 + + + Load From File; icons/16x16/delete.png + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 0 + 0 + + 1 + + + 0 + 0 + wxID_ANY + Delete + + 0 + + 0 + + + 0 + + 1 + btn_delete_table1 + 1 + + + protected + 1 + + + + Resizable + 1 + + wxBORDER_NONE + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + on_delete_table + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + + + 5 + wxEXPAND + 1 + + + bSizer152 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + 0 + 1 + + + 0 + wxID_ANY + + + list_ctrl_database_tables + protected + + + + ; ; forward_declare + + + + + + wxALIGN_LEFT + + wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE + Name + wxDATAVIEW_CELL_INERT + 0 + m_dataViewColumn12 + protected + Text + -1 + + + wxALIGN_RIGHT + + wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE + Rows + wxDATAVIEW_CELL_INERT + 1 + m_dataViewColumn13 + protected + Text + -1 + + + wxALIGN_RIGHT + + wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE + Size + wxDATAVIEW_CELL_INERT + 2 + m_dataViewColumn14 + protected + Text + -1 + + + wxALIGN_LEFT + + wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE + Created at + wxDATAVIEW_CELL_INERT + 3 + m_dataViewColumn15 + protected + Date + -1 + + + wxALIGN_LEFT + + wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE + Updated at + wxDATAVIEW_CELL_INERT + 4 + m_dataViewColumn16 + protected + Date + -1 + + + wxALIGN_LEFT + + wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE + Engine + wxDATAVIEW_CELL_INERT + 5 + m_dataViewColumn17 + protected + Text + -1 + + + wxALIGN_LEFT + + wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE + Collation + wxDATAVIEW_CELL_INERT + 6 + m_dataViewColumn19 + protected + Text + -1 + + + wxALIGN_LEFT + + wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE + Comments + wxDATAVIEW_CELL_INERT + 7 + m_dataViewColumn18 + protected + Text + -1 + + + + + + - + + + + 5 + wxEXPAND + 0 + + + bSizer138 + wxHORIZONTAL + none + 5 - wxALL|wxEXPAND + wxALL 0 - + 1 1 1 @@ -7284,7 +10440,7 @@ 0 - Load From File; icons/16x16/table_multiple.png + 1 0 @@ -7299,7 +10455,7 @@ 0 Left 0 - 0 + 1 1 @@ -7307,7 +10463,7 @@ 0 0 wxID_ANY - Clone + Cancel 0 @@ -7317,7 +10473,7 @@ 0 1 - btn_clone_table + btn_cancel_database 1 @@ -7329,7 +10485,7 @@ Resizable 1 - wxBORDER_NONE + ; ; forward_declare 0 @@ -7340,14 +10496,14 @@ - on_clone_table + on_cancel_database - - 2 - wxALL|wxEXPAND + + 5 + wxALL 0 - + 1 1 1 @@ -7359,7 +10515,7 @@ 0 - Load From File; icons/16x16/delete.png + 1 0 @@ -7374,7 +10530,7 @@ 0 Left 0 - 0 + 1 1 @@ -7382,7 +10538,7 @@ 0 0 wxID_ANY - Delete + MyButton 0 @@ -7392,7 +10548,7 @@ 0 1 - btn_delete_table + btn_apply_database 1 @@ -7404,7 +10560,7 @@ Resizable 1 - wxBORDER_NONE + ; ; forward_declare 0 @@ -7415,212 +10571,264 @@ - on_delete_table - - - - 5 - wxEXPAND - 1 - - 0 - protected - 0 + on_apply_database - + + + + + + Diagram + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 1 + wxID_ANY + + 0 + + + 0 + + 1 + m_panel31 + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + bSizer82 + wxVERTICAL + none + 5 - wxALL|wxEXPAND - 1 - + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + MyLabel + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText7011 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + MyLabel + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText7011111 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + 1 + 0 + 1 1 + 0 + Dock + 0 + Left 0 1 + 1 + 0 0 wxID_ANY + MyLabel + 0 + + 0 - - list_ctrl_database_tables + + 0 + 150,-1 + 1 + m_staticText70111111 + 1 + + protected + 1 + Resizable + 1 ; ; forward_declare + 0 - - wxALIGN_LEFT - - wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE - Name - wxDATAVIEW_CELL_INERT - 0 - m_dataViewColumn12 - protected - Text - -1 - - - wxALIGN_RIGHT - - wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE - Rows - wxDATAVIEW_CELL_INERT - 1 - m_dataViewColumn13 - protected - Text - -1 - - - wxALIGN_RIGHT - - wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE - Size - wxDATAVIEW_CELL_INERT - 2 - m_dataViewColumn14 - protected - Text - -1 - - - wxALIGN_LEFT - - wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE - Created at - wxDATAVIEW_CELL_INERT - 3 - m_dataViewColumn15 - protected - Date - -1 - - - wxALIGN_LEFT - - wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE - Updated at - wxDATAVIEW_CELL_INERT - 4 - m_dataViewColumn16 - protected - Date - -1 - - - wxALIGN_LEFT - - wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE - Engine - wxDATAVIEW_CELL_INERT - 5 - m_dataViewColumn17 - protected - Text - -1 - - - wxALIGN_LEFT - - wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE - Collation - wxDATAVIEW_CELL_INERT - 6 - m_dataViewColumn19 - protected - Text - -1 - - - wxALIGN_LEFT - - wxDATAVIEW_COL_RESIZABLE|wxDATAVIEW_COL_SORTABLE - Comments - wxDATAVIEW_CELL_INERT - 7 - m_dataViewColumn18 - protected - Text - -1 - + -1 - - - Diagram - 0 - - 1 - 1 - 1 - 1 - 0 - - 0 - 0 - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 0 - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - - 0 - - 1 - m_panel31 - 1 - - - protected - 1 - - Resizable - 1 - - ; ; forward_declare - 0 - - - - wxTAB_TRAVERSAL - - - bSizer82 - wxVERTICAL - none - - - @@ -7634,7 +10842,7 @@ Load From File; icons/16x16/table.png Table - 0 + 1 1 1 @@ -12750,7 +15958,7 @@ Load From File; icons/16x16/text_columns.png Data - 1 + 0 1 1 @@ -14505,24 +17713,6 @@ - - MyMenu - m_menu3 - protected - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - MyMenuItem - m_menuItem3 - none - - - - @@ -14793,11 +17983,11 @@ bSizer93 wxVERTICAL none - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -14867,11 +18057,11 @@ bSizer129 wxVERTICAL none - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -14932,11 +18122,11 @@ - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -14995,7 +18185,7 @@ - + MyMenu m_menu13 protected @@ -15016,11 +18206,11 @@ - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -15081,11 +18271,11 @@ - + 5 wxALIGN_CENTER|wxALL 0 - + 1 1 1 @@ -15171,11 +18361,11 @@ - + 5 wxALL 0 - + 1 1 1 @@ -15368,7 +18558,7 @@ - + MyMenu m_menu15 protected @@ -15440,13 +18630,78 @@ + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + m_textCtrl361 + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PASSWORD + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -15503,11 +18758,11 @@ - + 5 wxALIGN_CENTER|wxALL 1 - + 1 1 1 @@ -15568,13 +18823,78 @@ + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + database_encryption_old + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -15633,19 +18953,13 @@ - - - bSizer92 - wxVERTICAL - none - - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -15769,7 +19083,7 @@ 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -15975,11 +19289,11 @@ bSizer51 wxVERTICAL none - + 0 wxEXPAND | wxALL 0 - + 1 1 1 @@ -16031,7 +19345,7 @@ wxTAB_TRAVERSAL - + bSizer48 wxVERTICAL @@ -16097,13 +19411,31 @@ + + MyMenu + m_menu3 + protected + + + 0 + 1 + + wxID_ANY + wxITEM_NORMAL + MyMenuItem + m_menuItem3 + none + + + + - + 0 wxEXPAND | wxALL 0 - + 1 1 1 @@ -16155,16 +19487,16 @@ wxTAB_TRAVERSAL - + bSizer52 wxVERTICAL none - + 0 wxEXPAND 0 - + bSizer1212 wxHORIZONTAL @@ -16366,70 +19698,6 @@ - - 5 - wxEXPAND | wxALL - 1 - - 1 - 1 - 1 - 1 - 0 - - 0 - 0 - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 0 - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - - 0 - - 1 - m_panel35 - 1 - - - protected - 1 - - Resizable - 1 - - ; ; forward_declare - 0 - - - - wxTAB_TRAVERSAL - - - bSizer96 - wxVERTICAL - none - - - 5 wxALIGN_CENTER|wxALL @@ -17220,11 +20488,11 @@ - + 5 wxALL 0 - + 1 1 1 @@ -17279,11 +20547,11 @@ - + 5 wxALL 0 - + 1 1 1 @@ -17343,11 +20611,11 @@ - + 5 wxEXPAND 1 - + bSizer871 wxHORIZONTAL @@ -17481,11 +20749,11 @@ - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -17544,16 +20812,16 @@ - + bSizer115 wxVERTICAL none - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -17607,11 +20875,11 @@ wxTAB_TRAVERSAL - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -17665,11 +20933,11 @@ wxTAB_TRAVERSAL - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -17726,11 +20994,11 @@ - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -17791,11 +21059,11 @@ - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -17856,11 +21124,11 @@ - + 5 wxALL|wxEXPAND 1 - + 1 1 1 @@ -17938,7 +21206,7 @@ 5 wxEXPAND 1 - + bSizer8712 wxHORIZONTAL @@ -18202,11 +21470,11 @@ - + 5 wxEXPAND 0 - + bSizer12211 wxHORIZONTAL @@ -18215,11 +21483,11 @@ - + 5 wxALL 0 - + 1 1 1 @@ -18289,11 +21557,11 @@ bSizer86 wxHORIZONTAL none - + 5 wxEXPAND | wxALL 1 - + 1 1 1 @@ -18349,11 +21617,11 @@ - + 5 wxALL 0 - + 1 1 1 @@ -18415,6 +21683,133 @@ + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + m_textCtrl351 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Encryption + 0 + + 0 + + + 0 + 150,-1 + 1 + m_staticText701 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + MyMenu diff --git a/helpers/dataview.py b/helpers/dataview.py index 67a78a9..8e9d6e0 100644 --- a/helpers/dataview.py +++ b/helpers/dataview.py @@ -34,34 +34,24 @@ def __init__(self, column_count: Optional[int] = None): self._column_count = column_count def load(self, data: list[Any]): - logger.debug(f"{self.__class__.__name__}.load: {data[:50]}") - if data: self._data = data.copy() def filter(self, data: list[Any]): - logger.debug(f"{self.__class__.__name__}.filter: {data[:50]}") - if data: self._data = data.copy() def append(self, data: Any) -> int: - logger.debug(f"{self.__class__.__name__}.append: {data}") - self._data.append(data) return len(self._data) - 1 def insert(self, data: Any, index: int) -> int: - logger.debug(f"{self.__class__.__name__}._insert: {index} {data} ") - self._data.insert(index, data) return index def replace(self, data: Any, index: int) -> int: - logger.debug(f"{self.__class__.__name__}.replace: index={index} {data}") - index = self._data.index(data) self._data.remove(data) @@ -70,15 +60,11 @@ def replace(self, data: Any, index: int) -> int: return index def move(self, data: Any, current: int, future: int) -> (int, int): - logger.debug(f"{self.__class__.__name__}.move: {data} current={current} future={future}") - self._data[current], self._data[future] = self._data[future], self._data[current] return current, future def remove(self, data: Any) -> int: - logger.debug(f"{self.__class__.__name__}.remove: {data}") - index = self._data.index(data) self._data.remove(data) @@ -86,8 +72,6 @@ def remove(self, data: Any) -> int: return index def pop(self, data: Any) -> int: - logger.debug(f"{self.__class__.__name__}.pop: {data}") - index = self._data.index(data) self._data.pop(index) diff --git a/helpers/observables.py b/helpers/observables.py index 4fa5d52..5babdf1 100755 --- a/helpers/observables.py +++ b/helpers/observables.py @@ -2,12 +2,11 @@ import enum import inspect import weakref + from threading import Timer from typing import Callable, TypeVar, Generic, Any, SupportsIndex, Union, Optional, cast, Self, Hashable -import wx - from helpers.logger import logger T = TypeVar("T") @@ -365,7 +364,7 @@ def refresh(self) -> Self: class ObservableObject(Observable): - def _get_in_ref(self, ref: Union[Dict, List, Any], key: Union[str, SupportsIndex]): + def _get_in_ref(self, ref: Union[dict, list, Any], key: Union[str, SupportsIndex]): if isinstance(ref, dict): return ref.get(key) elif isinstance(ref, list): @@ -373,7 +372,7 @@ def _get_in_ref(self, ref: Union[Dict, List, Any], key: Union[str, SupportsIndex else: return getattr(ref, str(key), None) - def _set_in_ref(self, ref: Union[Dict, List, Any], key: Union[str, SupportsIndex], value: Any): + def _set_in_ref(self, ref: Union[dict, list, Any], key: Union[str, SupportsIndex], value: Any): if isinstance(ref, dict): ref[key] = value elif isinstance(ref, list): diff --git a/structures/engines/database.py b/structures/engines/database.py index 58dc527..7808629 100755 --- a/structures/engines/database.py +++ b/structures/engines/database.py @@ -76,6 +76,10 @@ def refresh(self): if getattr(self, observable_lazy_list_name, None) != (observable_lazy_list := getattr(original_database, observable_lazy_list_name, None)): observable_lazy_list.refresh() + @abc.abstractmethod + def apply(self) -> bool: + raise NotImplementedError + @dataclasses.dataclass(eq=False) class SQLTable(abc.ABC): diff --git a/structures/engines/mariadb/database.py b/structures/engines/mariadb/database.py index 0a63388..2e2cdad 100644 --- a/structures/engines/mariadb/database.py +++ b/structures/engines/mariadb/database.py @@ -14,6 +14,28 @@ @dataclasses.dataclass class MariaDBDatabase(SQLDatabase): default_collation: str = None + character_set: Optional[str] = None + encryption: Optional[str] = None + + def apply(self) -> bool: + clauses: list[str] = [] + + if self.character_set: + clauses.append(f"CHARACTER SET {self.context.quote_identifier(self.character_set)}") + + if self.default_collation: + clauses.append(f"COLLATE {self.context.quote_identifier(self.default_collation)}") + + if self.encryption: + clauses.append(f"ENCRYPTION = '{str(self.encryption).upper()}'") + + if not clauses: + return False + + self.context.execute( + f"ALTER DATABASE {self.context.quote_identifier(self.name)} {' '.join(clauses)}" + ) + return True @dataclasses.dataclass(eq=False) diff --git a/structures/engines/mysql/builder.py b/structures/engines/mysql/builder.py index fbe014c..dd361c2 100644 --- a/structures/engines/mysql/builder.py +++ b/structures/engines/mysql/builder.py @@ -7,7 +7,7 @@ class MySQLColumnBuilder(AbstractColumnBuilder): TEMPLATE = ["%(name)s", "%(datatype)s", "%(unsigned)s", "%(zerofill)s", "%(collate)s", "%(nullable)s", "%(default)s", "%(auto_increment)s", - "%(unique)s", "%(primary_key)s", "%(comment)s", "%(check)s", + "%(unique)s", "%(comment)s", "%(check)s", # TODO: COLUMN_FORMAT {FIXED|DYNAMIC|DEFAULT} - STORAGE {DISK|MEMORY}] # "%(format)s","%(storage)s", "%(generated)s"] @@ -51,7 +51,7 @@ def generated(self): class MySQLIndexBuilder(AbstractIndexBuilder): TEMPLATE = ["%(type)s", "%(name)s", "(%(columns)s)"] - def __init__(self, index: 'MariaDBIndex', exclude: Optional[List[str]] = None): + def __init__(self, index: 'MariaDBIndex', exclude: Optional[list[str]] = None): super().__init__(index, exclude) @property diff --git a/structures/engines/mysql/database.py b/structures/engines/mysql/database.py index 250ee33..ad6a836 100644 --- a/structures/engines/mysql/database.py +++ b/structures/engines/mysql/database.py @@ -25,6 +25,28 @@ @dataclasses.dataclass class MySQLDatabase(SQLDatabase): default_collation: str = None + character_set: Optional[str] = None + encryption: Optional[str] = None + + def apply(self) -> bool: + clauses: list[str] = [] + + if self.character_set: + clauses.append(f"CHARACTER SET {self.context.quote_identifier(self.character_set)}") + + if self.default_collation: + clauses.append(f"COLLATE {self.context.quote_identifier(self.default_collation)}") + + if self.encryption: + clauses.append(f"ENCRYPTION = '{str(self.encryption).upper()}'") + + if not clauses: + return False + + self.context.execute( + f"ALTER DATABASE {self.context.quote_identifier(self.name)} {' '.join(clauses)}" + ) + return True diff --git a/structures/engines/postgresql/database.py b/structures/engines/postgresql/database.py index c320356..ba75200 100644 --- a/structures/engines/postgresql/database.py +++ b/structures/engines/postgresql/database.py @@ -13,7 +13,29 @@ @dataclasses.dataclass class PostgreSQLDatabase(SQLDatabase): - pass + tablespace: Optional[str] = None + connection_limit: Optional[int] = None + + def apply(self) -> bool: + statements: list[str] = [] + + if self.tablespace: + statements.append( + f"ALTER DATABASE {self.context.quote_identifier(self.name)} SET TABLESPACE {self.context.quote_identifier(self.tablespace)}" + ) + + if self.connection_limit is not None: + statements.append( + f"ALTER DATABASE {self.context.quote_identifier(self.name)} CONNECTION LIMIT {int(self.connection_limit)}" + ) + + if not statements: + return False + + for statement in statements: + self.context.execute(statement) + + return True @dataclasses.dataclass(eq=False) diff --git a/structures/engines/sqlite/database.py b/structures/engines/sqlite/database.py index f31cb0e..9e65390 100644 --- a/structures/engines/sqlite/database.py +++ b/structures/engines/sqlite/database.py @@ -15,7 +15,8 @@ @dataclasses.dataclass class SQLiteDatabase(SQLDatabase): - pass + def apply(self) -> bool: + return False @dataclasses.dataclass(eq=False) diff --git a/windows/main/controller.py b/windows/main/controller.py index 78a58da..faecadc 100755 --- a/windows/main/controller.py +++ b/windows/main/controller.py @@ -36,6 +36,7 @@ from windows.main.tabs.column import TableColumnsController from windows.main.tabs.records import TableRecordsController from windows.main.tabs.database import ListDatabaseTable +from windows.main.tabs.database_options import DatabaseOptionsController from windows.main.tabs.explorer import TreeExplorerController from windows.main.tabs.foreign_key import TableForeignKeyController from windows.main.tabs.view import ViewEditorController @@ -59,6 +60,7 @@ def __init__(self): ) self.list_database_tables = ListDatabaseTable(self.list_ctrl_database_tables) + self.controller_database_options = DatabaseOptionsController(self) self.controller_tree_connections = TreeExplorerController(self.tree_ctrl_explorer) self.controller_tree_connections.on_cancel_table = self.on_cancel_table @@ -280,10 +282,10 @@ def toggle_panel(self, current: Optional[Union[SQLDatabase, SQLTable, SQLView, S def on_page_chaged(self, event): if int(event.Selection) == 5: - if table := CURRENT_TABLE(): + if table := CURRENT_TABLE.get_value(): table.load_records() - self.controller_list_table_records.load_model() + # self.controller_list_table_records.load_model() def _on_current_session(self, session: Session): from structures.session import Session @@ -330,6 +332,60 @@ def _on_current_database(self, database: SQLDatabase): if (session := CURRENT_SESSION.get_value()) and session.engine in [ConnectionEngine.SQLITE]: self.table_collation.Enable(False) + def on_apply_database(self, event: wx.Event): + database = CURRENT_DATABASE.get_value() + session = CURRENT_SESSION.get_value() + + if database is None or session is None: + return + + try: + database.apply() + session.context.databases.refresh() + + database = next( + (d for d in session.context.databases.get_value() if d.id == database.id), + None, + ) + + if database is not None: + CURRENT_DATABASE.set_value(None).set_value(database) + session.context.set_database(database) + + except Exception as ex: + logger.error(str(ex), exc_info=True) + wx.MessageDialog(None, str(ex), "Error", wx.OK | wx.ICON_ERROR).ShowModal() + + def on_cancel_database(self, event: wx.Event): + database = CURRENT_DATABASE.get_value() + session = CURRENT_SESSION.get_value() + + if database is None or session is None: + return + + if wx.MessageDialog( + None, + message=_(f"Do you want discard the change to {database.name}?"), + style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, + ).ShowModal() != wx.ID_YES: + return + + try: + session.context.databases.refresh() + + database = next( + (d for d in session.context.databases.get_value() if d.id == database.id), + None, + ) + + if database is not None: + CURRENT_DATABASE.set_value(None).set_value(database) + session.context.set_database(database) + + except Exception as ex: + logger.error(str(ex), exc_info=True) + wx.MessageDialog(None, str(ex), "Error", wx.OK | wx.ICON_ERROR).ShowModal() + # VIEW def _on_current_view(self, current: SQLView): self.toggle_panel(current) @@ -595,7 +651,7 @@ def on_apply_filters(self, event): filters = (self.sql_query_filters.GetSelectedText() or self.sql_query_filters.GetText()).strip() table.load_records(filters) - self.controller_list_table_records.load_model() + # self.controller_list_table_records.load_model() # def on_clear_record(self, event): # self.controller_list_table_records.on_row_clear() diff --git a/windows/main/tabs/database_options.py b/windows/main/tabs/database_options.py new file mode 100644 index 0000000..034161e --- /dev/null +++ b/windows/main/tabs/database_options.py @@ -0,0 +1,366 @@ +from typing import Optional + +import wx + +from helpers.bindings import AbstractModel +from helpers.observables import Observable, debounce + +from structures.connection import ConnectionEngine + +from windows.main import CURRENT_DATABASE, CURRENT_SESSION + + +class EditDatabaseOptionsModel(AbstractModel): + def __init__(self): + self.database_name = Observable() + self.database_character_set = Observable() + self.database_collation = Observable() + self.database_encryption = Observable(False) + self.database_read_only = Observable(False) + self.database_tablespace = Observable() + self.database_connection_limit = Observable(0) + self.database_password = Observable() + self.database_profile = Observable() + self.database_default_tablespace = Observable() + self.database_temporary_tablespace = Observable() + self.database_quota = Observable() + self.database_unlimited_quota = Observable(False) + self.database_account_status = Observable() + self.database_password_expire = Observable(False) + + debounce( + self.database_name, + self.database_character_set, + self.database_collation, + self.database_encryption, + self.database_read_only, + self.database_tablespace, + self.database_connection_limit, + self.database_password, + self.database_profile, + self.database_default_tablespace, + self.database_temporary_tablespace, + self.database_quota, + self.database_unlimited_quota, + self.database_account_status, + self.database_password_expire, + callback=self._update_database, + ) + + CURRENT_DATABASE.subscribe(self._load_database) + + @staticmethod + def _first_attr(source, names: list[str], default=None): + if source is None: + return default + + for name in names: + if hasattr(source, name): + value = getattr(source, name) + if value is not None: + return value + + return default + + @staticmethod + def _encryption_to_bool(value) -> bool: + if isinstance(value, bool): + return value + + if value is None: + return False + + return str(value).strip().upper() in ["Y", "YES", "TRUE", "1", "ON"] + + def _load_database(self, database) -> None: + self.database_name.set_initial(self._first_attr(database, ["name"], "")) + self.database_collation.set_initial( + self._first_attr(database, ["default_collation", "collation", "collation_name"], "") + ) + + context = database.context if database else None + charset = None + if context and self.database_collation.get_value() and getattr(context, "COLLATIONS", None): + charset = context.COLLATIONS.get(self.database_collation.get_value()) + + self.database_character_set.set_initial( + charset or self._first_attr(database, ["character_set", "charset"], "") + ) + + self.database_encryption.set_initial( + self._encryption_to_bool(self._first_attr(database, ["encryption"], None)) + ) + self.database_read_only.set_initial(bool(self._first_attr(database, ["read_only", "is_read_only"], False))) + self.database_tablespace.set_initial(self._first_attr(database, ["tablespace", "default_tablespace"], "")) + self.database_connection_limit.set_initial(int(self._first_attr(database, ["connection_limit"], 0) or 0)) + self.database_password.set_initial(self._first_attr(database, ["password"], "")) + self.database_profile.set_initial(self._first_attr(database, ["profile"], "")) + self.database_default_tablespace.set_initial(self._first_attr(database, ["default_tablespace"], "")) + self.database_temporary_tablespace.set_initial(self._first_attr(database, ["temporary_tablespace"], "")) + self.database_quota.set_initial(self._first_attr(database, ["quota"], "")) + self.database_unlimited_quota.set_initial(bool(self._first_attr(database, ["unlimited_quota"], False))) + self.database_account_status.set_initial(self._first_attr(database, ["account_status"], "")) + self.database_password_expire.set_initial(bool(self._first_attr(database, ["password_expire"], False))) + + def _update_database(self, *args) -> None: + if not any(arg is not None for arg in args): + return + + database = CURRENT_DATABASE.get_value() + if database is None: + return + + session = CURRENT_SESSION.get_value() + engine = session.engine if session else None + + encryption_value = self.database_encryption.get_value() + if engine in [ConnectionEngine.MYSQL, ConnectionEngine.MARIADB]: + encryption_value = "Y" if bool(encryption_value) else "N" + + if hasattr(database, "name"): + database.name = self.database_name.get_value() + + mapping = { + "character_set": self.database_character_set.get_value(), + "charset": self.database_character_set.get_value(), + "default_collation": self.database_collation.get_value(), + "collation": self.database_collation.get_value(), + "collation_name": self.database_collation.get_value(), + "encryption": encryption_value, + "read_only": self.database_read_only.get_value(), + "is_read_only": self.database_read_only.get_value(), + "tablespace": self.database_tablespace.get_value(), + "default_tablespace": self.database_default_tablespace.get_value(), + "temporary_tablespace": self.database_temporary_tablespace.get_value(), + "connection_limit": self.database_connection_limit.get_value(), + "password": self.database_password.get_value(), + "profile": self.database_profile.get_value(), + "quota": self.database_quota.get_value(), + "unlimited_quota": self.database_unlimited_quota.get_value(), + "account_status": self.database_account_status.get_value(), + "password_expire": self.database_password_expire.get_value(), + } + + for attr, value in mapping.items(): + if hasattr(database, attr): + setattr(database, attr, value) + + +class DatabaseOptionsController: + def __init__(self, parent): + self.parent = parent + self.model = EditDatabaseOptionsModel() + self._panel_by_name = self._build_panel_by_name() + self._panels_all = list(self._panel_by_name.values()) + self._controls_all = self._build_controls_all() + + self._bind_controls() + + CURRENT_SESSION.subscribe(self._on_current_session) + CURRENT_DATABASE.subscribe(self._on_current_database) + + self.apply_for_current_state() + + @staticmethod + def _first_attr(source, names: list[str], default=None): + if source is None: + return default + + for name in names: + if hasattr(source, name): + value = getattr(source, name) + if value is not None: + return value + + return default + + def _apply_choice(self, choice: wx.Choice, items: list[str], selected: Optional[str]) -> None: + normalized = [str(item) for item in items if item is not None and str(item)] + + if selected is not None and str(selected) and str(selected) not in normalized: + normalized.append(str(selected)) + + choice.SetItems(normalized) + + if not normalized: + return + + if selected and choice.SetStringSelection(str(selected)): + return + + choice.SetSelection(0) + + def _apply_engine(self, engine: Optional[ConnectionEngine]) -> None: + panel_names = self._get_panel_names_for_engine(engine) + visible_panels = [self._panel_by_name[name] for name in panel_names] + + self._batch_show_hide(show=visible_panels, hide=self._panels_all) + self._apply_readonly_rules(engine) + self._layout_database_options() + + def _apply_readonly_rules(self, engine: Optional[ConnectionEngine]) -> None: + is_sqlite = engine == ConnectionEngine.SQLITE + + self.parent.database_name.Enable(True) + self.parent.database_name.SetEditable(not is_sqlite) + self._set_controls_enabled(enabled=not is_sqlite) + + def _batch_show_hide(self, show: list[wx.Window], hide: list[wx.Window]) -> None: + show_set = set(show) + for panel in hide: + panel.Show(panel in show_set) + + def _bind_controls(self) -> None: + self.model.bind_controls( + database_name=self.parent.database_name, + database_character_set=self.parent.database_character_set, + database_collation=self.parent.database_collation, + database_encryption=self.parent.database_encryption, + database_read_only=self.parent.database_read_only, + database_tablespace=self.parent.database_tablespace, + database_connection_limit=self.parent.database_connection_limit, + database_password=self.parent.m_textCtrl36, + database_profile=self.parent.database_profile, + database_default_tablespace=self.parent.database_default_tablespace, + database_temporary_tablespace=self.parent.database_temporary_tablespace, + database_quota=self.parent.database_quota, + database_unlimited_quota=self.parent.database_unlimited_quota, + database_account_status=self.parent.database_account_status, + database_password_expire=self.parent.database_password_expire, + ) + + def _build_controls_all(self) -> list[wx.Window]: + return [ + self.parent.database_character_set, + self.parent.database_collation, + self.parent.database_encryption, + self.parent.database_read_only, + self.parent.database_tablespace, + self.parent.database_connection_limit, + self.parent.m_textCtrl36, + self.parent.database_profile, + self.parent.database_default_tablespace, + self.parent.database_temporary_tablespace, + self.parent.database_quota, + self.parent.database_unlimited_quota, + self.parent.database_account_status, + self.parent.database_password_expire, + ] + + def _build_panel_by_name(self) -> dict[str, wx.Window]: + return { + "database_character_set_panel": self.parent.database_character_set_panel, + "database_collation_panel": self.parent.database_collation_panel, + "database_encryption_panel": self.parent.database_encryption_panel, + "database_read_only_panel": self.parent.database_read_only_panel, + "database_tablespace_panel": self.parent.database_tablespace_panel, + "database_connection_limit_panel": self.parent.database_connection_limit_panel, + "database_password_panel": self.parent.database_password_panel, + "database_profile_panel": self.parent.database_profile_panel, + "database_default_tablespace_panel": self.parent.database_default_tablespace_panel, + "database_temporary_tablespace_panel": self.parent.database_temporary_tablespace_panel, + "database_quota_panel": self.parent.database_quota_panel, + "database_unlimited_quota_panel": self.parent.database_unlimited_quota_panel, + "database_account_status_panel": self.parent.database_account_status_panel, + "database_password_expire_panel": self.parent.database_password_expire_panel, + } + + def _get_panel_names_for_engine(self, engine: Optional[ConnectionEngine]) -> list[str]: + if engine in [ConnectionEngine.MYSQL, ConnectionEngine.MARIADB]: + return [ + "database_character_set_panel", + "database_collation_panel", + "database_encryption_panel", + ] + + if engine == ConnectionEngine.POSTGRESQL: + return [ + "database_collation_panel", + "database_tablespace_panel", + "database_connection_limit_panel", + ] + + if engine == ConnectionEngine.ORACLE: + return [ + "database_password_panel", + "database_profile_panel", + "database_default_tablespace_panel", + "database_temporary_tablespace_panel", + "database_quota_panel", + "database_unlimited_quota_panel", + "database_account_status_panel", + "database_password_expire_panel", + ] + + return [] + + def _layout_database_options(self) -> None: + self.parent.m_panel54.Layout() + if parent := self.parent.m_panel54.GetParent(): + parent.Layout() + + def _on_current_database(self, database) -> None: + self.apply_for_current_state() + + def _on_current_session(self, session) -> None: + self.apply_for_current_state() + + def _populate_choices(self, database) -> None: + context = database.context if database else None + + collations = [] + if context and getattr(context, "COLLATIONS", None): + collations = sorted(context.COLLATIONS.keys()) + + charsets = [] + if context and getattr(context, "COLLATIONS", None): + charsets = sorted(set(context.COLLATIONS.values())) + + self._apply_choice( + self.parent.database_character_set, + charsets, + self.model.database_character_set.get_value(), + ) + self._apply_choice( + self.parent.database_collation, + collations, + self.model.database_collation.get_value(), + ) + + self._apply_choice( + self.parent.database_tablespace, + [self._first_attr(database, ["tablespace", "default_tablespace"])], + self.model.database_tablespace.get_value(), + ) + self._apply_choice( + self.parent.database_profile, + [self._first_attr(database, ["profile"])], + self.model.database_profile.get_value(), + ) + self._apply_choice( + self.parent.database_default_tablespace, + [self._first_attr(database, ["default_tablespace"])], + self.model.database_default_tablespace.get_value(), + ) + self._apply_choice( + self.parent.database_temporary_tablespace, + [self._first_attr(database, ["temporary_tablespace"])], + self.model.database_temporary_tablespace.get_value(), + ) + self._apply_choice( + self.parent.database_account_status, + [self._first_attr(database, ["account_status"])], + self.model.database_account_status.get_value(), + ) + + def _set_controls_enabled(self, enabled: bool) -> None: + for control in self._controls_all: + control.Enable(enabled) + + def apply_for_current_state(self) -> None: + session = CURRENT_SESSION.get_value() + database = CURRENT_DATABASE.get_value() + engine = session.engine if session else None + + self._populate_choices(database) + self._apply_engine(engine) diff --git a/windows/views.py b/windows/views.py index 3d49225..78797fc 100755 --- a/windows/views.py +++ b/windows/views.py @@ -834,6 +834,9 @@ def __init__( self, parent ): self.m_menubar2 = wx.MenuBar( 0 ) self.m_menu2 = wx.Menu() + self.m_menuItem22 = wx.MenuItem( self.m_menu2, wx.ID_ANY, _(u"Settings"), wx.EmptyString, wx.ITEM_NORMAL ) + self.m_menu2.Append( self.m_menuItem22 ) + self.m_menubar2.Append( self.m_menu2, _(u"File") ) self.m_menu4 = wx.Menu() @@ -944,9 +947,327 @@ def __init__( self, parent ): self.m_panel30 = wx.Panel( self.m_notebook6, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) bSizer80 = wx.BoxSizer( wx.VERTICAL ) + self.m_splitter7 = wx.SplitterWindow( self.m_panel30, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.SP_3D ) + self.m_panel54 = wx.Panel( self.m_splitter7, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer158 = wx.BoxSizer( wx.VERTICAL ) + + bSizer159 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText90 = wx.StaticText( self.m_panel54, wx.ID_ANY, _(u"Name"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText90.Wrap( -1 ) + + self.m_staticText90.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer159.Add( self.m_staticText90, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + self.database_name = wx.TextCtrl( self.m_panel54, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer159.Add( self.database_name, 1, wx.ALL, 5 ) + + + bSizer158.Add( bSizer159, 0, wx.EXPAND, 5 ) + + bSizer1481111 = wx.BoxSizer( wx.HORIZONTAL ) + + + bSizer158.Add( bSizer1481111, 1, wx.EXPAND, 5 ) + + bSizer142 = wx.BoxSizer( wx.HORIZONTAL ) + + self.database_character_set_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer139 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText70 = wx.StaticText( self.database_character_set_panel, wx.ID_ANY, _(u"Character set"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText70.Wrap( -1 ) + + self.m_staticText70.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer139.Add( self.m_staticText70, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + database_character_setChoices = [] + self.database_character_set = wx.Choice( self.database_character_set_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, database_character_setChoices, 0 ) + self.database_character_set.SetSelection( 0 ) + bSizer139.Add( self.database_character_set, 1, wx.ALL, 5 ) + + + self.database_character_set_panel.SetSizer( bSizer139 ) + self.database_character_set_panel.Layout() + bSizer139.Fit( self.database_character_set_panel ) + bSizer142.Add( self.database_character_set_panel, 1, wx.ALIGN_CENTER, 5 ) + + self.database_collation_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer1392 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText702 = wx.StaticText( self.database_collation_panel, wx.ID_ANY, _(u"Collation"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText702.Wrap( -1 ) + + self.m_staticText702.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer1392.Add( self.m_staticText702, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + database_collationChoices = [] + self.database_collation = wx.Choice( self.database_collation_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, database_collationChoices, 0 ) + self.database_collation.SetSelection( 0 ) + bSizer1392.Add( self.database_collation, 1, wx.ALL, 5 ) + + + self.database_collation_panel.SetSizer( bSizer1392 ) + self.database_collation_panel.Layout() + bSizer1392.Fit( self.database_collation_panel ) + bSizer142.Add( self.database_collation_panel, 1, wx.ALIGN_CENTER, 5 ) + + + bSizer158.Add( bSizer142, 0, wx.EXPAND, 5 ) + + bSizer13911 = wx.BoxSizer( wx.HORIZONTAL ) + + self.database_encryption_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer1391 = wx.BoxSizer( wx.HORIZONTAL ) + + self.database_encryption = wx.CheckBox( self.database_encryption_panel, wx.ID_ANY, _(u"Encryption"), wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer1391.Add( self.database_encryption, 0, wx.ALL, 5 ) + + + self.database_encryption_panel.SetSizer( bSizer1391 ) + self.database_encryption_panel.Layout() + bSizer1391.Fit( self.database_encryption_panel ) + bSizer13911.Add( self.database_encryption_panel, 1, wx.ALIGN_CENTER, 0 ) + + self.database_read_only_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer148 = wx.BoxSizer( wx.HORIZONTAL ) + + self.database_read_only = wx.CheckBox( self.database_read_only_panel, wx.ID_ANY, _(u"Read Only"), wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer148.Add( self.database_read_only, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + + self.database_read_only_panel.SetSizer( bSizer148 ) + self.database_read_only_panel.Layout() + bSizer148.Fit( self.database_read_only_panel ) + bSizer13911.Add( self.database_read_only_panel, 1, wx.EXPAND | wx.ALL, 5 ) + + + bSizer158.Add( bSizer13911, 0, wx.EXPAND, 5 ) + + bSizer92 = wx.BoxSizer( wx.HORIZONTAL ) + + self.database_tablespace_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer13912 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText7012 = wx.StaticText( self.database_tablespace_panel, wx.ID_ANY, _(u"Tablespace"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText7012.Wrap( -1 ) + + self.m_staticText7012.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer13912.Add( self.m_staticText7012, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + database_tablespaceChoices = [] + self.database_tablespace = wx.Choice( self.database_tablespace_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, database_tablespaceChoices, 0 ) + self.database_tablespace.SetSelection( 0 ) + bSizer13912.Add( self.database_tablespace, 1, wx.ALL, 5 ) + + + self.database_tablespace_panel.SetSizer( bSizer13912 ) + self.database_tablespace_panel.Layout() + bSizer13912.Fit( self.database_tablespace_panel ) + bSizer92.Add( self.database_tablespace_panel, 1, wx.ALIGN_CENTER, 5 ) + + self.database_connection_limit_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer139111 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText70111 = wx.StaticText( self.database_connection_limit_panel, wx.ID_ANY, _(u"Connection limit"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText70111.Wrap( -1 ) + + self.m_staticText70111.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer139111.Add( self.m_staticText70111, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + self.database_connection_limit = wx.SpinCtrl( self.database_connection_limit_panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.SP_ARROW_KEYS, 0, 0, 0 ) + bSizer139111.Add( self.database_connection_limit, 1, wx.ALL, 5 ) + + + self.database_connection_limit_panel.SetSizer( bSizer139111 ) + self.database_connection_limit_panel.Layout() + bSizer139111.Fit( self.database_connection_limit_panel ) + bSizer92.Add( self.database_connection_limit_panel, 1, wx.ALIGN_CENTER, 5 ) + + + bSizer158.Add( bSizer92, 0, wx.EXPAND, 5 ) + + bSizer1481 = wx.BoxSizer( wx.HORIZONTAL ) + + self.database_password_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer139121 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText70121 = wx.StaticText( self.database_password_panel, wx.ID_ANY, _(u"Password"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText70121.Wrap( -1 ) + + self.m_staticText70121.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer139121.Add( self.m_staticText70121, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + self.m_textCtrl36 = wx.TextCtrl( self.database_password_panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PASSWORD ) + bSizer139121.Add( self.m_textCtrl36, 1, wx.ALL, 5 ) + + + self.database_password_panel.SetSizer( bSizer139121 ) + self.database_password_panel.Layout() + bSizer139121.Fit( self.database_password_panel ) + bSizer1481.Add( self.database_password_panel, 1, wx.ALIGN_CENTER, 5 ) + + self.database_profile_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer1391111 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText701111 = wx.StaticText( self.database_profile_panel, wx.ID_ANY, _(u"Profile"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText701111.Wrap( -1 ) + + self.m_staticText701111.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer1391111.Add( self.m_staticText701111, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + database_profileChoices = [] + self.database_profile = wx.Choice( self.database_profile_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, database_profileChoices, 0 ) + self.database_profile.SetSelection( 0 ) + bSizer1391111.Add( self.database_profile, 1, wx.ALL, 5 ) + + + self.database_profile_panel.SetSizer( bSizer1391111 ) + self.database_profile_panel.Layout() + bSizer1391111.Fit( self.database_profile_panel ) + bSizer1481.Add( self.database_profile_panel, 1, wx.ALIGN_CENTER, 5 ) + + + bSizer158.Add( bSizer1481, 0, wx.EXPAND, 5 ) + + bSizer96 = wx.BoxSizer( wx.HORIZONTAL ) + + self.database_default_tablespace_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer1391212 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText701212 = wx.StaticText( self.database_default_tablespace_panel, wx.ID_ANY, _(u"Default tablespace"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText701212.Wrap( -1 ) + + self.m_staticText701212.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer1391212.Add( self.m_staticText701212, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + database_default_tablespaceChoices = [] + self.database_default_tablespace = wx.Choice( self.database_default_tablespace_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, database_default_tablespaceChoices, 0 ) + self.database_default_tablespace.SetSelection( 0 ) + bSizer1391212.Add( self.database_default_tablespace, 1, wx.ALL, 5 ) + + + self.database_default_tablespace_panel.SetSizer( bSizer1391212 ) + self.database_default_tablespace_panel.Layout() + bSizer1391212.Fit( self.database_default_tablespace_panel ) + bSizer96.Add( self.database_default_tablespace_panel, 1, wx.ALIGN_CENTER, 5 ) + + self.database_temporary_tablespace_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer13912121 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText7012121 = wx.StaticText( self.database_temporary_tablespace_panel, wx.ID_ANY, _(u"Temporary tablespace"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText7012121.Wrap( -1 ) + + self.m_staticText7012121.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer13912121.Add( self.m_staticText7012121, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + database_temporary_tablespaceChoices = [] + self.database_temporary_tablespace = wx.Choice( self.database_temporary_tablespace_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, database_temporary_tablespaceChoices, 0 ) + self.database_temporary_tablespace.SetSelection( 0 ) + bSizer13912121.Add( self.database_temporary_tablespace, 1, wx.ALL, 5 ) + + + self.database_temporary_tablespace_panel.SetSizer( bSizer13912121 ) + self.database_temporary_tablespace_panel.Layout() + bSizer13912121.Fit( self.database_temporary_tablespace_panel ) + bSizer96.Add( self.database_temporary_tablespace_panel, 1, wx.ALIGN_CENTER, 5 ) + + + bSizer158.Add( bSizer96, 0, wx.EXPAND, 5 ) + + bSizer14811 = wx.BoxSizer( wx.HORIZONTAL ) + + self.database_quota_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer1391211 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText701211 = wx.StaticText( self.database_quota_panel, wx.ID_ANY, _(u"Quota"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText701211.Wrap( -1 ) + + self.m_staticText701211.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer1391211.Add( self.m_staticText701211, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + self.database_quota = wx.TextCtrl( self.database_quota_panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer1391211.Add( self.database_quota, 1, wx.ALL, 5 ) + + + self.database_quota_panel.SetSizer( bSizer1391211 ) + self.database_quota_panel.Layout() + bSizer1391211.Fit( self.database_quota_panel ) + bSizer14811.Add( self.database_quota_panel, 1, wx.ALIGN_CENTER, 5 ) + + self.database_unlimited_quota_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer13911111 = wx.BoxSizer( wx.HORIZONTAL ) + + self.database_unlimited_quota = wx.CheckBox( self.database_unlimited_quota_panel, wx.ID_ANY, _(u"Unlimited quota"), wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer13911111.Add( self.database_unlimited_quota, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + + self.database_unlimited_quota_panel.SetSizer( bSizer13911111 ) + self.database_unlimited_quota_panel.Layout() + bSizer13911111.Fit( self.database_unlimited_quota_panel ) + bSizer14811.Add( self.database_unlimited_quota_panel, 1, wx.ALIGN_CENTER, 5 ) + + + bSizer158.Add( bSizer14811, 0, wx.EXPAND, 5 ) + + bSizer148111 = wx.BoxSizer( wx.HORIZONTAL ) + + self.database_account_status_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer13912111 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText7012111 = wx.StaticText( self.database_account_status_panel, wx.ID_ANY, _(u"Account status"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText7012111.Wrap( -1 ) + + self.m_staticText7012111.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer13912111.Add( self.m_staticText7012111, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + database_account_statusChoices = [] + self.database_account_status = wx.Choice( self.database_account_status_panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, database_account_statusChoices, 0 ) + self.database_account_status.SetSelection( 0 ) + bSizer13912111.Add( self.database_account_status, 1, wx.ALL, 5 ) + + + self.database_account_status_panel.SetSizer( bSizer13912111 ) + self.database_account_status_panel.Layout() + bSizer13912111.Fit( self.database_account_status_panel ) + bSizer148111.Add( self.database_account_status_panel, 1, wx.ALIGN_CENTER, 5 ) + + self.database_password_expire_panel = wx.Panel( self.m_panel54, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer139111111 = wx.BoxSizer( wx.HORIZONTAL ) + + self.database_password_expire = wx.CheckBox( self.database_password_expire_panel, wx.ID_ANY, _(u"Password expire"), wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer139111111.Add( self.database_password_expire, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + + + self.database_password_expire_panel.SetSizer( bSizer139111111 ) + self.database_password_expire_panel.Layout() + bSizer139111111.Fit( self.database_password_expire_panel ) + bSizer148111.Add( self.database_password_expire_panel, 1, wx.ALIGN_CENTER, 5 ) + + + bSizer158.Add( bSizer148111, 0, wx.EXPAND, 5 ) + + + self.m_panel54.SetSizer( bSizer158 ) + self.m_panel54.Layout() + bSizer158.Fit( self.m_panel54 ) + self.m_panel55 = wx.Panel( self.m_splitter7, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + bSizer154 = wx.BoxSizer( wx.VERTICAL ) + bSizer531 = wx.BoxSizer( wx.HORIZONTAL ) - self.m_staticText391 = wx.StaticText( self.m_panel30, wx.ID_ANY, _(u"Table:"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText391 = wx.StaticText( self.m_panel55, wx.ID_ANY, _(u"Table:"), wx.DefaultPosition, wx.DefaultSize, 0 ) self.m_staticText391.Wrap( -1 ) bSizer531.Add( self.m_staticText391, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) @@ -954,32 +1275,34 @@ def __init__( self, parent ): bSizer531.Add( ( 100, 0), 0, wx.EXPAND, 5 ) - self.btn_insert_table = wx.Button( self.m_panel30, wx.ID_ANY, _(u"Insert"), wx.DefaultPosition, wx.DefaultSize, wx.BORDER_NONE ) + self.btn_insert_table = wx.Button( self.m_panel55, wx.ID_ANY, _(u"Insert"), wx.DefaultPosition, wx.DefaultSize, wx.BORDER_NONE ) self.btn_insert_table.SetBitmap( wx.Bitmap( u"icons/16x16/add.png", wx.BITMAP_TYPE_ANY ) ) bSizer531.Add( self.btn_insert_table, 0, wx.ALL|wx.EXPAND, 2 ) - self.btn_clone_table = wx.Button( self.m_panel30, wx.ID_ANY, _(u"Clone"), wx.DefaultPosition, wx.DefaultSize, wx.BORDER_NONE ) + self.btn_clone_table = wx.Button( self.m_panel55, wx.ID_ANY, _(u"Clone"), wx.DefaultPosition, wx.DefaultSize, wx.BORDER_NONE ) self.btn_clone_table.SetBitmap( wx.Bitmap( u"icons/16x16/table_multiple.png", wx.BITMAP_TYPE_ANY ) ) self.btn_clone_table.Enable( False ) bSizer531.Add( self.btn_clone_table, 0, wx.ALL|wx.EXPAND, 5 ) - self.btn_delete_table = wx.Button( self.m_panel30, wx.ID_ANY, _(u"Delete"), wx.DefaultPosition, wx.DefaultSize, wx.BORDER_NONE ) + self.btn_delete_table1 = wx.Button( self.m_panel55, wx.ID_ANY, _(u"Delete"), wx.DefaultPosition, wx.DefaultSize, wx.BORDER_NONE ) - self.btn_delete_table.SetBitmap( wx.Bitmap( u"icons/16x16/delete.png", wx.BITMAP_TYPE_ANY ) ) - self.btn_delete_table.Enable( False ) + self.btn_delete_table1.SetBitmap( wx.Bitmap( u"icons/16x16/delete.png", wx.BITMAP_TYPE_ANY ) ) + self.btn_delete_table1.Enable( False ) - bSizer531.Add( self.btn_delete_table, 0, wx.ALL|wx.EXPAND, 2 ) + bSizer531.Add( self.btn_delete_table1, 0, wx.ALL|wx.EXPAND, 2 ) bSizer531.Add( ( 0, 0), 1, wx.EXPAND, 5 ) - bSizer80.Add( bSizer531, 0, wx.EXPAND, 5 ) + bSizer154.Add( bSizer531, 0, wx.EXPAND, 5 ) + + bSizer152 = wx.BoxSizer( wx.VERTICAL ) - self.list_ctrl_database_tables = wx.dataview.DataViewCtrl( self.m_panel30, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.list_ctrl_database_tables = wx.dataview.DataViewCtrl( self.m_panel55, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 ) self.m_dataViewColumn12 = self.list_ctrl_database_tables.AppendTextColumn( _(u"Name"), 0, wx.dataview.DATAVIEW_CELL_INERT, -1, wx.ALIGN_LEFT, wx.dataview.DATAVIEW_COL_RESIZABLE|wx.dataview.DATAVIEW_COL_SORTABLE ) self.m_dataViewColumn13 = self.list_ctrl_database_tables.AppendTextColumn( _(u"Rows"), 1, wx.dataview.DATAVIEW_CELL_INERT, -1, wx.ALIGN_RIGHT, wx.dataview.DATAVIEW_COL_RESIZABLE|wx.dataview.DATAVIEW_COL_SORTABLE ) self.m_dataViewColumn14 = self.list_ctrl_database_tables.AppendTextColumn( _(u"Size"), 2, wx.dataview.DATAVIEW_CELL_INERT, -1, wx.ALIGN_RIGHT, wx.dataview.DATAVIEW_COL_RESIZABLE|wx.dataview.DATAVIEW_COL_SORTABLE ) @@ -988,16 +1311,60 @@ def __init__( self, parent ): self.m_dataViewColumn17 = self.list_ctrl_database_tables.AppendTextColumn( _(u"Engine"), 5, wx.dataview.DATAVIEW_CELL_INERT, -1, wx.ALIGN_LEFT, wx.dataview.DATAVIEW_COL_RESIZABLE|wx.dataview.DATAVIEW_COL_SORTABLE ) self.m_dataViewColumn19 = self.list_ctrl_database_tables.AppendTextColumn( _(u"Collation"), 6, wx.dataview.DATAVIEW_CELL_INERT, -1, wx.ALIGN_LEFT, wx.dataview.DATAVIEW_COL_RESIZABLE|wx.dataview.DATAVIEW_COL_SORTABLE ) self.m_dataViewColumn18 = self.list_ctrl_database_tables.AppendTextColumn( _(u"Comments"), 7, wx.dataview.DATAVIEW_CELL_INERT, -1, wx.ALIGN_LEFT, wx.dataview.DATAVIEW_COL_RESIZABLE|wx.dataview.DATAVIEW_COL_SORTABLE ) - bSizer80.Add( self.list_ctrl_database_tables, 1, wx.ALL|wx.EXPAND, 5 ) + bSizer152.Add( self.list_ctrl_database_tables, 1, wx.ALL|wx.EXPAND, 5 ) + + + bSizer154.Add( bSizer152, 1, wx.EXPAND, 5 ) + + + self.m_panel55.SetSizer( bSizer154 ) + self.m_panel55.Layout() + bSizer154.Fit( self.m_panel55 ) + self.m_splitter7.SplitHorizontally( self.m_panel54, self.m_panel55, -1 ) + bSizer80.Add( self.m_splitter7, 1, wx.EXPAND, 5 ) + + bSizer138 = wx.BoxSizer( wx.HORIZONTAL ) + + self.btn_cancel_database = wx.Button( self.m_panel30, wx.ID_ANY, _(u"Cancel"), wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer138.Add( self.btn_cancel_database, 0, wx.ALL, 5 ) + + self.btn_apply_database = wx.Button( self.m_panel30, wx.ID_ANY, _(u"MyButton"), wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer138.Add( self.btn_apply_database, 0, wx.ALL, 5 ) + + + bSizer80.Add( bSizer138, 0, wx.EXPAND, 5 ) self.m_panel30.SetSizer( bSizer80 ) self.m_panel30.Layout() bSizer80.Fit( self.m_panel30 ) - self.m_notebook6.AddPage( self.m_panel30, _(u"Tables"), False ) + self.m_notebook6.AddPage( self.m_panel30, _(u"Options"), False ) self.m_panel31 = wx.Panel( self.m_notebook6, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) + self.m_panel31.Hide() + bSizer82 = wx.BoxSizer( wx.VERTICAL ) + self.m_staticText7011 = wx.StaticText( self.m_panel31, wx.ID_ANY, _(u"MyLabel"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText7011.Wrap( -1 ) + + self.m_staticText7011.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer82.Add( self.m_staticText7011, 0, wx.ALL, 5 ) + + self.m_staticText7011111 = wx.StaticText( self.m_panel31, wx.ID_ANY, _(u"MyLabel"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText7011111.Wrap( -1 ) + + self.m_staticText7011111.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer82.Add( self.m_staticText7011111, 0, wx.ALL, 5 ) + + self.m_staticText70111111 = wx.StaticText( self.m_panel31, wx.ID_ANY, _(u"MyLabel"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText70111111.Wrap( -1 ) + + self.m_staticText70111111.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer82.Add( self.m_staticText70111111, 0, wx.ALL, 5 ) + self.m_panel31.SetSizer( bSizer82 ) self.m_panel31.Layout() @@ -1407,7 +1774,7 @@ def __init__( self, parent ): self.panel_table.SetSizer( bSizer251 ) self.panel_table.Layout() bSizer251.Fit( self.panel_table ) - self.MainFrameNotebook.AddPage( self.panel_table, _(u"Table"), False ) + self.MainFrameNotebook.AddPage( self.panel_table, _(u"Table"), True ) MainFrameNotebookBitmap = wx.Bitmap( u"icons/16x16/table.png", wx.BITMAP_TYPE_ANY ) if ( MainFrameNotebookBitmap.IsOk() ): MainFrameNotebookImages.Add( MainFrameNotebookBitmap ) @@ -1798,7 +2165,7 @@ def __init__( self, parent ): self.panel_records.Bind( wx.EVT_RIGHT_DOWN, self.panel_recordsOnContextMenu ) - self.MainFrameNotebook.AddPage( self.panel_records, _(u"Data"), True ) + self.MainFrameNotebook.AddPage( self.panel_records, _(u"Data"), False ) MainFrameNotebookBitmap = wx.Bitmap( u"icons/16x16/text_columns.png", wx.BITMAP_TYPE_ANY ) if ( MainFrameNotebookBitmap.IsOk() ): MainFrameNotebookImages.Add( MainFrameNotebookBitmap ) @@ -1920,12 +2287,6 @@ def __init__( self, parent ): self.m_panel15.SetSizer( bSizer25 ) self.m_panel15.Layout() bSizer25.Fit( self.m_panel15 ) - self.m_menu3 = wx.Menu() - self.m_menuItem3 = wx.MenuItem( self.m_menu3, wx.ID_ANY, _(u"MyMenuItem"), wx.EmptyString, wx.ITEM_NORMAL ) - self.m_menu3.Append( self.m_menuItem3 ) - - self.m_panel15.Bind( wx.EVT_RIGHT_DOWN, self.m_panel15OnContextMenu ) - self.m_splitter4.SplitVertically( self.m_panel14, self.m_panel15, 320 ) bSizer72.Add( self.m_splitter4, 1, wx.EXPAND, 5 ) @@ -1991,13 +2352,16 @@ def __init__( self, parent ): # Connect Events self.Bind( wx.EVT_CLOSE, self.do_close ) + self.Bind( wx.EVT_MENU, self.on_settings, id = self.m_menuItem22.GetId() ) self.Bind( wx.EVT_MENU, self.on_menu_about, id = self.m_menuItem15.GetId() ) self.Bind( wx.EVT_TOOL, self.do_open_connection_manager, id = self.m_tool5.GetId() ) self.Bind( wx.EVT_TOOL, self.do_disconnect, id = self.m_tool4.GetId() ) self.MainFrameNotebook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.on_page_chaged ) self.btn_insert_table.Bind( wx.EVT_BUTTON, self.on_insert_table ) self.btn_clone_table.Bind( wx.EVT_BUTTON, self.on_clone_table ) - self.btn_delete_table.Bind( wx.EVT_BUTTON, self.on_delete_table ) + self.btn_delete_table1.Bind( wx.EVT_BUTTON, self.on_delete_table ) + self.btn_cancel_database.Bind( wx.EVT_BUTTON, self.on_cancel_database ) + self.btn_apply_database.Bind( wx.EVT_BUTTON, self.on_apply_database ) self.btn_delete_index.Bind( wx.EVT_BUTTON, self.on_delete_index ) self.btn_clear_index.Bind( wx.EVT_BUTTON, self.on_clear_index ) self.btn_insert_foreign_key.Bind( wx.EVT_BUTTON, self.on_insert_foreign_key ) @@ -2029,6 +2393,9 @@ def __del__( self ): def do_close( self, event ): event.Skip() + def on_settings( self, event ): + event.Skip() + def on_menu_about( self, event ): event.Skip() @@ -2050,6 +2417,12 @@ def on_clone_table( self, event ): def on_delete_table( self, event ): event.Skip() + def on_cancel_database( self, event ): + event.Skip() + + def on_apply_database( self, event ): + event.Skip() + def on_delete_index( self, event ): event.Skip() @@ -2136,9 +2509,6 @@ def m_splitter6OnIdle( self, event ): self.m_splitter6.SetSashPosition( -300 ) self.m_splitter6.Unbind( wx.EVT_IDLE ) - def m_panel15OnContextMenu( self, event ): - self.m_panel15.PopupMenu( self.m_menu3, event.GetPosition() ) - ########################################################################### ## Class Trash @@ -2212,6 +2582,9 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx. self.m_textCtrl10 = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE|wx.TE_RICH|wx.TE_RICH2 ) bSizer129.Add( self.m_textCtrl10, 1, wx.ALL|wx.EXPAND, 5 ) + self.m_textCtrl361 = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PASSWORD ) + bSizer129.Add( self.m_textCtrl361, 1, wx.ALL, 5 ) + bSizer93.Add( bSizer129, 1, wx.EXPAND, 5 ) @@ -2222,18 +2595,17 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx. self.ssh_tunnel_password1 = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PASSWORD ) bSizer93.Add( self.ssh_tunnel_password1, 1, wx.ALIGN_CENTER|wx.ALL, 5 ) + database_encryption_oldChoices = [] + self.database_encryption_old = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, database_encryption_oldChoices, 0 ) + self.database_encryption_old.SetSelection( 0 ) + bSizer93.Add( self.database_encryption_old, 1, wx.ALL, 5 ) + bSizer90.Add( bSizer93, 1, wx.EXPAND, 5 ) self.m_collapsiblePane2 = wx.CollapsiblePane( self, wx.ID_ANY, _(u"collapsible"), wx.DefaultPosition, wx.DefaultSize, wx.CP_DEFAULT_STYLE ) self.m_collapsiblePane2.Collapse( False ) - bSizer92 = wx.BoxSizer( wx.VERTICAL ) - - - self.m_collapsiblePane2.GetPane().SetSizer( bSizer92 ) - self.m_collapsiblePane2.GetPane().Layout() - bSizer92.Fit( self.m_collapsiblePane2.GetPane() ) bSizer90.Add( self.m_collapsiblePane2, 1, wx.EXPAND | wx.ALL, 5 ) self.tree_ctrl_sessions = wx.TreeCtrl( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TR_DEFAULT_STYLE|wx.TR_FULL_ROW_HIGHLIGHT|wx.TR_HAS_BUTTONS|wx.TR_HIDE_ROOT|wx.TR_TWIST_BUTTONS ) @@ -2271,6 +2643,12 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx. self.panel_credentials.SetSizer( bSizer48 ) self.panel_credentials.Layout() bSizer48.Fit( self.panel_credentials ) + self.m_menu3 = wx.Menu() + self.m_menuItem3 = wx.MenuItem( self.m_menu3, wx.ID_ANY, _(u"MyMenuItem"), wx.EmptyString, wx.ITEM_NORMAL ) + self.m_menu3.Append( self.m_menuItem3 ) + + self.panel_credentials.Bind( wx.EVT_RIGHT_DOWN, self.panel_credentialsOnContextMenu ) + bSizer51.Add( self.panel_credentials, 0, wx.EXPAND | wx.ALL, 0 ) self.panel_source = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) @@ -2305,15 +2683,6 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx. bSizer90.Add( bSizer51, 0, wx.EXPAND, 0 ) - self.m_panel35 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) - bSizer96 = wx.BoxSizer( wx.VERTICAL ) - - - self.m_panel35.SetSizer( bSizer96 ) - self.m_panel35.Layout() - bSizer96.Fit( self.m_panel35 ) - bSizer90.Add( self.m_panel35, 1, wx.EXPAND | wx.ALL, 5 ) - self.ssh_tunnel_port = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) bSizer90.Add( self.ssh_tunnel_port, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) @@ -2480,6 +2849,16 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx. self.filename1 = wx.FilePickerCtrl( self, wx.ID_ANY, wx.EmptyString, _(u"Select a file"), _(u"*.*"), wx.DefaultPosition, wx.DefaultSize, wx.FLP_CHANGE_DIR|wx.FLP_DEFAULT_STYLE|wx.FLP_FILE_MUST_EXIST ) bSizer90.Add( self.filename1, 0, wx.ALL, 5 ) + self.m_textCtrl351 = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer90.Add( self.m_textCtrl351, 0, wx.ALL, 5 ) + + self.m_staticText701 = wx.StaticText( self, wx.ID_ANY, _(u"Encryption"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText701.Wrap( -1 ) + + self.m_staticText701.SetMinSize( wx.Size( 150,-1 ) ) + + bSizer90.Add( self.m_staticText701, 0, wx.ALIGN_CENTER|wx.ALL, 5 ) + self.SetSizer( bSizer90 ) self.Layout() @@ -2511,6 +2890,9 @@ def rad_view_constraintOnContextMenu( self, event ): def tree_ctrl_sessionsOnContextMenu( self, event ): self.tree_ctrl_sessions.PopupMenu( self.m_menu12, event.GetPosition() ) + def panel_credentialsOnContextMenu( self, event ): + self.panel_credentials.PopupMenu( self.m_menu3, event.GetPosition() ) + def TrashOnContextMenu( self, event ): self.PopupMenu( self.m_menu11, event.GetPosition() ) From 8465a43434dd4447fa01e11ce02dddaa99c89753 Mon Sep 17 00:00:00 2001 From: gtripoli Date: Mon, 9 Mar 2026 18:52:13 +0100 Subject: [PATCH 05/12] refactor(database): align SQLDatabase with create/alter/save pattern AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- structures/engines/database.py | 16 +++++++++++++++- structures/engines/mariadb/database.py | 5 ++++- structures/engines/mysql/database.py | 5 ++++- structures/engines/postgresql/database.py | 5 ++++- structures/engines/sqlite/database.py | 5 ++++- windows/main/controller.py | 2 +- 6 files changed, 32 insertions(+), 6 deletions(-) diff --git a/structures/engines/database.py b/structures/engines/database.py index 7808629..44c4835 100755 --- a/structures/engines/database.py +++ b/structures/engines/database.py @@ -76,8 +76,22 @@ def refresh(self): if getattr(self, observable_lazy_list_name, None) != (observable_lazy_list := getattr(original_database, observable_lazy_list_name, None)): observable_lazy_list.refresh() + @property + def is_new(self) -> bool: + return self.id <= -1 + + def save(self) -> bool: + if self.is_new: + return self.create() + + return self.alter() + + @abc.abstractmethod + def create(self) -> bool: + raise NotImplementedError + @abc.abstractmethod - def apply(self) -> bool: + def alter(self) -> bool: raise NotImplementedError diff --git a/structures/engines/mariadb/database.py b/structures/engines/mariadb/database.py index 2e2cdad..fd03456 100644 --- a/structures/engines/mariadb/database.py +++ b/structures/engines/mariadb/database.py @@ -17,7 +17,10 @@ class MariaDBDatabase(SQLDatabase): character_set: Optional[str] = None encryption: Optional[str] = None - def apply(self) -> bool: + def create(self) -> bool: + return False + + def alter(self) -> bool: clauses: list[str] = [] if self.character_set: diff --git a/structures/engines/mysql/database.py b/structures/engines/mysql/database.py index ad6a836..fb82b2e 100644 --- a/structures/engines/mysql/database.py +++ b/structures/engines/mysql/database.py @@ -28,7 +28,10 @@ class MySQLDatabase(SQLDatabase): character_set: Optional[str] = None encryption: Optional[str] = None - def apply(self) -> bool: + def create(self) -> bool: + return False + + def alter(self) -> bool: clauses: list[str] = [] if self.character_set: diff --git a/structures/engines/postgresql/database.py b/structures/engines/postgresql/database.py index ba75200..4cf58e7 100644 --- a/structures/engines/postgresql/database.py +++ b/structures/engines/postgresql/database.py @@ -16,7 +16,10 @@ class PostgreSQLDatabase(SQLDatabase): tablespace: Optional[str] = None connection_limit: Optional[int] = None - def apply(self) -> bool: + def create(self) -> bool: + return False + + def alter(self) -> bool: statements: list[str] = [] if self.tablespace: diff --git a/structures/engines/sqlite/database.py b/structures/engines/sqlite/database.py index 9e65390..0454908 100644 --- a/structures/engines/sqlite/database.py +++ b/structures/engines/sqlite/database.py @@ -15,7 +15,10 @@ @dataclasses.dataclass class SQLiteDatabase(SQLDatabase): - def apply(self) -> bool: + def create(self) -> bool: + return False + + def alter(self) -> bool: return False diff --git a/windows/main/controller.py b/windows/main/controller.py index faecadc..f0724ff 100755 --- a/windows/main/controller.py +++ b/windows/main/controller.py @@ -340,7 +340,7 @@ def on_apply_database(self, event: wx.Event): return try: - database.apply() + database.save() session.context.databases.refresh() database = next( From 658a923e58649b8d066b01b3f74ef72cecc8adb1 Mon Sep 17 00:00:00 2001 From: gtripoli Date: Tue, 10 Mar 2026 11:29:14 +0100 Subject: [PATCH 06/12] feat(dump): object-driven SQL dump + docs/tests + database delete UI wiring AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- PROJECT_STATUS.md | 16 +- PeterSQL.fbp | 77 ++++- ROADMAP.md | 23 +- structures/engines/database.py | 40 ++- structures/engines/dump.py | 275 ++++++++++++++++++ structures/engines/mariadb/database.py | 76 ++++- structures/engines/mysql/database.py | 76 ++++- structures/engines/postgresql/database.py | 60 +++- structures/engines/sqlite/database.py | 31 +- tests/engines/base_database_tests.py | 119 ++++++++ .../engines/mariadb/test_integration_suite.py | 18 ++ tests/engines/mysql/test_integration_suite.py | 18 ++ .../postgresql/test_integration_suite.py | 14 + tests/engines/sqlite/test_context.py | 23 ++ .../engines/sqlite/test_integration_suite.py | 6 + windows/main/controller.py | 59 ++++ windows/views.py | 9 +- 17 files changed, 882 insertions(+), 58 deletions(-) create mode 100644 structures/engines/dump.py create mode 100644 tests/engines/base_database_tests.py diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index cbceeb6..7c0e50c 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -1,6 +1,6 @@ # PeterSQL — Project Status -> **Last Updated:** 2026-03-07 +> **Last Updated:** 2026-03-10 > **Validation Policy:** new engine features are marked **PARTIAL** until broader integration validation is complete. --- @@ -27,6 +27,8 @@ | **PostgreSQL Procedure** | Class + CRUD methods exist, context introspection exists, integration tests now cover create/alter/drop across supported PG versions; broader validation still ongoing. | | **Check Constraints (MySQL/MariaDB/PostgreSQL)** | Engine classes and introspection exist, cross-version validation still needed. | | **Connection Reliability Features** | Persistent connection statistics, empty DB password support, and TLS auto-retry are implemented and need longer real-world validation. | +| **SQL Dump / Backup** | `SQLDatabase.dump()` produces object-driven `.sql` dumps (schema + records); restore/import workflow is still missing. | +| **Database Lifecycle (Create/Drop)** | Engine database objects expose lifecycle methods, but context/UI workflow parity is still incomplete. | ### ❌ Missing / Not Implemented @@ -36,7 +38,6 @@ | **Database Create/Drop UI** | No complete create/drop workflow across engines. | | **Schema/Sequence Management** | PostgreSQL schema/sequence CRUD is not available. | | **User/Role/Grants** | Not implemented for any engine. | -| **Import/Export** | Dump/restore and structured data import/export not implemented. | --- @@ -72,7 +73,7 @@ | View / Trigger / Function | ✅ | ✅ | ✅ | ✅ | Implemented in engine layer. | | Check Constraint | 🟡 | 🟡 | 🟡 | 🟡 | Implemented (`MySQLCheck` + `get_checks()`), validation ongoing. | | Procedure | 🟡 | 🟡 | 🟡 | 🟡 | Implemented (`MySQLProcedure` + `get_procedures()`), broader validation ongoing. | -| Database Create/Drop | ❌ | ✅ | ❌ | ❌ | Read-only listing in context. | +| Database Create/Drop | 🟡 | ✅ | 🟡 | 🟡 | Engine object lifecycle methods exist; context/UI wiring still partial. | --- @@ -84,7 +85,7 @@ | View / Trigger / Function | ✅ | ✅ | ✅ | ✅ | Implemented in engine layer. | | Check Constraint | 🟡 | 🟡 | 🟡 | 🟡 | Implemented (`MariaDBCheck` + `get_checks()`), validation ongoing. | | Procedure | 🟡 | 🟡 | 🟡 | 🟡 | Implemented (`MariaDBProcedure` + `get_procedures()`), broader validation ongoing. | -| Database Create/Drop | ❌ | ✅ | ❌ | ❌ | Read-only listing in context. | +| Database Create/Drop | 🟡 | ✅ | 🟡 | 🟡 | Engine object lifecycle methods exist; context/UI wiring still partial. | --- @@ -97,6 +98,7 @@ | Function | 🟡 | 🟡 | 🟡 | 🟡 | `PostgreSQLFunction` implemented, still under validation. | | Procedure | 🟡 | 🟡 | 🟡 | 🟡 | `PostgreSQLProcedure` implemented, still under validation. | | Check Constraint | 🟡 | 🟡 | 🟡 | 🟡 | Implemented (`PostgreSQLCheck` + `get_checks()`), validation ongoing. | +| Database Create/Drop | 🟡 | ✅ | 🟡 | 🟡 | Engine object lifecycle methods exist; context/UI wiring still partial. | | Schema / Sequence | ❌ | 🟡 | ❌ | ❌ | Basic schema visibility exists; no CRUD layer yet. | --- @@ -126,12 +128,14 @@ - Empty database password accepted in connection validation. - Automatic TLS retry path for MySQL/MariaDB when server requires TLS. - Unit reliability coverage for MySQL/MariaDB TLS auto-retry and SSH tunnel lifecycle contracts. +- SQL dump/backup pipeline is now object-driven via `SQLDatabase.dump()` + per-object `raw_create()`. - CI workflow split into `test`, `update` (nightly), and `release` jobs. ### Main Remaining Risks - PostgreSQL Function/Procedure now have integration coverage for create/alter/drop, but still need broader long-run/manual validation. - Check constraints across MySQL/MariaDB/PostgreSQL need more cross-version coverage. +- SQL dump/backup still needs broader cross-engine manual restore validation. - SSH tunnel integration validation with testcontainers remains blocked (existing SSH integration suites are still skipped). - UI parity lags engine parity for Trigger/Function/Procedure editors. @@ -147,7 +151,7 @@ ### Priority B — Close Engine Gaps -1. Database create/drop methods in engine contexts. +1. Complete context/UI wiring for database lifecycle (create/drop) across engines. ### Priority C — UI Completeness @@ -160,7 +164,7 @@ 1. PostgreSQL schema CRUD. 2. PostgreSQL sequence CRUD. 3. User/role/grants management. -4. Import/export workflows. +4. Restore and structured import/export workflows. --- diff --git a/PeterSQL.fbp b/PeterSQL.fbp index e118717..ec7b972 100755 --- a/PeterSQL.fbp +++ b/PeterSQL.fbp @@ -10538,7 +10538,82 @@ 0 0 wxID_ANY - MyButton + Delete + + 0 + + 0 + + + 0 + + 1 + btn_delete_database + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + on_delete_database + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 0 + 1 + + 1 + + + 0 + 0 + wxID_ANY + Apply 0 diff --git a/ROADMAP.md b/ROADMAP.md index 33291b6..9096d56 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,6 +1,6 @@ # PeterSQL — Development Roadmap -> **Last Updated:** 2026-03-07 +> **Last Updated:** 2026-03-10 > **Status Rule:** newly implemented features are tracked as **PARTIAL** until validated across supported versions. --- @@ -56,6 +56,18 @@ This roadmap reflects the current project state and separates: - **Validation status:** unit tests now cover connection statistics updates, MySQL/MariaDB TLS retry behavior, and SSH tunnel context lifecycle contracts. - **Next:** unblock and run SSH testcontainers integration validation (currently skipped) + long-run behavioral validation. +- [x] **SQL dump/backup object-driven flow** (PARTIAL) + - **Scope:** `SQLDatabase.dump()` now generates SQL dump files through domain objects (`raw_create()`), with ordered schema + records sections. + - **Files:** + - `structures/engines/database.py` + - `structures/engines/dump.py` + - `structures/engines/mysql/database.py` + - `structures/engines/mariadb/database.py` + - `structures/engines/postgresql/database.py` + - `structures/engines/sqlite/database.py` + - **Validation status:** unit suite is green in serial and xdist runs. + - **Next:** cross-engine manual restore/import verification from produced dumps. + --- ## 🟡 P1 - Engine Gaps @@ -68,8 +80,8 @@ This roadmap reflects the current project state and separates: - **Status:** Engine CRUD + introspection implemented, integration tests added. - **Files:** `structures/engines/mariadb/context.py`, `structures/engines/mariadb/database.py`, `tests/engines/mariadb/test_integration_suite.py`, `tests/engines/base_procedure_tests.py` -- [ ] **Database create/drop API parity** - - **Current state:** list/read available, lifecycle operations missing in contexts. +- [ ] **Database lifecycle parity (context + UI wiring)** + - **Current state:** engine database objects expose create/alter/drop, but context/UI workflow remains read/list oriented. - **Files:** `structures/engines/*/context.py` --- @@ -100,7 +112,7 @@ This roadmap reflects the current project state and separates: - [ ] PostgreSQL sequence CRUD - [ ] User/role management - [ ] Privileges/grants management -- [ ] Import/export workflows (dump/restore + structured data) +- [ ] Restore + structured import/export workflows - [ ] PostgreSQL advanced objects (materialized views, partitioning, extensions) --- @@ -120,7 +132,7 @@ Before moving a PARTIAL item to DONE: ### Current Status -- **P0 implemented (partial):** 4/4 +- **P0 implemented (partial):** 5/5 - **P1 gaps closed:** 2/3 - **P2 UI tasks complete:** 1/5 - **P3 advanced tasks complete:** 0/6 @@ -131,6 +143,7 @@ Before moving a PARTIAL item to DONE: - PostgreSQL Function/Procedure integration tests now include ALTER coverage (create/alter/drop). - Check constraint support added for MySQL, MariaDB, PostgreSQL. - Connection statistics and TLS auto-retry behavior added in connection manager. +- SQL dump/backup pipeline refactored to object-driven generation (`SQLDatabase.dump()` + `raw_create()`). - SSH tunnel unit contract tests added for context lifecycle and process stop behavior. - CI workflow split into test/nightly-update/release lanes. diff --git a/structures/engines/database.py b/structures/engines/database.py index 44c4835..70802dc 100755 --- a/structures/engines/database.py +++ b/structures/engines/database.py @@ -13,6 +13,7 @@ from structures.engines.datatype import SQLDataType from structures.engines.indextype import SQLIndexType +from structures.engines.dump import create_database_dump from structures.engines.sqlite.indextype import SQLiteIndexType @@ -94,6 +95,23 @@ def create(self) -> bool: def alter(self) -> bool: raise NotImplementedError + @abc.abstractmethod + def drop(self) -> bool: + raise NotImplementedError + + def dump( + self, + /, + *, + include_schema: bool = True, + include_records: bool = True, + ) -> str: + return create_database_dump( + self, + include_schema=include_schema, + include_records=include_records, + ) + @dataclasses.dataclass(eq=False) class SQLTable(abc.ABC): @@ -213,9 +231,8 @@ def is_new(self): def generate_uuid(length: int = 8) -> str: return str(uuid.uuid4())[::-1][:length] - @abc.abstractmethod def raw_create(self) -> str: - raise NotImplementedError + raise NotImplementedError(f"{self.__class__.__name__}.raw_create() is not implemented") def get_identifier_indexes(self) -> list['SQLIndex']: identifier_indexes = [] @@ -490,6 +507,9 @@ def copy(self): field_values = {f.name: getattr(self, f.name) for f in dataclasses.fields(cls)} return cls(**field_values) + def raw_create(self) -> str: + raise NotImplementedError(f"{self.__class__.__name__}.raw_create() is not implemented") + @dataclasses.dataclass(eq=False) class SQLForeignKey(abc.ABC): @@ -695,6 +715,10 @@ def drop(self) -> bool: def alter(self) -> bool: raise NotImplementedError + @abc.abstractmethod + def raw_create(self) -> str: + raise NotImplementedError + @dataclasses.dataclass(eq=False) class SQLTrigger(abc.ABC): @@ -744,6 +768,10 @@ def drop(self) -> bool: def alter(self) -> bool: raise NotImplementedError + @abc.abstractmethod + def raw_create(self) -> str: + raise NotImplementedError + @dataclasses.dataclass(eq=False) class SQLProcedure(abc.ABC): @@ -790,6 +818,10 @@ def drop(self) -> bool: def alter(self) -> bool: raise NotImplementedError + @abc.abstractmethod + def raw_create(self) -> str: + raise NotImplementedError + @dataclasses.dataclass(eq=False) class SQLFunction(abc.ABC): @@ -836,6 +868,10 @@ def drop(self) -> bool: def alter(self) -> bool: raise NotImplementedError + @abc.abstractmethod + def raw_create(self) -> str: + raise NotImplementedError + @dataclasses.dataclass(eq=False) class SQLEvent(abc.ABC): diff --git a/structures/engines/dump.py b/structures/engines/dump.py new file mode 100644 index 0000000..cca3645 --- /dev/null +++ b/structures/engines/dump.py @@ -0,0 +1,275 @@ +import datetime +import decimal +import json +import pathlib + +from typing import Any + + +def create_database_dump( + database: Any, + /, + *, + include_schema: bool = True, + include_records: bool = True, +) -> str: + database.context.set_database(database) + dump_path = _build_dump_path(database.name) + with dump_path.open("w", encoding="utf-8") as handle: + _write_header(handle) + if include_schema: + _write_schema(handle, database) + if include_records: + _write_records(handle, database) + + return str(dump_path) + + +def _build_dump_path(database_name: str) -> pathlib.Path: + now = datetime.datetime.now() + safe_name = "".join(char if char.isalnum() or char == "_" else "_" for char in database_name) + suffix = now.strftime("%Y%m%d_%H%M%S_%f") + filename = f"petersql_backup_{safe_name}_{suffix}.sql" + return pathlib.Path.cwd() / filename + + +def _write_header(handle): + now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + handle.write(f"-- This backup was created by PeterSQL on {now}\n\n") + + +def _write_records(handle, database: Any): + _write_section_title(handle, "Insert records") + statements = _collect_record_statements(database) + _write_statements(handle, statements) + + +def _write_schema(handle, database: Any): + _write_section_title(handle, "Create database") + _write_statements(handle, _collect_database_statements(database)) + + _write_section_title(handle, "Create tables") + _write_statements(handle, _collect_table_statements(database)) + + _write_section_title(handle, "Create indexes/triggers/views/procedures/functions") + _write_statements(handle, _collect_secondary_statements(database)) + + +def _write_section_title(handle, title: str): + handle.write(f"-- {title}\n") + handle.write("-- ----------------------------------------\n") + + +def _write_statements(handle, statements: list[str]): + if not statements: + handle.write("-- No statements\n\n") + return + + for statement in statements: + if not statement: + continue + handle.write(statement.rstrip()) + handle.write("\n\n") + + +def _collect_database_statements(database: Any) -> list[str]: + context = database.context + context_name = context.__class__.__name__.lower() + if "sqlite" in context_name: + return ["-- SQLite does not support CREATE DATABASE statements"] + + database_name = context.quote_identifier(database.name) + create_statement = f"CREATE DATABASE {database_name};" + if "postgresql" in context_name: + return [create_statement, f"\\connect {database.name}"] + + return [create_statement, f"USE {database_name};"] + + +def _collect_table_statements(database: Any) -> list[str]: + statements = [] + tables = sorted(list(database.tables), key=lambda table: table.name) + for table in tables: + statements.append(_normalize_statement(table.raw_create())) + + return [statement for statement in statements if statement] + + +def _collect_secondary_statements(database: Any) -> list[str]: + statements = [] + statements.extend(_collect_index_statements(database)) + statements.extend(_collect_trigger_statements(database)) + statements.extend(_collect_view_statements(database)) + statements.extend(_collect_procedure_statements(database)) + statements.extend(_collect_function_statements(database)) + return [statement for statement in statements if statement] + + +def _collect_index_statements(database: Any) -> list[str]: + statements = [] + context_name = database.context.__class__.__name__.lower() + tables = sorted(list(database.tables), key=lambda table: table.name) + for table in tables: + indexes = sorted(list(table.indexes), key=lambda index: index.name) + for index in indexes: + if not _is_dumpable_index(index, context_name): + continue + + statements.append(_normalize_statement(index.raw_create())) + + return [statement for statement in statements if statement] + + +def _collect_record_statements(database: Any) -> list[str]: + statements = [] + tables = sorted(list(database.tables), key=lambda table: table.name) + for table in tables: + statements.extend(_table_record_statements(table)) + + return statements + + +def _collect_trigger_statements(database: Any) -> list[str]: + context_name = database.context.__class__.__name__.lower() + triggers = sorted(list(getattr(database, "triggers", [])), key=lambda trigger: trigger.name) + if "mysql" in context_name or "mariadb" in context_name: + return [_mysql_block_statement(trigger.raw_create()) for trigger in triggers] + + return [_normalize_statement(trigger.raw_create()) for trigger in triggers] + + +def _collect_view_statements(database: Any) -> list[str]: + views = sorted(list(getattr(database, "views", [])), key=lambda view: view.name) + return [_normalize_statement(view.raw_create()) for view in views] + + +def _collect_function_statements(database: Any) -> list[str]: + context_name = database.context.__class__.__name__.lower() + functions = sorted(list(getattr(database, "functions", [])), key=lambda function: function.name) + if "mysql" in context_name or "mariadb" in context_name: + return [_mysql_block_statement(function.raw_create()) for function in functions] + + return [_normalize_statement(function.raw_create()) for function in functions] + + +def _collect_procedure_statements(database: Any) -> list[str]: + context_name = database.context.__class__.__name__.lower() + procedures = sorted(list(getattr(database, "procedures", [])), key=lambda procedure: procedure.name) + if "mysql" in context_name or "mariadb" in context_name: + return [_mysql_block_statement(procedure.raw_create()) for procedure in procedures] + + return [_normalize_statement(procedure.raw_create()) for procedure in procedures] + + +def _is_dumpable_index(index: Any, context_name: str) -> bool: + if index.type.name == "PRIMARY": + return False + + if "sqlite" in context_name and index.name.startswith("sqlite_autoindex_"): + return False + + return True + + +def _mysql_block_statement(statement: str) -> str: + statement = statement.strip().rstrip(";") + return f"DELIMITER $$\n{statement}$$\nDELIMITER ;" + + +def _normalize_statement(statement: str) -> str: + statement = statement.strip().rstrip(";") + if not statement: + return "" + return f"{statement};" + + +def _render_literal(value: Any, table: Any) -> str: + if value is None: + return "NULL" + if isinstance(value, bool): + return _render_boolean(value, table) + if isinstance(value, (int, float, decimal.Decimal)): + return str(value) + if isinstance(value, datetime.datetime): + return _quote_literal(value.isoformat(sep=" ")) + if isinstance(value, (datetime.date, datetime.time)): + return _quote_literal(value.isoformat()) + if isinstance(value, (bytes, bytearray, memoryview)): + return _quote_literal(bytes(value).hex()) + if isinstance(value, (dict, list, tuple, set)): + return _quote_literal(json.dumps(value, ensure_ascii=False)) + + return _quote_literal(str(value)) + + +def _render_boolean(value: bool, table: Any) -> str: + context_name = table.database.context.__class__.__name__.lower() + if "postgresql" in context_name: + return "TRUE" if value else "FALSE" + return "1" if value else "0" + + +def _table_record_statements(table: Any) -> list[str]: + context = table.database.context + columns = [column for column in table.columns if getattr(column, "virtuality", None) is None] + if not columns: + return [] + + table_name = _table_reference(table) + column_names = ", ".join(context.quote_identifier(column.name) for column in columns) + ordering = ", ".join(context.quote_identifier(column.name) for column in columns) + return _table_record_pages(table, table_name, column_names, columns, ordering) + + +def _table_record_pages( + table: Any, + table_name: str, + column_names: str, + columns: list[Any], + ordering: str, +) -> list[str]: + offset = 0 + limit = 1000 + statements = [] + while True: + records = table.database.context.get_records(table, limit=limit, offset=offset, orders=ordering) + if not records: + break + + statements.extend(_table_record_page(table, table_name, column_names, columns, records)) + if len(records) < limit: + break + + offset += limit + + return statements + + +def _table_record_page( + table: Any, + table_name: str, + column_names: str, + columns: list[Any], + records: list[Any], +) -> list[str]: + statements = [] + for record in records: + values = [_render_literal(record.values.get(column.name), table) for column in columns] + values_sql = ", ".join(values) + statements.append(f"INSERT INTO {table_name} ({column_names}) VALUES ({values_sql});") + + return statements + + +def _table_reference(table: Any) -> str: + context = table.database.context + context_name = context.__class__.__name__.lower() + if "sqlite" in context_name: + return context.quote_identifier(table.name) + + return table.fully_qualified_name + + +def _quote_literal(value: str) -> str: + escaped = value.replace("'", "''") + return f"'{escaped}'" \ No newline at end of file diff --git a/structures/engines/mariadb/database.py b/structures/engines/mariadb/database.py index fd03456..d383b8a 100644 --- a/structures/engines/mariadb/database.py +++ b/structures/engines/mariadb/database.py @@ -17,10 +17,7 @@ class MariaDBDatabase(SQLDatabase): character_set: Optional[str] = None encryption: Optional[str] = None - def create(self) -> bool: - return False - - def alter(self) -> bool: + def _build_database_clauses(self) -> list[str]: clauses: list[str] = [] if self.character_set: @@ -32,6 +29,19 @@ def alter(self) -> bool: if self.encryption: clauses.append(f"ENCRYPTION = '{str(self.encryption).upper()}'") + return clauses + + def create(self) -> bool: + clauses = self._build_database_clauses() + query = f"CREATE DATABASE {self.context.quote_identifier(self.name)}" + if clauses: + query += f" {' '.join(clauses)}" + + return self.context.execute(query) + + def alter(self) -> bool: + clauses = self._build_database_clauses() + if not clauses: return False @@ -40,6 +50,10 @@ def alter(self) -> bool: ) return True + def drop(self) -> bool: + query = f"DROP DATABASE {self.context.quote_identifier(self.name)}" + return self.context.execute(query) + @dataclasses.dataclass(eq=False) class MariaDBTable(SQLTable): @@ -224,6 +238,9 @@ def drop(self) -> bool: @dataclasses.dataclass(eq=False) class MariaDBIndex(SQLIndex): + def raw_create(self) -> str: + return str(MariaDBIndexBuilder(self)) + def create(self) -> bool: if self.type == MariaDBIndexType.PRIMARY: return self.table.database.context.execute(f"""ALTER TABLE {self.table.fully_qualified_name} ADD PRIMARY KEY ({", ".join(self.columns)})""") @@ -356,9 +373,12 @@ def delete(self) -> bool: class MariaDBView(SQLView): + def raw_create(self) -> str: + return f"CREATE VIEW {self.fully_qualified_name} AS {self.statement}" + def create(self) -> bool: self.database.context.set_database(self.database) - return self.database.context.execute(f"CREATE VIEW {self.fully_qualified_name} AS {self.statement}") + return self.database.context.execute(self.raw_create()) def drop(self) -> bool: self.database.context.set_database(self.database) @@ -370,8 +390,20 @@ def alter(self) -> bool: class MariaDBTrigger(SQLTrigger): + def _show_create_trigger(self) -> str: + context = self.database.context + context.execute(f"SHOW CREATE TRIGGER {self.quoted_name}") + row = context.fetchone() + return row.get("SQL Original Statement", "") if row else "" + + def raw_create(self) -> str: + if self.statement.strip().upper().startswith(("BEFORE", "AFTER")): + return f"CREATE TRIGGER {self.fully_qualified_name} {self.statement}" + + return self._show_create_trigger() + def create(self) -> bool: - return self.database.context.execute(f"CREATE TRIGGER {self.fully_qualified_name} {self.statement}") + return self.database.context.execute(self.raw_create()) def drop(self) -> bool: return self.database.context.execute(f"DROP TRIGGER IF EXISTS {self.fully_qualified_name}") @@ -388,9 +420,21 @@ class MariaDBFunction(SQLFunction): deterministic: bool = False statement: str = "" + def _show_create_function(self) -> str: + context = self.database.context + context.execute(f"SHOW CREATE FUNCTION {self.fully_qualified_name}") + row = context.fetchone() + return row.get("Create Function", "") if row else "" + def create(self) -> bool: + return self.database.context.execute(self.raw_create()) + + def raw_create(self) -> str: + if not self.statement.strip() or not self.returns.strip(): + return self._show_create_function() + deterministic = "DETERMINISTIC" if self.deterministic else "NOT DETERMINISTIC" - query = f""" + return f""" CREATE FUNCTION {self.fully_qualified_name}({self.parameters}) RETURNS {self.returns} {deterministic} @@ -398,7 +442,6 @@ def create(self) -> bool: {self.statement}; END """ - return self.database.context.execute(query) def drop(self) -> bool: return self.database.context.execute(f"DROP FUNCTION IF EXISTS {self.fully_qualified_name}") @@ -421,16 +464,27 @@ def _render_body(statement: str) -> str: return f"{body};" + def _show_create_procedure(self) -> str: + context = self.database.context + context.execute(f"SHOW CREATE PROCEDURE {self.fully_qualified_name}") + row = context.fetchone() + return row.get("Create Procedure", "") if row else "" + def create(self) -> bool: + self.database.context.set_database(self.database) + return self.database.context.execute(self.raw_create()) + + def raw_create(self) -> str: + if not self.statement.strip(): + return self._show_create_procedure() + body = self._render_body(self.statement) - query = f""" + return f""" CREATE PROCEDURE {self.fully_qualified_name}({self.parameters}) BEGIN {body} END """ - self.database.context.set_database(self.database) - return self.database.context.execute(query) def drop(self) -> bool: self.database.context.set_database(self.database) diff --git a/structures/engines/mysql/database.py b/structures/engines/mysql/database.py index fb82b2e..fd2694d 100644 --- a/structures/engines/mysql/database.py +++ b/structures/engines/mysql/database.py @@ -28,10 +28,7 @@ class MySQLDatabase(SQLDatabase): character_set: Optional[str] = None encryption: Optional[str] = None - def create(self) -> bool: - return False - - def alter(self) -> bool: + def _build_database_clauses(self) -> list[str]: clauses: list[str] = [] if self.character_set: @@ -43,6 +40,19 @@ def alter(self) -> bool: if self.encryption: clauses.append(f"ENCRYPTION = '{str(self.encryption).upper()}'") + return clauses + + def create(self) -> bool: + clauses = self._build_database_clauses() + query = f"CREATE DATABASE {self.context.quote_identifier(self.name)}" + if clauses: + query += f" {' '.join(clauses)}" + + return self.context.execute(query) + + def alter(self) -> bool: + clauses = self._build_database_clauses() + if not clauses: return False @@ -51,6 +61,10 @@ def alter(self) -> bool: ) return True + def drop(self) -> bool: + query = f"DROP DATABASE {self.context.quote_identifier(self.name)}" + return self.context.execute(query) + @dataclasses.dataclass(eq=False) @@ -222,6 +236,9 @@ def drop(self) -> bool: @dataclasses.dataclass(eq=False) class MySQLIndex(SQLIndex): + def raw_create(self) -> str: + return str(MySQLIndexBuilder(self)) + def create(self) -> bool: if self.type == MySQLIndexType.PRIMARY: return self.table.database.context.execute(f"""ALTER TABLE `{self.table.database.name}`.`{self.table.name}` ADD PRIMARY KEY ({", ".join(self.columns)})""") @@ -354,9 +371,12 @@ def delete(self) -> bool: class MySQLView(SQLView): + def raw_create(self) -> str: + return f"CREATE VIEW {self.fully_qualified_name} AS {self.statement}" + def create(self) -> bool: self.database.context.set_database(self.database) - return self.database.context.execute(f"CREATE VIEW {self.fully_qualified_name} AS {self.statement}") + return self.database.context.execute(self.raw_create()) def drop(self) -> bool: self.database.context.set_database(self.database) @@ -368,8 +388,20 @@ def alter(self) -> bool: class MySQLTrigger(SQLTrigger): + def _show_create_trigger(self) -> str: + context = self.database.context + context.execute(f"SHOW CREATE TRIGGER {self.quoted_name}") + row = context.fetchone() + return row.get("SQL Original Statement", "") if row else "" + + def raw_create(self) -> str: + if self.statement.strip().upper().startswith(("BEFORE", "AFTER")): + return f"CREATE TRIGGER {self.fully_qualified_name} {self.statement}" + + return self._show_create_trigger() + def create(self) -> bool: - return self.database.context.execute(f"CREATE TRIGGER {self.fully_qualified_name} {self.statement}") + return self.database.context.execute(self.raw_create()) def drop(self) -> bool: return self.database.context.execute(f"DROP TRIGGER IF EXISTS {self.fully_qualified_name}") @@ -386,9 +418,21 @@ class MySQLFunction(SQLFunction): deterministic: bool = False sql: str = "" + def _show_create_function(self) -> str: + context = self.database.context + context.execute(f"SHOW CREATE FUNCTION {self.fully_qualified_name}") + row = context.fetchone() + return row.get("Create Function", "") if row else "" + def create(self) -> bool: + return self.database.context.execute(self.raw_create()) + + def raw_create(self) -> str: + if not self.sql.strip() or not self.returns.strip(): + return self._show_create_function() + deterministic = "DETERMINISTIC" if self.deterministic else "NOT DETERMINISTIC" - query = f""" + return f""" CREATE FUNCTION {self.fully_qualified_name}({self.parameters}) RETURNS {self.returns} {deterministic} @@ -396,7 +440,6 @@ def create(self) -> bool: {self.sql}; END """ - return self.database.context.execute(query) def drop(self) -> bool: return self.database.context.execute(f"DROP FUNCTION IF EXISTS {self.fully_qualified_name}") @@ -419,16 +462,27 @@ def _render_body(statement: str) -> str: return f"{body};" + def _show_create_procedure(self) -> str: + context = self.database.context + context.execute(f"SHOW CREATE PROCEDURE {self.fully_qualified_name}") + row = context.fetchone() + return row.get("Create Procedure", "") if row else "" + def create(self) -> bool: + self.database.context.set_database(self.database) + return self.database.context.execute(self.raw_create()) + + def raw_create(self) -> str: + if not self.statement.strip(): + return self._show_create_procedure() + body = self._render_body(self.statement) - query = f""" + return f""" CREATE PROCEDURE {self.fully_qualified_name}({self.parameters}) BEGIN {body} END """ - self.database.context.set_database(self.database) - return self.database.context.execute(query) def drop(self) -> bool: self.database.context.set_database(self.database) diff --git a/structures/engines/postgresql/database.py b/structures/engines/postgresql/database.py index 4cf58e7..57be67d 100644 --- a/structures/engines/postgresql/database.py +++ b/structures/engines/postgresql/database.py @@ -16,8 +16,24 @@ class PostgreSQLDatabase(SQLDatabase): tablespace: Optional[str] = None connection_limit: Optional[int] = None + def _build_create_database_clauses(self) -> list[str]: + clauses: list[str] = [] + + if self.tablespace: + clauses.append(f"TABLESPACE {self.context.quote_identifier(self.tablespace)}") + + if self.connection_limit is not None: + clauses.append(f"CONNECTION LIMIT {int(self.connection_limit)}") + + return clauses + def create(self) -> bool: - return False + clauses = self._build_create_database_clauses() + query = f"CREATE DATABASE {self.context.quote_identifier(self.name)}" + if clauses: + query += f" {' '.join(clauses)}" + + return self.context.execute(query) def alter(self) -> bool: statements: list[str] = [] @@ -40,6 +56,10 @@ def alter(self) -> bool: return True + def drop(self) -> bool: + query = f"DROP DATABASE {self.context.quote_identifier(self.name)}" + return self.context.execute(query) + @dataclasses.dataclass(eq=False) class PostgreSQLTable(SQLTable): @@ -242,8 +262,17 @@ def modify(self, current: Self): @dataclasses.dataclass(eq=False) class PostgreSQLIndex(SQLIndex): + def raw_create(self) -> str: + if self.type.name == "PRIMARY": + return "" + + return str(PostgreSQLIndexBuilder(self, inline=False)) + def create(self) -> bool: - statement = str(PostgreSQLIndexBuilder(self, inline=False)) + statement = self.raw_create() + if not statement: + return True + return self.table.database.context.execute(statement) def drop(self) -> bool: @@ -373,9 +402,11 @@ class PostgreSQLView(SQLView): def fully_qualified_name(self): return self.database.context.qualify('public', self.name) + def raw_create(self) -> str: + return f'CREATE VIEW {self.fully_qualified_name} AS {self.statement};' + def create(self) -> bool: - statement = f'CREATE VIEW {self.fully_qualified_name} AS {self.statement};' - return self.database.context.execute(statement) + return self.database.context.execute(self.raw_create()) def drop(self) -> bool: statement = f'DROP VIEW IF EXISTS {self.fully_qualified_name};' @@ -398,8 +429,8 @@ class PostgreSQLFunction(SQLFunction): def fully_qualified_name(self): return self.database.context.qualify('public', self.name) - def create(self) -> bool: - create_statement = f""" + def raw_create(self) -> str: + return f""" CREATE OR REPLACE FUNCTION {self.fully_qualified_name}({self.parameters}) RETURNS {self.returns} LANGUAGE {self.language} @@ -410,7 +441,9 @@ def create(self) -> bool: END; $$; """ - return self.database.context.execute(create_statement) + + def create(self) -> bool: + return self.database.context.execute(self.raw_create()) def drop(self) -> bool: statement = f'DROP FUNCTION IF EXISTS {self.fully_qualified_name}({self.parameters});' @@ -431,8 +464,8 @@ class PostgreSQLProcedure(SQLProcedure): def fully_qualified_name(self): return self.database.context.qualify('public', self.name) - def create(self) -> bool: - create_statement = f""" + def raw_create(self) -> str: + return f""" CREATE OR REPLACE PROCEDURE {self.fully_qualified_name}({self.parameters}) LANGUAGE {self.language} AS $$ @@ -441,7 +474,9 @@ def create(self) -> bool: END; $$; """ - return self.database.context.execute(create_statement) + + def create(self) -> bool: + return self.database.context.execute(self.raw_create()) def drop(self) -> bool: statement = f'DROP PROCEDURE IF EXISTS {self.fully_qualified_name}({self.parameters});' @@ -454,8 +489,11 @@ def alter(self) -> bool: @dataclasses.dataclass class PostgreSQLTrigger(SQLTrigger): + def raw_create(self) -> str: + return self.statement + def create(self) -> bool: - return self.database.context.execute(self.statement) + return self.database.context.execute(self.raw_create()) def alter(self) -> bool: self.drop() diff --git a/structures/engines/sqlite/database.py b/structures/engines/sqlite/database.py index 0454908..79d1363 100644 --- a/structures/engines/sqlite/database.py +++ b/structures/engines/sqlite/database.py @@ -21,6 +21,9 @@ def create(self) -> bool: def alter(self) -> bool: return False + def drop(self) -> bool: + return False + @dataclasses.dataclass(eq=False) class SQLiteTable(SQLTable): @@ -330,25 +333,27 @@ class SQLiteIndex(SQLIndex): def fully_qualified_name(self): return self.table.database.context.qualify(self.table.database.name, self.name) - def create(self) -> bool: + def raw_create(self) -> str: if self.type == SQLiteIndexType.PRIMARY: - return False # PRIMARY is handled in table creation + return "" if self.type == SQLiteIndexType.UNIQUE and self.name.startswith("sqlite_autoindex_"): - return False # CONSTRAINT UNIQUE is handled in table creation + return "" unique_index = "UNIQUE INDEX" if self.type == SQLiteIndexType.UNIQUE else "INDEX" - quote_identifier = self.table.database.context.quote_identifier - columns_clause = ", ".join([quote_identifier(column) for column in self.columns]) where_clause = f" WHERE {self.condition}" if self.condition else "" - - statement = ( + return ( f"CREATE {unique_index} IF NOT EXISTS {self.fully_qualified_name} " - f"ON {self.table.name}({columns_clause}){where_clause} " + f"ON {self.table.name}({columns_clause}){where_clause}" ) + def create(self) -> bool: + statement = self.raw_create() + if not statement: + return False + return self.table.database.context.execute(statement) def drop(self) -> bool: @@ -474,8 +479,11 @@ def __init__(self, /, id: int, name: str, database: SQLDatabase, statement: str) super().__init__(id=id, name=name, database=database, statement=statement) + def raw_create(self) -> str: + return f"CREATE VIEW IF NOT EXISTS {self.fully_qualified_name} AS {self.statement}" + def create(self) -> bool: - return self.database.context.execute(f"CREATE VIEW IF NOT EXISTS {self.fully_qualified_name} AS {self.statement}") + return self.database.context.execute(self.raw_create()) def drop(self) -> bool: return self.database.context.execute(f"DROP VIEW IF EXISTS {self.fully_qualified_name}") @@ -485,8 +493,11 @@ def alter(self) -> bool: class SQLiteTrigger(SQLTrigger): + def raw_create(self) -> str: + return f"CREATE TRIGGER IF NOT EXISTS {self.fully_qualified_name} {self.statement}" + def create(self) -> bool: - return self.database.context.execute(f"CREATE TRIGGER IF NOT EXISTS {self.fully_qualified_name} {self.statement}") + return self.database.context.execute(self.raw_create()) def drop(self) -> bool: return self.database.context.execute(f"DROP TRIGGER IF EXISTS {self.fully_qualified_name}") diff --git a/tests/engines/base_database_tests.py b/tests/engines/base_database_tests.py new file mode 100644 index 0000000..a703367 --- /dev/null +++ b/tests/engines/base_database_tests.py @@ -0,0 +1,119 @@ +import uuid + + +class BaseDatabaseCreateAlterTests: + + def _build_new_database(self, database, session, name: str): + database_class = database.__class__ + new_database = database_class(id=-1, name=name, context=session.context) + + for key, value in self.get_create_options().items(): + if hasattr(new_database, key): + setattr(new_database, key, value) + + return new_database + + @staticmethod + def _find_database_by_name(session, name: str): + session.context.databases.refresh() + return next((db for db in session.context.databases.get_value() if db.name == name), None) + + def _execute_database_operation(self, session, operation): + if not self.requires_autocommit_for_database_ddl(): + operation() + return + + connection = getattr(session.context, "_connection", None) + if connection is None or not hasattr(connection, "autocommit"): + operation() + return + + connection.commit() + + previous_autocommit = connection.autocommit + connection.autocommit = True + try: + operation() + finally: + connection.autocommit = previous_autocommit + + def _drop_database(self, session, database) -> bool: + result = False + + def operation() -> None: + nonlocal result + result = database.drop() + + self._execute_database_operation(session, operation) + return result + + def _create_database(self, database) -> bool: + if not self.requires_autocommit_for_database_ddl(): + return database.create() + + connection = getattr(database.context, "_connection", None) + if connection is None or not hasattr(connection, "autocommit"): + return database.create() + + connection.commit() + + previous_autocommit = connection.autocommit + connection.autocommit = True + try: + return database.create() + finally: + connection.autocommit = previous_autocommit + + @staticmethod + def _build_unique_database_name() -> str: + unique_name = str(uuid.uuid4()).replace("-", "")[:12] + return f"db_{unique_name}" + + def test_database_create_and_drop(self, session, database): + name = self._build_unique_database_name() + created_database = self._build_new_database(database, session, name) + + assert self._create_database(created_database) is True + + loaded_database = self._find_database_by_name(session, name) + assert loaded_database is not None + + assert self._drop_database(session, loaded_database) is True + + def test_database_alter(self, database): + for key, value in self.get_alter_options().items(): + if hasattr(database, key): + setattr(database, key, value) + + assert database.alter() is True + + def get_create_options(self) -> dict[str, str]: + return {} + + def get_alter_options(self) -> dict[str, str]: + return {} + + def requires_autocommit_for_database_ddl(self) -> bool: + return False + + +class BaseDatabaseUnsupportedTests: + + @staticmethod + def _build_new_database(database, session, name: str): + database_class = database.__class__ + return database_class(id=-1, name=name, context=session.context) + + @staticmethod + def _build_unique_database_name() -> str: + unique_name = str(uuid.uuid4()).replace("-", "")[:12] + return f"db_{unique_name}" + + def test_database_create_returns_false(self, session, database): + name = self._build_unique_database_name() + new_database = self._build_new_database(database, session, name) + + assert new_database.create() is False + + def test_database_alter_returns_false(self, database): + assert database.alter() is False \ No newline at end of file diff --git a/tests/engines/mariadb/test_integration_suite.py b/tests/engines/mariadb/test_integration_suite.py index 2d65fba..92da41b 100644 --- a/tests/engines/mariadb/test_integration_suite.py +++ b/tests/engines/mariadb/test_integration_suite.py @@ -2,6 +2,7 @@ from structures.engines.mariadb.datatype import MariaDBDataType from structures.engines.mariadb.indextype import MariaDBIndexType +from tests.engines.base_database_tests import BaseDatabaseCreateAlterTests from tests.engines.base_table_tests import BaseTableTests from tests.engines.base_record_tests import BaseRecordTests from tests.engines.base_column_tests import BaseColumnTests @@ -102,3 +103,20 @@ def get_simple_view_statement(self) -> str: @pytest.mark.xdist_group("mariadb") class TestMariaDBViewDefiner(BaseViewDefinerTests): pass + + +@pytest.mark.integration +@pytest.mark.xdist_group("mariadb") +class TestMariaDBDatabase(BaseDatabaseCreateAlterTests): + + def get_create_options(self) -> dict[str, str]: + return { + "character_set": "utf8mb4", + "default_collation": "utf8mb4_general_ci", + } + + def get_alter_options(self) -> dict[str, str]: + return { + "character_set": "utf8mb4", + "default_collation": "utf8mb4_general_ci", + } diff --git a/tests/engines/mysql/test_integration_suite.py b/tests/engines/mysql/test_integration_suite.py index d8ee6c0..e0a54c6 100644 --- a/tests/engines/mysql/test_integration_suite.py +++ b/tests/engines/mysql/test_integration_suite.py @@ -2,6 +2,7 @@ from structures.engines.mysql.datatype import MySQLDataType from structures.engines.mysql.indextype import MySQLIndexType +from tests.engines.base_database_tests import BaseDatabaseCreateAlterTests from tests.engines.base_table_tests import BaseTableTests from tests.engines.base_record_tests import BaseRecordTests from tests.engines.base_column_tests import BaseColumnTests @@ -102,3 +103,20 @@ def get_simple_view_statement(self) -> str: @pytest.mark.xdist_group("mysql") class TestMySQLViewDefiner(BaseViewDefinerTests): pass + + +@pytest.mark.integration +@pytest.mark.xdist_group("mysql") +class TestMySQLDatabase(BaseDatabaseCreateAlterTests): + + def get_create_options(self) -> dict[str, str]: + return { + "character_set": "utf8mb4", + "default_collation": "utf8mb4_general_ci", + } + + def get_alter_options(self) -> dict[str, str]: + return { + "character_set": "utf8mb4", + "default_collation": "utf8mb4_general_ci", + } diff --git a/tests/engines/postgresql/test_integration_suite.py b/tests/engines/postgresql/test_integration_suite.py index 4a9443a..b016e9c 100644 --- a/tests/engines/postgresql/test_integration_suite.py +++ b/tests/engines/postgresql/test_integration_suite.py @@ -11,6 +11,7 @@ from structures.engines.postgresql.datatype import PostgreSQLDataType from structures.engines.postgresql.indextype import PostgreSQLIndexType +from tests.engines.base_database_tests import BaseDatabaseCreateAlterTests from tests.engines.base_table_tests import BaseTableTests from tests.engines.base_record_tests import BaseRecordTests from tests.engines.base_column_tests import BaseColumnTests @@ -136,3 +137,16 @@ class TestPostgreSQLViewIsNew(BaseViewIsNewTests): def get_simple_view_statement(self) -> str: return "SELECT 1 as id" + + +@pytest.mark.integration +@pytest.mark.xdist_group("postgresql") +class TestPostgreSQLDatabase(BaseDatabaseCreateAlterTests): + + def get_alter_options(self) -> dict[str, int]: + return { + "connection_limit": 50, + } + + def requires_autocommit_for_database_ddl(self) -> bool: + return True diff --git a/tests/engines/sqlite/test_context.py b/tests/engines/sqlite/test_context.py index 8e6a38d..a5460b8 100644 --- a/tests/engines/sqlite/test_context.py +++ b/tests/engines/sqlite/test_context.py @@ -1,3 +1,5 @@ +import pathlib + from structures.session import Session from structures.connection import Connection, ConnectionEngine from structures.configurations import SourceConfiguration @@ -105,3 +107,24 @@ def test_context_quote_identifier(self, sqlite_session): assert ctx.quote_identifier("normal") == "normal" # Names with spaces are quoted using IDENTIFIER_QUOTE_CHAR assert ctx.quote_identifier("with space") == f'{quote}with space{quote}' + + def test_database_dump(self, sqlite_session): + ctx = sqlite_session.context + database = ctx.get_databases()[0] + + with ctx.transaction() as transaction: + transaction.execute("CREATE TABLE dump_users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)") + transaction.execute("INSERT INTO dump_users (id, name) VALUES (1, 'Alice')") + + dump_path = pathlib.Path(database.dump()) + content = dump_path.read_text(encoding="utf-8") + + assert dump_path.exists() + assert "This backup was created by PeterSQL" in content + assert "-- Create database" in content + assert "-- Create tables" in content + assert "-- Insert records" in content + assert "CREATE TABLE" in content + assert "INSERT INTO" in content + + dump_path.unlink(missing_ok=True) diff --git a/tests/engines/sqlite/test_integration_suite.py b/tests/engines/sqlite/test_integration_suite.py index f5cdaad..bd4b6d5 100644 --- a/tests/engines/sqlite/test_integration_suite.py +++ b/tests/engines/sqlite/test_integration_suite.py @@ -2,6 +2,7 @@ from structures.engines.sqlite.datatype import SQLiteDataType from structures.engines.sqlite.indextype import SQLiteIndexType +from tests.engines.base_database_tests import BaseDatabaseUnsupportedTests from tests.engines.base_table_tests import BaseTableTests from tests.engines.base_record_tests import BaseRecordTests from tests.engines.base_column_tests import BaseColumnTests @@ -76,3 +77,8 @@ class TestSQLiteViewIsNew(BaseViewIsNewTests): def get_simple_view_statement(self) -> str: return "SELECT * FROM users" + + +@pytest.mark.integration +class TestSQLiteDatabase(BaseDatabaseUnsupportedTests): + pass diff --git a/windows/main/controller.py b/windows/main/controller.py index f0724ff..214169e 100755 --- a/windows/main/controller.py +++ b/windows/main/controller.py @@ -386,6 +386,65 @@ def on_cancel_database(self, event: wx.Event): logger.error(str(ex), exc_info=True) wx.MessageDialog(None, str(ex), "Error", wx.OK | wx.ICON_ERROR).ShowModal() + def on_delete_database(self, event: wx.Event): + database = CURRENT_DATABASE.get_value() + session = CURRENT_SESSION.get_value() + + if database is None or session is None: + return + + choice = wx.MessageDialog( + None, + message=_( + "Do you want to create a dump before dropping database '{database_name}'?\n\n" + "Dump is not implemented yet.\n" + "- Yes: open dump flow (coming soon, no drop).\n" + "- No: drop the database now." + ).format(database_name=database.name), + caption=_("Delete database"), + style=wx.YES_NO | wx.CANCEL | wx.CANCEL_DEFAULT | wx.ICON_WARNING, + ).ShowModal() + + if choice == wx.ID_YES: + wx.MessageBox( + _("Dump is not implemented yet. No action has been performed."), + _("Dump not available"), + wx.OK | wx.ICON_INFORMATION, + ) + return + + if choice != wx.ID_NO: + return + + try: + dropped = database.drop() + + if not dropped: + wx.MessageBox( + _("Database deletion is not supported by this engine."), + _("Delete database"), + wx.OK | wx.ICON_WARNING, + ) + return + + session.context.databases.refresh() + + CURRENT_DATABASE.set_value(None) + next_database = next(iter(session.context.databases.get_value()), None) + if next_database is not None: + CURRENT_DATABASE.set_value(next_database) + session.context.set_database(next_database) + + wx.MessageBox( + _("Database deleted successfully"), + _("Success"), + wx.OK | wx.ICON_INFORMATION, + ) + + except Exception as ex: + logger.error(str(ex), exc_info=True) + wx.MessageDialog(None, str(ex), "Error", wx.OK | wx.ICON_ERROR).ShowModal() + # VIEW def _on_current_view(self, current: SQLView): self.toggle_panel(current) diff --git a/windows/views.py b/windows/views.py index 78797fc..eb525bd 100755 --- a/windows/views.py +++ b/windows/views.py @@ -1328,7 +1328,10 @@ def __init__( self, parent ): self.btn_cancel_database = wx.Button( self.m_panel30, wx.ID_ANY, _(u"Cancel"), wx.DefaultPosition, wx.DefaultSize, 0 ) bSizer138.Add( self.btn_cancel_database, 0, wx.ALL, 5 ) - self.btn_apply_database = wx.Button( self.m_panel30, wx.ID_ANY, _(u"MyButton"), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.btn_delete_database = wx.Button( self.m_panel30, wx.ID_ANY, _(u"Delete"), wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer138.Add( self.btn_delete_database, 0, wx.ALL, 5 ) + + self.btn_apply_database = wx.Button( self.m_panel30, wx.ID_ANY, _(u"Apply"), wx.DefaultPosition, wx.DefaultSize, 0 ) bSizer138.Add( self.btn_apply_database, 0, wx.ALL, 5 ) @@ -2361,6 +2364,7 @@ def __init__( self, parent ): self.btn_clone_table.Bind( wx.EVT_BUTTON, self.on_clone_table ) self.btn_delete_table1.Bind( wx.EVT_BUTTON, self.on_delete_table ) self.btn_cancel_database.Bind( wx.EVT_BUTTON, self.on_cancel_database ) + self.btn_delete_database.Bind( wx.EVT_BUTTON, self.on_delete_database ) self.btn_apply_database.Bind( wx.EVT_BUTTON, self.on_apply_database ) self.btn_delete_index.Bind( wx.EVT_BUTTON, self.on_delete_index ) self.btn_clear_index.Bind( wx.EVT_BUTTON, self.on_clear_index ) @@ -2420,6 +2424,9 @@ def on_delete_table( self, event ): def on_cancel_database( self, event ): event.Skip() + def on_delete_database( self, event ): + event.Skip() + def on_apply_database( self, event ): event.Skip() From 8f76308068cbc03e8989b2ccdc9204a8bac7e339 Mon Sep 17 00:00:00 2001 From: gtripoli Date: Tue, 10 Mar 2026 11:35:07 +0100 Subject: [PATCH 07/12] chore(ci): disable release job until release flow is ready AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6962176..18d644d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -221,7 +221,8 @@ jobs: run: echo "Build scripts are not ready yet." release: - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]') + # Temporarily disabled: release flow is not ready yet. + if: false runs-on: ubuntu-latest steps: From 0b95612e8c68a8cf79ac98a715654157693173e5 Mon Sep 17 00:00:00 2001 From: gtripoli Date: Tue, 10 Mar 2026 12:23:38 +0100 Subject: [PATCH 08/12] change runtest.sh to runtest.py AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 18d644d..e2fdc29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,7 +109,7 @@ jobs: fi - name: Run tests (--all) - run: xvfb-run -a ./scripts/runtest.sh --all + run: xvfb-run -a ./scripts/runtest.py --all update: if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]') @@ -201,7 +201,7 @@ jobs: fi - name: Run tests and update README (--update) - run: xvfb-run -a ./scripts/runtest.sh --update + run: xvfb-run -a ./scripts/runtest.py --update - name: Commit and push updated README run: | From 6fcb36245bf26edc540928292331b56661c9d90a Mon Sep 17 00:00:00 2001 From: gtripoli Date: Tue, 10 Mar 2026 15:47:23 +0100 Subject: [PATCH 09/12] change ci AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- .github/workflows/ci.yml | 11 +++-- windows/dialogs/connections/view.py | 70 +++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2fdc29..656afef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,7 @@ name: CI permissions: contents: write + actions: write on: push: @@ -82,7 +83,7 @@ jobs: run: | mkdir -p "${WHEEL_CACHE_DIR}" - if ls "${WHEEL_CACHE_DIR}"/wxPython*.whl >/dev/null 2>&1; then + if ls "${WHEEL_CACHE_DIR}"/wxpython-*.whl >/dev/null 2>&1; then echo "wxPython wheel found in cache, skipping wheel build" echo "needs_build=false" >> "$GITHUB_OUTPUT" else @@ -95,6 +96,7 @@ jobs: run: | mkdir -p "${WHEEL_CACHE_DIR}" python -m pip wheel -w "${WHEEL_CACHE_DIR}" -r requirements.lock.txt + find "${PIP_CACHE_DIR}/wheels" -type f -name 'wxpython-*.whl' -exec cp -u {} "${WHEEL_CACHE_DIR}/" \; - name: Upgrade build tooling run: uv pip install -U pip setuptools wheel @@ -109,7 +111,7 @@ jobs: fi - name: Run tests (--all) - run: xvfb-run -a ./scripts/runtest.py --all + run: xvfb-run -a uv run ./scripts/runtest.py --all update: if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]') @@ -174,7 +176,7 @@ jobs: run: | mkdir -p "${WHEEL_CACHE_DIR}" - if ls "${WHEEL_CACHE_DIR}"/wxPython*.whl >/dev/null 2>&1; then + if ls "${WHEEL_CACHE_DIR}"/wxpython-*.whl >/dev/null 2>&1; then echo "wxPython wheel found in cache, skipping wheel build" echo "needs_build=false" >> "$GITHUB_OUTPUT" else @@ -187,6 +189,7 @@ jobs: run: | mkdir -p "${WHEEL_CACHE_DIR}" python -m pip wheel -w "${WHEEL_CACHE_DIR}" -r requirements.lock.txt + find "${PIP_CACHE_DIR}/wheels" -type f -name 'wxpython-*.whl' -exec cp -u {} "${WHEEL_CACHE_DIR}/" \; - name: Upgrade build tooling run: uv pip install -U pip setuptools wheel @@ -201,7 +204,7 @@ jobs: fi - name: Run tests and update README (--update) - run: xvfb-run -a ./scripts/runtest.py --update + run: xvfb-run -a uv run ./scripts/runtest.py --update - name: Commit and push updated README run: | diff --git a/windows/dialogs/connections/view.py b/windows/dialogs/connections/view.py index c2a9792..94b4417 100644 --- a/windows/dialogs/connections/view.py +++ b/windows/dialogs/connections/view.py @@ -31,6 +31,9 @@ class ConnectionsManager(ConnectionsDialog): + _SETTINGS_SECTION = "connections_dialog" + _SETTINGS_EXPANDED_DIRECTORIES = "expanded_directories" + def __init__(self, parent): super().__init__(parent) self._app = wx.GetApp() @@ -83,6 +86,7 @@ def __init__(self, parent): self._pending_parent_directory_id = None self._setup_event_handlers() self._update_tree_menu_state() + wx.CallAfter(self._restore_expanded_directory_paths_from_settings) def _update_tree_menu_state(self): selected_connection = CURRENT_CONNECTION() @@ -229,11 +233,75 @@ def _setup_event_handlers(self): wx.dataview.EVT_DATAVIEW_ITEM_CONTEXT_MENU, self._on_tree_item_context_menu, ) + self.connections_tree_ctrl.Bind( + wx.dataview.EVT_DATAVIEW_ITEM_EXPANDED, + self._on_tree_expansion_changed, + ) + self.connections_tree_ctrl.Bind( + wx.dataview.EVT_DATAVIEW_ITEM_COLLAPSED, + self._on_tree_expansion_changed, + ) CURRENT_DIRECTORY.subscribe(self._on_current_directory) CURRENT_CONNECTION.subscribe(self._on_current_connection) PENDING_CONNECTION.subscribe(self._on_pending_connection) + @staticmethod + def _serialize_expanded_directory_paths(paths) -> list[list[int]]: + serialized_paths = [] + for path in sorted(paths): + if not path: + continue + + if not all(isinstance(node_id, int) for node_id in path): + continue + + serialized_paths.append([int(node_id) for node_id in path]) + + return serialized_paths + + @staticmethod + def _deserialize_expanded_directory_paths(raw_paths) -> set[tuple[int, ...]]: + if not isinstance(raw_paths, list): + return set() + + deserialized_paths = set() + for raw_path in raw_paths: + if not isinstance(raw_path, list) or not raw_path: + continue + + if not all(isinstance(node_id, int) for node_id in raw_path): + continue + + deserialized_paths.add(tuple(int(node_id) for node_id in raw_path)) + + return deserialized_paths + + def _save_expanded_directory_paths_to_settings(self) -> None: + expanded_paths = self._capture_expanded_directory_paths() + serialized_paths = self._serialize_expanded_directory_paths(expanded_paths) + + if self._app.settings.get_value(self._SETTINGS_SECTION) is None: + self._app.settings.set_value(self._SETTINGS_SECTION, value={}) + + self._app.settings.set_value( + self._SETTINGS_SECTION, + self._SETTINGS_EXPANDED_DIRECTORIES, + value=serialized_paths, + ) + + def _restore_expanded_directory_paths_from_settings(self) -> None: + raw_paths = self._app.settings.get_value( + self._SETTINGS_SECTION, + self._SETTINGS_EXPANDED_DIRECTORIES, + ) + expanded_paths = self._deserialize_expanded_directory_paths(raw_paths) + self._restore_expanded_directory_paths(expanded_paths) + + def _on_tree_expansion_changed(self, event) -> None: + self._save_expanded_directory_paths_to_settings() + event.Skip() + def _on_tree_item_context_menu(self, event): position = event.GetPosition() if position == wx.DefaultPosition: @@ -760,6 +828,8 @@ def on_delete(self, *args): wx.CallAfter(self._restore_expanded_directory_paths, expanded_paths) def on_exit(self, event): + self._save_expanded_directory_paths_to_settings() + if not self._app.main_frame: self._app.do_exit(event) else: From 369170339129e812e976ba87428129d16cc1a5e2 Mon Sep 17 00:00:00 2001 From: gtripoli Date: Tue, 10 Mar 2026 17:04:52 +0100 Subject: [PATCH 10/12] change ci AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- .github/workflows/ci.yml | 16 + helpers/bindings.py | 6 +- helpers/dataview.py | 2 +- locale/de_DE/LC_MESSAGES/petersql.mo | Bin 8416 -> 0 bytes locale/de_DE/LC_MESSAGES/petersql.po | 1091 +++++++++++------- locale/en_US/LC_MESSAGES/petersql.po | 748 ++++++++---- locale/es_ES/LC_MESSAGES/petersql.mo | Bin 8405 -> 0 bytes locale/es_ES/LC_MESSAGES/petersql.po | 1091 +++++++++++------- locale/fr_FR/LC_MESSAGES/petersql.mo | Bin 8614 -> 0 bytes locale/fr_FR/LC_MESSAGES/petersql.po | 1091 +++++++++++------- locale/it_IT/LC_MESSAGES/petersql.mo | Bin 8332 -> 0 bytes locale/it_IT/LC_MESSAGES/petersql.po | 844 +++++++++----- locale/petersql.pot | 732 ++++++++---- scripts/locale/es_ES/LC_MESSAGES/petersql.po | 952 --------------- scripts/locale/fr_FR/LC_MESSAGES/petersql.po | 952 --------------- scripts/locale/it_IT/LC_MESSAGES/petersql.po | 952 --------------- scripts/locale/petersql.pot | 943 --------------- scripts/locales.py | 5 +- structures/engines/datatype.py | 8 +- structures/engines/indextype.py | 2 +- structures/engines/sqlite/database.py | 4 +- 21 files changed, 3722 insertions(+), 5717 deletions(-) delete mode 100644 locale/de_DE/LC_MESSAGES/petersql.mo delete mode 100644 locale/es_ES/LC_MESSAGES/petersql.mo delete mode 100644 locale/fr_FR/LC_MESSAGES/petersql.mo delete mode 100644 locale/it_IT/LC_MESSAGES/petersql.mo delete mode 100644 scripts/locale/es_ES/LC_MESSAGES/petersql.po delete mode 100644 scripts/locale/fr_FR/LC_MESSAGES/petersql.po delete mode 100644 scripts/locale/it_IT/LC_MESSAGES/petersql.po delete mode 100644 scripts/locale/petersql.pot diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 656afef..cc0a35a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,6 +101,14 @@ jobs: - name: Upgrade build tooling run: uv pip install -U pip setuptools wheel + - name: Preinstall wxPython from wheelhouse + run: | + if ls "${WHEEL_CACHE_DIR}"/wxpython-*.whl >/dev/null 2>&1; then + uv pip install --no-index --find-links "${WHEEL_CACHE_DIR}" wxpython==4.2.5 + else + echo "No wxPython wheel found in wheelhouse" + fi + - name: Install dependencies from wheelhouse (fallback on build) run: | if uv sync --extra dev --find-links "${WHEEL_CACHE_DIR}" --no-build; then @@ -194,6 +202,14 @@ jobs: - name: Upgrade build tooling run: uv pip install -U pip setuptools wheel + - name: Preinstall wxPython from wheelhouse + run: | + if ls "${WHEEL_CACHE_DIR}"/wxpython-*.whl >/dev/null 2>&1; then + uv pip install --no-index --find-links "${WHEEL_CACHE_DIR}" wxpython==4.2.5 + else + echo "No wxPython wheel found in wheelhouse" + fi + - name: Install dependencies from wheelhouse (fallback on build) run: | if uv sync --extra dev --find-links "${WHEEL_CACHE_DIR}" --no-build; then diff --git a/helpers/bindings.py b/helpers/bindings.py index e05b25c..9a0a417 100644 --- a/helpers/bindings.py +++ b/helpers/bindings.py @@ -110,7 +110,7 @@ def get(self) -> Any: class BindSelectionControl(AbstractBindControl): - def __init__(self, control: CONTROL_BIND_SELECTION, observable: Observable, initial: Optional[List[str]] = None): + def __init__(self, control: CONTROL_BIND_SELECTION, observable: Observable, initial: Optional[list[str]] = None): super().__init__(control, observable, event=wx.EVT_CHOICE) if initial is not None: self.control.Set(initial) @@ -226,7 +226,7 @@ def __init__(cls, name, bases, attrs): class AbstractModel(metaclass=AbstractMetaModel): - observables: List[Observable] = [] + observables: list[Observable] = [] def bind_control(self, control: CONTROLS, observable: Observable): if isinstance(control, wx.StaticText): @@ -246,7 +246,7 @@ def bind_control(self, control: CONTROLS, observable: Observable): self.observables.append(observable) - def bind_controls(self, **controls: Union[CONTROLS, Tuple[CONTROLS, Dict]]): + def bind_controls(self, **controls: Union[CONTROLS, tuple[CONTROLS, dict]]): for name, ctrl in controls.items(): if hasattr(self, name) and isinstance(getattr(self, name), Observable): observable = getattr(self, name) diff --git a/helpers/dataview.py b/helpers/dataview.py index 8e9d6e0..a59467c 100644 --- a/helpers/dataview.py +++ b/helpers/dataview.py @@ -105,7 +105,7 @@ def __init__(self, column_count: Optional[int] = None): AbstractBaseDataModel.__init__(self, column_count) wx.dataview.PyDataViewModel.__init__(self) - def _load(self, data: List[Any]): + def _load(self, data: list[Any]): self.clear() AbstractBaseDataModel.load(self, data) self.Cleared() diff --git a/locale/de_DE/LC_MESSAGES/petersql.mo b/locale/de_DE/LC_MESSAGES/petersql.mo deleted file mode 100644 index c6aade8cab74cb30c7b6a4392cd02e1c954d3d91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8416 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 diff --git a/locale/de_DE/LC_MESSAGES/petersql.po b/locale/de_DE/LC_MESSAGES/petersql.po index ae08f55..da6114d 100644 --- a/locale/de_DE/LC_MESSAGES/petersql.po +++ b/locale/de_DE/LC_MESSAGES/petersql.po @@ -1,761 +1,1048 @@ -# German translations for petersql. -# Copyright (C) 2024 -# This file is distributed under the same license as the petersql package. -# -msgid "" -msgstr "" -"Language: de\n" -"Content-Type: text/plain; charset=UTF-8\n" - -#: helpers/__init__.py:14 +#: helpers/__init__.py:16 msgctxt "unit" msgid "B" -msgstr "B" +msgstr "" -#: helpers/__init__.py:15 +#: helpers/__init__.py:17 msgctxt "unit" msgid "KB" -msgstr "KB" +msgstr "" -#: helpers/__init__.py:16 +#: helpers/__init__.py:18 msgctxt "unit" msgid "MB" -msgstr "MB" +msgstr "" -#: helpers/__init__.py:17 +#: helpers/__init__.py:19 msgctxt "unit" msgid "GB" -msgstr "GB" +msgstr "" -#: helpers/__init__.py:18 +#: helpers/__init__.py:20 msgctxt "unit" msgid "TB" -msgstr "TB" +msgstr "" -#: structures/ssh_tunnel.py:117 +#: structures/ssh_tunnel.py:166 msgid "OpenSSH client not found." -msgstr "OpenSSH-Client nicht gefunden." +msgstr "" -#: windows/__init__.py:31 windows/main/main_frame.py:224 +#: windows/dialogs/connections/view.py:395 +#: windows/dialogs/connections/view.py:738 windows/main/controller.py:296 +#: windows/views.py:33 msgid "Connection" -msgstr "Verbindung" - -#: windows/__init__.py:45 windows/__init__.py:83 windows/__init__.py:820 -#: windows/__init__.py:880 windows/__init__.py:1267 windows/__init__.py:1950 -#: windows/__init__.py:1973 windows/__init__.py:1974 windows/__init__.py:1975 -#: windows/__init__.py:1976 windows/__init__.py:1977 windows/__init__.py:1978 -#: windows/__init__.py:1979 windows/__init__.py:1980 windows/__init__.py:1981 -#: windows/__init__.py:1985 windows/__init__.py:2076 windows/__init__.py:2277 +msgstr "" + #: windows/components/dataview.py:113 windows/components/dataview.py:225 #: windows/components/dataview.py:238 windows/components/dataview.py:253 +#: windows/views.py:47 windows/views.py:97 windows/views.py:956 +#: windows/views.py:1306 windows/views.py:1413 windows/views.py:1796 +#: windows/views.py:2707 windows/views.py:2730 windows/views.py:2731 +#: windows/views.py:2732 windows/views.py:2733 windows/views.py:2734 +#: windows/views.py:2735 windows/views.py:2736 windows/views.py:2737 +#: windows/views.py:2738 windows/views.py:2742 windows/views.py:2936 +#: windows/views.py:3137 msgid "Name" -msgstr "Name" +msgstr "" -#: windows/__init__.py:46 windows/__init__.py:322 +#: windows/views.py:48 windows/views.py:381 msgid "Last connection" -msgstr "Letzte Verbindung" +msgstr "" -#: windows/__init__.py:59 windows/connections/manager.py:174 +#: windows/dialogs/connections/view.py:631 windows/views.py:61 msgid "New directory" -msgstr "Neues Verzeichnis" +msgstr "" -#: windows/__init__.py:62 -msgid "New Session" -msgstr "Neue Sitzung" +#: windows/dialogs/connections/model.py:187 +#: windows/dialogs/connections/view.py:591 windows/views.py:65 +msgid "New connection" +msgstr "" -#: windows/__init__.py:67 -msgid "Import" -msgstr "Importieren" +#: windows/views.py:71 +msgid "Rename" +msgstr "" -#: windows/__init__.py:97 windows/__init__.py:825 windows/__init__.py:935 -#: windows/__init__.py:1990 windows/__init__.py:2332 +#: windows/views.py:76 +msgid "Clone connection" +msgstr "" + +#: windows/views.py:81 windows/views.py:556 windows/views.py:1290 +#: windows/views.py:1331 windows/views.py:1706 windows/views.py:1738 +#: windows/views.py:1997 windows/views.py:3273 windows/views.py:3305 +msgid "Delete" +msgstr "" + +#: windows/views.py:111 windows/views.py:1311 windows/views.py:1468 +#: windows/views.py:2747 windows/views.py:3192 msgid "Engine" -msgstr "Engine" +msgstr "" -#: windows/__init__.py:118 +#: windows/views.py:132 msgid "Host + port" -msgstr "Host + Port" +msgstr "" -#: windows/__init__.py:134 +#: windows/views.py:148 msgid "Username" -msgstr "Benutzername" +msgstr "" -#: windows/__init__.py:147 +#: windows/views.py:161 windows/views.py:1100 msgid "Password" -msgstr "Passwort" +msgstr "" -#: windows/__init__.py:163 +#: windows/views.py:177 +msgid "Use TLS" +msgstr "" + +#: windows/views.py:180 msgid "Use SSH tunnel" -msgstr "SSH-Tunnel verwenden" +msgstr "" -#: windows/__init__.py:185 windows/__init__.py:1897 +#: windows/views.py:202 windows/views.py:2668 msgid "Filename" -msgstr "Dateiname" +msgstr "" -#: windows/__init__.py:190 windows/__init__.py:1902 +#: windows/views.py:207 windows/views.py:324 windows/views.py:2673 +#: windows/views.py:2856 msgid "Select a file" -msgstr "Datei auswählen" +msgstr "" -#: windows/__init__.py:190 -msgid "*. *" -msgstr "*. *" +#: windows/views.py:207 windows/views.py:324 windows/views.py:2856 +msgid "*.*" +msgstr "" -#: windows/__init__.py:204 windows/__init__.py:827 windows/__init__.py:893 -#: windows/__init__.py:1991 windows/__init__.py:2174 windows/__init__.py:2290 #: windows/components/dataview.py:70 windows/components/dataview.py:92 +#: windows/views.py:221 windows/views.py:1313 windows/views.py:1426 +#: windows/views.py:2748 windows/views.py:3034 windows/views.py:3150 msgid "Comments" -msgstr "Kommentare" +msgstr "" -#: windows/__init__.py:218 windows/__init__.py:520 +#: windows/main/controller.py:219 windows/views.py:235 windows/views.py:683 +#: windows/views.py:837 msgid "Settings" -msgstr "Einstellungen" +msgstr "" -#: windows/__init__.py:227 +#: windows/views.py:244 msgid "SSH executable" -msgstr "SSH-Executable" +msgstr "" -#: windows/__init__.py:232 +#: windows/views.py:249 msgid "ssh" -msgstr "ssh" +msgstr "" -#: windows/__init__.py:240 +#: windows/views.py:257 msgid "SSH host + port" -msgstr "SSH-Host + Port" +msgstr "" + +#: windows/views.py:269 +msgid "SSH host + port (the SSH server that forwards traffic to the DB)" +msgstr "" -#: windows/__init__.py:256 +#: windows/views.py:278 msgid "SSH username" -msgstr "SSH-Benutzername" +msgstr "" -#: windows/__init__.py:269 +#: windows/views.py:291 msgid "SSH password" -msgstr "SSH-Passwort" +msgstr "" -#: windows/__init__.py:282 +#: windows/views.py:304 msgid "Local port" -msgstr "Lokaler Port" +msgstr "" -#: windows/__init__.py:288 +#: windows/views.py:310 msgid "if the value is set to 0, the first available port will be used" -msgstr "wenn der Wert auf 0 gesetzt ist, wird der erste verfügbare Port verwendet" +msgstr "" + +#: windows/views.py:319 +msgid "Identity file" +msgstr "" + +#: windows/views.py:335 +msgid "Remote host + port" +msgstr "" -#: windows/__init__.py:299 +#: windows/views.py:347 +msgid "Remote host/port is the real DB target (defaults to DB Host/Port)." +msgstr "" + +#: windows/views.py:358 msgid "SSH Tunnel" -msgstr "SSH-Tunnel" +msgstr "" -#: windows/__init__.py:305 windows/__init__.py:823 windows/__init__.py:1988 +#: windows/views.py:364 windows/views.py:1309 windows/views.py:2745 msgid "Created at" -msgstr "Erstellt am" +msgstr "" -#: windows/__init__.py:339 +#: windows/views.py:398 msgid "Successful connections" -msgstr "Erfolgreiche Verbindungen" +msgstr "" + +#: windows/views.py:415 +msgid "Last successful connection" +msgstr "" -#: windows/__init__.py:356 +#: windows/views.py:432 msgid "Unsuccessful connections" -msgstr "Erfolglose Verbindungen" +msgstr "" + +#: windows/views.py:449 +msgid "Last failure reason" +msgstr "" + +#: windows/views.py:466 +msgid "Total connection attempts" +msgstr "" -#: windows/__init__.py:375 +#: windows/views.py:483 +msgid " Average connection time (ms)" +msgstr "" + +#: windows/views.py:500 +msgid " Most recent connection duration" +msgstr "" + +#: windows/views.py:519 msgid "Statistics" -msgstr "Statistiken" +msgstr "" -#: windows/__init__.py:393 windows/__init__.py:1139 +#: windows/views.py:537 windows/views.py:1672 msgid "Create" -msgstr "Erstellen" +msgstr "" -#: windows/__init__.py:403 windows/__init__.py:806 windows/__init__.py:1173 -#: windows/__init__.py:1205 windows/__init__.py:1340 windows/__init__.py:2413 -#: windows/__init__.py:2445 -msgid "Delete" -msgstr "Löschen" +#: windows/views.py:541 +msgid "Create connection" +msgstr "" + +#: windows/views.py:544 +msgid "Create directory" +msgstr "" -#: windows/__init__.py:420 windows/__init__.py:634 windows/__init__.py:1208 -#: windows/__init__.py:1343 windows/__init__.py:1419 windows/__init__.py:2221 -#: windows/__init__.py:2448 +#: windows/views.py:573 windows/views.py:797 windows/views.py:1328 +#: windows/views.py:1741 windows/views.py:2002 windows/views.py:2078 +#: windows/views.py:3081 windows/views.py:3308 msgid "Cancel" -msgstr "Abbrechen" +msgstr "" -#: windows/__init__.py:425 windows/__init__.py:1348 windows/__init__.py:2231 -#: windows/__init__.py:2453 +#: windows/views.py:578 windows/views.py:2007 windows/views.py:3091 +#: windows/views.py:3313 msgid "Save" -msgstr "Speichern" +msgstr "" -#: windows/__init__.py:432 +#: windows/views.py:585 msgid "Test" -msgstr "Testen" +msgstr "" -#: windows/__init__.py:439 +#: windows/views.py:592 msgid "Connect" -msgstr "Verbinden" +msgstr "" -#: windows/__init__.py:532 +#: windows/views.py:695 msgid "Language" -msgstr "Sprache" +msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "English" -msgstr "Englisch" +msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "Italian" -msgstr "Italienisch" +msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "French" -msgstr "Französisch" +msgstr "" -#: windows/__init__.py:549 +#: windows/views.py:712 msgid "Locale" -msgstr "Lokale" +msgstr "" -#: windows/__init__.py:570 +#: windows/views.py:733 msgid "Edit Value" -msgstr "Wert bearbeiten" +msgstr "" -#: windows/__init__.py:580 +#: windows/views.py:743 msgid "Syntax" -msgstr "Syntax" +msgstr "" -#: windows/__init__.py:637 +#: windows/views.py:800 msgid "Ok" -msgstr "Ok" +msgstr "" -#: windows/__init__.py:668 +#: windows/views.py:831 msgid "PeterSQL" -msgstr "PeterSQL" +msgstr "" -#: windows/__init__.py:674 +#: windows/views.py:840 msgid "File" -msgstr "Datei" +msgstr "" -#: windows/__init__.py:677 +#: windows/views.py:843 msgid "About" -msgstr "Über" +msgstr "" -#: windows/__init__.py:680 +#: windows/views.py:846 msgid "Help" -msgstr "Hilfe" +msgstr "" -#: windows/__init__.py:685 +#: windows/views.py:851 msgid "Open connection manager" -msgstr "Verbindungsmanager öffnen" +msgstr "" -#: windows/__init__.py:687 +#: windows/views.py:853 msgid "Disconnect from server" -msgstr "Vom Server trennen" +msgstr "" -#: windows/__init__.py:691 +#: windows/views.py:857 msgid "tool" -msgstr "Werkzeug" +msgstr "" -#: windows/__init__.py:691 +#: windows/views.py:857 msgid "Refresh" -msgstr "Aktualisieren" +msgstr "" -#: windows/__init__.py:695 windows/__init__.py:697 +#: windows/views.py:861 windows/views.py:863 msgid "Add" -msgstr "Hinzufügen" +msgstr "" -#: windows/__init__.py:731 windows/__init__.py:735 windows/__init__.py:1507 -#: windows/__init__.py:1603 +#: windows/views.py:897 windows/views.py:901 windows/views.py:2166 +#: windows/views.py:2654 msgid "MyMenuItem" -msgstr "MeinMenüElement" +msgstr "" -#: windows/__init__.py:738 windows/__init__.py:1236 windows/__init__.py:2476 +#: windows/views.py:904 windows/views.py:1769 windows/views.py:3336 msgid "MyMenu" -msgstr "MeinMenü" +msgstr "" -#: windows/__init__.py:753 +#: windows/views.py:919 windows/views.py:1350 windows/views.py:1357 +#: windows/views.py:1364 msgid "MyLabel" -msgstr "MeinLabel" +msgstr "" -#: windows/__init__.py:759 +#: windows/views.py:925 msgid "Databases" -msgstr "Datenbanken" +msgstr "" -#: windows/__init__.py:760 windows/__init__.py:822 windows/__init__.py:1959 -#: windows/__init__.py:1987 +#: windows/views.py:926 windows/views.py:1308 windows/views.py:2716 +#: windows/views.py:2744 msgid "Size" -msgstr "Größe" +msgstr "" -#: windows/__init__.py:761 +#: windows/views.py:927 msgid "Elements" -msgstr "Elemente" +msgstr "" -#: windows/__init__.py:762 +#: windows/views.py:928 msgid "Modified at" -msgstr "Geändert am" +msgstr "" -#: windows/__init__.py:763 windows/__init__.py:834 +#: windows/views.py:929 msgid "Tables" -msgstr "Tabellen" +msgstr "" -#: windows/__init__.py:770 +#: windows/views.py:936 msgid "System" -msgstr "System" +msgstr "" + +#: windows/views.py:979 +msgid "Character set" +msgstr "" + +#: windows/components/dataview.py:43 windows/components/dataview.py:67 +#: windows/components/dataview.py:89 windows/views.py:1000 +#: windows/views.py:1312 windows/views.py:2980 +msgid "Collation" +msgstr "" + +#: windows/views.py:1026 windows/views.py:2862 +msgid "Encryption" +msgstr "" + +#: windows/views.py:1038 +msgid "Read Only" +msgstr "" -#: windows/__init__.py:786 +#: windows/views.py:1055 +msgid "Tablespace" +msgstr "" + +#: windows/views.py:1076 +msgid "Connection limit" +msgstr "" + +#: windows/views.py:1119 +msgid "Profile" +msgstr "" + +#: windows/views.py:1145 +msgid "Default tablespace" +msgstr "" + +#: windows/views.py:1166 +msgid "Temporary tablespace" +msgstr "" + +#: windows/views.py:1192 +msgid "Quota" +msgstr "" + +#: windows/views.py:1211 +msgid "Unlimited quota" +msgstr "" + +#: windows/views.py:1228 +msgid "Account status" +msgstr "" + +#: windows/views.py:1249 +msgid "Password expire" +msgstr "" + +#: windows/views.py:1270 msgid "Table:" -msgstr "Tabelle:" +msgstr "" -#: windows/__init__.py:794 windows/__init__.py:1018 windows/__init__.py:1062 -#: windows/__init__.py:1168 windows/__init__.py:2408 +#: windows/views.py:1278 windows/views.py:1551 windows/views.py:1595 +#: windows/views.py:1701 windows/views.py:3268 msgid "Insert" -msgstr "Einfügen" +msgstr "" -#: windows/__init__.py:799 +#: windows/views.py:1283 msgid "Clone" -msgstr "Klonen" +msgstr "" -#: windows/__init__.py:821 +#: windows/views.py:1307 msgid "Rows" -msgstr "Zeilen" +msgstr "" -#: windows/__init__.py:824 windows/__init__.py:1989 +#: windows/views.py:1310 windows/views.py:2746 msgid "Updated at" -msgstr "Aktualisiert am" +msgstr "" -#: windows/__init__.py:826 windows/__init__.py:2120 -#: windows/components/dataview.py:43 windows/components/dataview.py:67 -#: windows/components/dataview.py:89 -msgid "Collation" -msgstr "Sortierung" +#: windows/views.py:1334 windows/views.py:1746 windows/views.py:2085 +#: windows/views.py:2140 +msgid "Apply" +msgstr "" + +#: windows/views.py:1344 windows/views.py:1503 windows/views.py:1956 +#: windows/views.py:3225 +msgid "Options" +msgstr "" -#: windows/__init__.py:842 +#: windows/views.py:1375 msgid "Diagram" -msgstr "Diagramm" +msgstr "" -#: windows/__init__.py:853 windows/__init__.py:1958 +#: windows/views.py:1386 windows/views.py:2715 msgid "Database" -msgstr "Datenbank" +msgstr "" -#: windows/__init__.py:908 windows/__init__.py:2305 +#: windows/views.py:1441 windows/views.py:3165 msgid "Base" -msgstr "Basis" +msgstr "" -#: windows/__init__.py:922 windows/__init__.py:2319 +#: windows/views.py:1455 windows/views.py:3179 msgid "Auto Increment" -msgstr "Auto Inkrement" +msgstr "" -#: windows/__init__.py:950 windows/__init__.py:2347 +#: windows/views.py:1483 windows/views.py:3207 msgid "Default Collation" -msgstr "Standard-Sortierung" - -#: windows/__init__.py:970 windows/__init__.py:1301 windows/__init__.py:2365 -msgid "Options" -msgstr "Optionen" +msgstr "" -#: windows/__init__.py:982 windows/__init__.py:1023 windows/__init__.py:1067 +#: windows/views.py:1515 windows/views.py:1556 windows/views.py:1600 msgid "Remove" -msgstr "Entfernen" +msgstr "" -#: windows/__init__.py:989 windows/__init__.py:1030 windows/__init__.py:1074 +#: windows/views.py:1522 windows/views.py:1563 windows/views.py:1607 msgid "Clear" -msgstr "Löschen" +msgstr "" -#: windows/__init__.py:1004 windows/__init__.py:2379 +#: windows/views.py:1537 windows/views.py:3239 msgid "Indexes" -msgstr "Indizes" +msgstr "" -#: windows/__init__.py:1048 +#: windows/views.py:1581 msgid "Foreign Keys" -msgstr "Fremdschlüssel" +msgstr "" -#: windows/__init__.py:1092 +#: windows/views.py:1625 msgid "Checks" -msgstr "Prüfungen" +msgstr "" -#: windows/__init__.py:1160 windows/__init__.py:2400 +#: windows/views.py:1693 windows/views.py:3260 msgid "Columns:" -msgstr "Spalten:" +msgstr "" -#: windows/__init__.py:1180 windows/__init__.py:2420 +#: windows/views.py:1713 windows/views.py:3280 msgid "Up" -msgstr "Hoch" +msgstr "" -#: windows/__init__.py:1187 windows/__init__.py:2427 +#: windows/views.py:1720 windows/views.py:3287 msgid "Down" -msgstr "Runter" - -#: windows/__init__.py:1213 windows/__init__.py:1426 windows/__init__.py:1481 -msgid "Apply" -msgstr "Anwenden" +msgstr "" -#: windows/__init__.py:1226 windows/__init__.py:1233 windows/__init__.py:2466 -#: windows/__init__.py:2473 +#: windows/views.py:1759 windows/views.py:1766 windows/views.py:3326 +#: windows/views.py:3333 msgid "Add Index" -msgstr "Index hinzufügen" +msgstr "" -#: windows/__init__.py:1230 windows/__init__.py:2470 +#: windows/views.py:1763 windows/views.py:3330 msgid "Add PrimaryKey" -msgstr "Primärschlüssel hinzufügen" +msgstr "" -#: windows/__init__.py:1247 +#: windows/views.py:1780 msgid "Table" -msgstr "Tabelle" +msgstr "" -#: windows/__init__.py:1280 -msgid "Temporary" -msgstr "Temporär" +#: windows/views.py:1816 +msgid "Definer" +msgstr "" + +#: windows/views.py:1836 +msgid "Schema" +msgstr "" + +#: windows/views.py:1862 +msgid "SQL security" +msgstr "" + +#: windows/views.py:1869 +msgid "DEFINER" +msgstr "" + +#: windows/views.py:1869 +msgid "INVOKER" +msgstr "" + +#: windows/views.py:1881 windows/views.py:2558 windows/views.py:2577 +#: windows/views.py:2820 +msgid "Algorithm" +msgstr "" -#: windows/__init__.py:1360 +#: windows/views.py:1883 windows/views.py:2543 windows/views.py:2576 +#: windows/views.py:2825 +msgid "UNDEFINED" +msgstr "" + +#: windows/views.py:1886 windows/views.py:2546 windows/views.py:2576 +#: windows/views.py:2828 +msgid "MERGE" +msgstr "" + +#: windows/views.py:1889 windows/views.py:2555 windows/views.py:2576 +#: windows/views.py:2831 +msgid "TEMPTABLE" +msgstr "" + +#: windows/views.py:1899 windows/views.py:2582 +msgid "View constraint" +msgstr "" + +#: windows/views.py:1901 windows/views.py:2581 +msgid "None" +msgstr "" + +#: windows/views.py:1904 windows/views.py:2581 +msgid "LOCAL" +msgstr "" + +#: windows/views.py:1907 +msgid "CASCADE" +msgstr "" + +#: windows/views.py:1910 +msgid "CHECK ONLY" +msgstr "" + +#: windows/views.py:1913 windows/views.py:2581 +msgid "READ ONLY" +msgstr "" + +#: windows/views.py:1925 +msgid "Force" +msgstr "" + +#: windows/views.py:1937 +msgid "Security barrier" +msgstr "" + +#: windows/views.py:2019 msgid "Views" -msgstr "Ansichten" +msgstr "" -#: windows/__init__.py:1368 +#: windows/views.py:2027 msgid "Triggers" -msgstr "Trigger" +msgstr "" -#: windows/__init__.py:1380 +#: windows/views.py:2039 #, python-format msgid "Table `%(database_name)s`.`%(table_name)s`: %(total_rows) rows total" -msgstr "Tabelle `%(database_name)s`.`%(table_name)s`: %(total_rows) Zeilen insgesamt" +msgstr "" -#: windows/__init__.py:1390 +#: windows/views.py:2049 msgid "Insert record" -msgstr "Datensatz einfügen" +msgstr "" -#: windows/__init__.py:1395 +#: windows/views.py:2054 msgid "Duplicate record" -msgstr "Datensatz duplizieren" +msgstr "" -#: windows/__init__.py:1402 +#: windows/views.py:2061 msgid "Delete record" -msgstr "Datensatz löschen" +msgstr "" -#: windows/__init__.py:1412 +#: windows/views.py:2071 msgid "Apply changes automatically" -msgstr "Änderungen automatisch anwenden" +msgstr "" -#: windows/__init__.py:1414 windows/__init__.py:1415 +#: windows/views.py:2073 windows/views.py:2074 msgid "" "If enabled, table edits are applied immediately without pressing Apply or" " Cancel" msgstr "" -"Wenn aktiviert, werden Tabellenbearbeitungen sofort angewendet, ohne auf Anwenden oder Abbrechen zu drücken" -"" -#: windows/__init__.py:1436 +#: windows/views.py:2095 msgid "Next" -msgstr "Weiter" +msgstr "" -#: windows/__init__.py:1444 +#: windows/views.py:2103 msgid "Filters" -msgstr "Filter" +msgstr "" -#: windows/__init__.py:1484 +#: windows/views.py:2143 msgid "CTRL+ENTER" -msgstr "CTRL+ENTER" +msgstr "" -#: windows/__init__.py:1504 +#: windows/views.py:2163 msgid "Insert row" -msgstr "Zeile einfügen" +msgstr "" -#: windows/__init__.py:1512 +#: windows/views.py:2171 msgid "Data" -msgstr "Daten" +msgstr "" -#: windows/__init__.py:1561 +#: windows/views.py:2225 windows/views.py:2275 +msgid "New" +msgstr "" + +#: windows/views.py:2252 msgid "Query" -msgstr "Abfrage" +msgstr "" -#: windows/__init__.py:1581 +#: windows/views.py:2272 msgid "Close" -msgstr "Schließen" - -#: windows/__init__.py:1584 windows/__init__.py:2019 -msgid "New" -msgstr "Neu" +msgstr "" -#: windows/__init__.py:1594 +#: windows/views.py:2285 msgid "Query #2" -msgstr "Abfrage #2" +msgstr "" -#: windows/__init__.py:1835 +#: windows/views.py:2537 msgid "Column5" -msgstr "Spalte5" +msgstr "" + +#: windows/views.py:2548 +msgid "Import" +msgstr "" -#: windows/__init__.py:1842 +#: windows/views.py:2573 +msgid "Read only" +msgstr "" + +#: windows/views.py:2581 +msgid "CASCADED" +msgstr "" + +#: windows/views.py:2581 +msgid "CHECK OPTION" +msgstr "" + +#: windows/views.py:2613 msgid "collapsible" -msgstr "zusammenklappbar" +msgstr "" -#: windows/__init__.py:1864 +#: windows/views.py:2629 msgid "Column3" -msgstr "Spalte3" +msgstr "" -#: windows/__init__.py:1865 +#: windows/views.py:2630 msgid "Column4" -msgstr "Spalte4" +msgstr "" -#: windows/__init__.py:1902 +#: windows/views.py:2673 msgid "" "Database " "(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" msgstr "" -"Database " -"(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" -#: windows/__init__.py:1934 +#: windows/views.py:2685 msgid "Port" -msgstr "Port" +msgstr "" -#: windows/__init__.py:1951 +#: windows/views.py:2708 msgid "Usage" -msgstr "Verwendung" +msgstr "" -#: windows/__init__.py:1962 +#: windows/views.py:2719 #, python-format msgid "%(total_rows)s" -msgstr "%(total_rows)s" +msgstr "" -#: windows/__init__.py:1967 +#: windows/views.py:2724 msgid "rows total" -msgstr "Zeilen insgesamt" +msgstr "" -#: windows/__init__.py:1986 +#: windows/views.py:2743 msgid "Lines" -msgstr "Zeilen" +msgstr "" + +#: windows/views.py:2775 +msgid "Temporary" +msgstr "" + +#: windows/views.py:2786 +msgid "Engine options" +msgstr "" -#: windows/__init__.py:2068 +#: windows/views.py:2845 +msgid "RadioBtn" +msgstr "" + +#: windows/views.py:2928 msgid "Edit Column" -msgstr "Spalte bearbeiten" +msgstr "" -#: windows/__init__.py:2084 +#: windows/views.py:2944 msgid "Datatype" -msgstr "Datentyp" +msgstr "" -#: windows/__init__.py:2099 windows/components/dataview.py:121 +#: windows/components/dataview.py:121 windows/views.py:2959 msgid "Length/Set" -msgstr "Länge/Menge" +msgstr "" -#: windows/__init__.py:2138 windows/components/dataview.py:51 +#: windows/components/dataview.py:51 windows/views.py:2998 msgid "Unsigned" -msgstr "Ohne Vorzeichen" +msgstr "" -#: windows/__init__.py:2144 windows/components/dataview.py:25 -#: windows/components/dataview.py:52 windows/components/dataview.py:75 +#: windows/components/dataview.py:25 windows/components/dataview.py:52 +#: windows/components/dataview.py:75 windows/views.py:3004 msgid "Allow NULL" -msgstr "NULL erlauben" +msgstr "" -#: windows/__init__.py:2150 +#: windows/views.py:3010 msgid "Zero Fill" -msgstr "Nullfüllung" +msgstr "" -#: windows/__init__.py:2161 windows/components/dataview.py:32 -#: windows/components/dataview.py:56 windows/components/dataview.py:78 +#: windows/components/dataview.py:32 windows/components/dataview.py:56 +#: windows/components/dataview.py:78 windows/views.py:3021 msgid "Default" -msgstr "Standard" +msgstr "" -#: windows/__init__.py:2187 windows/components/dataview.py:36 -#: windows/components/dataview.py:60 windows/components/dataview.py:82 +#: windows/components/dataview.py:36 windows/components/dataview.py:60 +#: windows/components/dataview.py:82 windows/views.py:3047 msgid "Virtuality" -msgstr "Virtualität" +msgstr "" -#: windows/__init__.py:2202 windows/components/dataview.py:39 -#: windows/components/dataview.py:63 windows/components/dataview.py:85 -#: windows/components/dataview.py:241 +#: windows/components/dataview.py:39 windows/components/dataview.py:63 +#: windows/components/dataview.py:85 windows/components/dataview.py:241 +#: windows/views.py:3062 msgid "Expression" -msgstr "Ausdruck" +msgstr "" #: windows/components/dataview.py:28 msgid "Check" -msgstr "Prüfen" +msgstr "" #: windows/components/dataview.py:53 msgid "Zerofill" -msgstr "Nullfüllung" +msgstr "" #: windows/components/dataview.py:109 msgid "#" -msgstr "#" +msgstr "" #: windows/components/dataview.py:117 msgid "Data type" -msgstr "Datentyp" +msgstr "" #: windows/components/dataview.py:155 msgid "Add column\tCTRL+INS" -msgstr "Spalte hinzufügen\tCTRL+INS" +msgstr "" #: windows/components/dataview.py:161 msgid "Remove column\tCTRL+DEL" -msgstr "Spalte entfernen\tCTRL+DEL" +msgstr "" #: windows/components/dataview.py:169 msgid "Move up\tCTRL+UP" -msgstr "Nach oben bewegen\tCTRL+UP" +msgstr "" #: windows/components/dataview.py:176 msgid "Move down\tCTRL+D" -msgstr "Nach unten bewegen\tCTRL+D" +msgstr "" #: windows/components/dataview.py:199 msgid "Create new index" -msgstr "Neuen Index erstellen" +msgstr "" #: windows/components/dataview.py:214 msgid "Append to index" -msgstr "An Index anhängen" +msgstr "" #: windows/components/dataview.py:228 msgid "Column(s)/Expression" -msgstr "Spalte(n)/Ausdruck" +msgstr "" #: windows/components/dataview.py:229 msgid "Condition" -msgstr "Bedingung" +msgstr "" #: windows/components/dataview.py:259 msgid "Column(s)" -msgstr "Spalte(n)" +msgstr "" #: windows/components/dataview.py:265 msgid "Reference table" -msgstr "Referenztabelle" +msgstr "" #: windows/components/dataview.py:271 msgid "Reference column(s)" -msgstr "Referenzspalte(n)" +msgstr "" #: windows/components/dataview.py:277 msgid "On UPDATE" -msgstr "Bei UPDATE" +msgstr "" #: windows/components/dataview.py:283 msgid "On DELETE" -msgstr "Bei DELETE" +msgstr "" #: windows/components/dataview.py:299 msgid "Add foreign key" -msgstr "Fremdschlüssel hinzufügen" +msgstr "" #: windows/components/dataview.py:305 msgid "Remove foreign key" -msgstr "Fremdschlüssel entfernen" +msgstr "" -#: windows/components/popup.py:24 +#: windows/components/popup.py:26 msgid "No default value" -msgstr "Kein Standardwert" +msgstr "" -#: windows/components/popup.py:29 +#: windows/components/popup.py:31 msgid "NULL" -msgstr "NULL" +msgstr "" -#: windows/components/popup.py:33 +#: windows/components/popup.py:35 msgid "AUTO INCREMENT" -msgstr "AUTO INCREMENT" +msgstr "" -#: windows/components/popup.py:37 +#: windows/components/popup.py:39 msgid "Text/Expression" -msgstr "Text/Ausdruck" - -#: windows/connections/manager.py:125 -msgid "Confirm save" -msgstr "Speichern bestätigen" - -#: windows/connections/manager.py:193 -msgid "Connection error" -msgstr "Verbindungsfehler" - -#: windows/connections/manager.py:214 -msgid "connection" -msgstr "Verbindung" +msgstr "" -#: windows/connections/manager.py:215 windows/connections/manager.py:230 -msgid "Confirm delete" -msgstr "Löschen bestätigen" +#: windows/dialogs/connections/view.py:119 windows/main/tabs/query.py:376 +msgid "Unknown error" +msgstr "" -#: windows/connections/manager.py:229 -msgid "directory" -msgstr "Verzeichnis" +#: windows/dialogs/connections/view.py:394 +msgid "Connection established successfully" +msgstr "" -#: windows/connections/model.py:101 -msgid "New connection" -msgstr "Neue Verbindung" +#: windows/dialogs/connections/view.py:407 +msgid "Confirm save" +msgstr "" -#: windows/main/database.py:70 -msgid "The connection to the database was lost." -msgstr "Die Verbindung zur Datenbank wurde verloren." +#: windows/dialogs/connections/view.py:459 +msgid "You have unsaved changes. Do you want to save them before continuing?" +msgstr "" -#: windows/main/database.py:72 -msgid "Do you want to reconnect?" -msgstr "Möchten Sie erneut verbinden?" +#: windows/dialogs/connections/view.py:461 +msgid "Unsaved changes" +msgstr "" -#: windows/main/database.py:74 -msgid "Connection lost" -msgstr "Verbindung verloren" +#: windows/dialogs/connections/view.py:736 +msgid "" +"This connection cannot work without TLS. TLS has been enabled " +"automatically." +msgstr "" -#: windows/main/database.py:83 -msgid "Reconnection failed:" -msgstr "Wiederverbindung fehlgeschlagen:" +#: windows/dialogs/connections/view.py:762 +msgid "Connection error" +msgstr "" -#: windows/main/database.py:84 -msgid "Error" -msgstr "Fehler" +#: windows/dialogs/connections/view.py:788 +#: windows/dialogs/connections/view.py:803 +msgid "Confirm delete" +msgstr "" -#: windows/main/main_frame.py:124 +#: windows/main/controller.py:172 msgid "days" -msgstr "Tage" +msgstr "" -#: windows/main/main_frame.py:125 +#: windows/main/controller.py:173 msgid "hours" -msgstr "Stunden" +msgstr "" -#: windows/main/main_frame.py:126 +#: windows/main/controller.py:174 msgid "minutes" -msgstr "Minuten" +msgstr "" -#: windows/main/main_frame.py:127 +#: windows/main/controller.py:175 msgid "seconds" -msgstr "Sekunden" +msgstr "" -#: windows/main/main_frame.py:135 +#: windows/main/controller.py:183 #, python-brace-format msgid "Memory used: {used} ({percentage:.2%})" -msgstr "Verwendeter Speicher: {used} ({percentage:.2%})" +msgstr "" + +#: windows/main/controller.py:219 +msgid "Settings saved successfully" +msgstr "" -#: windows/main/main_frame.py:226 +#: windows/main/controller.py:298 msgid "Version" -msgstr "Version" +msgstr "" -#: windows/main/main_frame.py:228 +#: windows/main/controller.py:300 msgid "Uptime" -msgstr "Betriebszeit" +msgstr "" + +#: windows/main/controller.py:399 +#, python-brace-format +msgid "" +"Do you want to create a dump before dropping database '{database_name}'?\n" +"\n" +"Dump is not implemented yet.\n" +"- Yes: open dump flow (coming soon, no drop).\n" +"- No: drop the database now." +msgstr "" -#: windows/main/main_frame.py:431 +#: windows/main/controller.py:404 windows/main/controller.py:425 +msgid "Delete database" +msgstr "" + +#: windows/main/controller.py:410 +msgid "Dump is not implemented yet. No action has been performed." +msgstr "" + +#: windows/main/controller.py:411 +msgid "Dump not available" +msgstr "" + +#: windows/main/controller.py:424 +msgid "Database deletion is not supported by this engine." +msgstr "" + +#: windows/main/controller.py:439 +msgid "Database deleted successfully" +msgstr "" + +#: windows/main/controller.py:440 windows/main/tabs/view.py:253 +#: windows/main/tabs/view.py:279 +msgid "Success" +msgstr "" + +#: windows/main/controller.py:582 msgid "Delete table" -msgstr "Tabelle löschen" +msgstr "" -#: windows/main/main_frame.py:586 +#: windows/main/controller.py:699 msgid "Do you want delete the records?" -msgstr "Möchten Sie die Datensätze löschen?" +msgstr "" -#~ msgid "Created at:" -#~ msgstr "" +#: windows/main/tabs/database.py:71 +msgid "The connection to the database was lost." +msgstr "" + +#: windows/main/tabs/database.py:73 +msgid "Do you want to reconnect?" +msgstr "" + +#: windows/main/tabs/database.py:75 +msgid "Connection lost" +msgstr "" -#~ msgid "Last connection:" -#~ msgstr "" +#: windows/main/tabs/database.py:85 +msgid "Reconnection failed:" +msgstr "" -#~ msgid "Successful connections:" -#~ msgstr "" +#: windows/main/tabs/database.py:86 windows/main/tabs/query.py:450 +#: windows/main/tabs/view.py:256 windows/main/tabs/view.py:282 +msgid "Error" +msgstr "" -#~ msgid "Unsuccessful connections:" -#~ msgstr "" +#: windows/main/tabs/query.py:305 +#, python-brace-format +msgid "{} rows affected" +msgstr "" -#~ msgid "Session Manager" -#~ msgstr "" +#: windows/main/tabs/query.py:309 windows/main/tabs/query.py:331 +#, python-brace-format +msgid "Query {}" +msgstr "" -#~ msgid "Session name" -#~ msgstr "" +#: windows/main/tabs/query.py:314 +#, python-brace-format +msgid "Query {} (Error)" +msgstr "" -#~ msgid "Connection type" -#~ msgstr "" +#: windows/main/tabs/query.py:326 +#, python-brace-format +msgid "Query {} ({} rows × {} cols)" +msgstr "" -#~ msgid "Open" -#~ msgstr "" +#: windows/main/tabs/query.py:353 +#, python-brace-format +msgid "{} rows" +msgstr "" -#~ msgid "Open session manager" -#~ msgstr "" +#: windows/main/tabs/query.py:355 +#, python-brace-format +msgid "{:.1f} ms" +msgstr "" -#~ msgid "Foreign Key" -#~ msgstr "" +#: windows/main/tabs/query.py:358 +#, python-brace-format +msgid "{} warnings" +msgstr "" + +#: windows/main/tabs/query.py:370 +msgid "Error:" +msgstr "" + +#: windows/main/tabs/query.py:449 +msgid "No active database connection" +msgstr "" + +#: windows/main/tabs/view.py:252 +msgid "View created successfully" +msgstr "" + +#: windows/main/tabs/view.py:252 +msgid "View updated successfully" +msgstr "" + +#: windows/main/tabs/view.py:256 +#, python-brace-format +msgid "Error saving view: {}" +msgstr "" + +#: windows/main/tabs/view.py:269 +#, python-brace-format +msgid "Are you sure you want to delete view '{}'?" +msgstr "" + +#: windows/main/tabs/view.py:270 +msgid "Confirm Delete" +msgstr "" + +#: windows/main/tabs/view.py:279 +msgid "View deleted successfully" +msgstr "" + +#: windows/main/tabs/view.py:282 +#, python-brace-format +msgid "Error deleting view: {}" +msgstr "" diff --git a/locale/en_US/LC_MESSAGES/petersql.po b/locale/en_US/LC_MESSAGES/petersql.po index c910513..da6114d 100644 --- a/locale/en_US/LC_MESSAGES/petersql.po +++ b/locale/en_US/LC_MESSAGES/petersql.po @@ -1,545 +1,750 @@ -#: helpers/__init__.py:14 +#: helpers/__init__.py:16 msgctxt "unit" msgid "B" msgstr "" -#: helpers/__init__.py:15 +#: helpers/__init__.py:17 msgctxt "unit" msgid "KB" msgstr "" -#: helpers/__init__.py:16 +#: helpers/__init__.py:18 msgctxt "unit" msgid "MB" msgstr "" -#: helpers/__init__.py:17 +#: helpers/__init__.py:19 msgctxt "unit" msgid "GB" msgstr "" -#: helpers/__init__.py:18 +#: helpers/__init__.py:20 msgctxt "unit" msgid "TB" msgstr "" -#: structures/ssh_tunnel.py:117 +#: structures/ssh_tunnel.py:166 msgid "OpenSSH client not found." msgstr "" -#: windows/__init__.py:31 windows/main/main_frame.py:224 +#: windows/dialogs/connections/view.py:395 +#: windows/dialogs/connections/view.py:738 windows/main/controller.py:296 +#: windows/views.py:33 msgid "Connection" msgstr "" -#: windows/__init__.py:45 windows/__init__.py:83 windows/__init__.py:820 -#: windows/__init__.py:880 windows/__init__.py:1267 windows/__init__.py:1950 -#: windows/__init__.py:1973 windows/__init__.py:1974 windows/__init__.py:1975 -#: windows/__init__.py:1976 windows/__init__.py:1977 windows/__init__.py:1978 -#: windows/__init__.py:1979 windows/__init__.py:1980 windows/__init__.py:1981 -#: windows/__init__.py:1985 windows/__init__.py:2076 windows/__init__.py:2277 #: windows/components/dataview.py:113 windows/components/dataview.py:225 #: windows/components/dataview.py:238 windows/components/dataview.py:253 +#: windows/views.py:47 windows/views.py:97 windows/views.py:956 +#: windows/views.py:1306 windows/views.py:1413 windows/views.py:1796 +#: windows/views.py:2707 windows/views.py:2730 windows/views.py:2731 +#: windows/views.py:2732 windows/views.py:2733 windows/views.py:2734 +#: windows/views.py:2735 windows/views.py:2736 windows/views.py:2737 +#: windows/views.py:2738 windows/views.py:2742 windows/views.py:2936 +#: windows/views.py:3137 msgid "Name" msgstr "" -#: windows/__init__.py:46 windows/__init__.py:322 +#: windows/views.py:48 windows/views.py:381 msgid "Last connection" msgstr "" -#: windows/__init__.py:59 windows/connections/manager.py:174 +#: windows/dialogs/connections/view.py:631 windows/views.py:61 msgid "New directory" msgstr "" -#: windows/__init__.py:62 -msgid "New Session" +#: windows/dialogs/connections/model.py:187 +#: windows/dialogs/connections/view.py:591 windows/views.py:65 +msgid "New connection" +msgstr "" + +#: windows/views.py:71 +msgid "Rename" msgstr "" -#: windows/__init__.py:67 -msgid "Import" +#: windows/views.py:76 +msgid "Clone connection" msgstr "" -#: windows/__init__.py:97 windows/__init__.py:825 windows/__init__.py:935 -#: windows/__init__.py:1990 windows/__init__.py:2332 +#: windows/views.py:81 windows/views.py:556 windows/views.py:1290 +#: windows/views.py:1331 windows/views.py:1706 windows/views.py:1738 +#: windows/views.py:1997 windows/views.py:3273 windows/views.py:3305 +msgid "Delete" +msgstr "" + +#: windows/views.py:111 windows/views.py:1311 windows/views.py:1468 +#: windows/views.py:2747 windows/views.py:3192 msgid "Engine" msgstr "" -#: windows/__init__.py:118 +#: windows/views.py:132 msgid "Host + port" msgstr "" -#: windows/__init__.py:134 +#: windows/views.py:148 msgid "Username" msgstr "" -#: windows/__init__.py:147 +#: windows/views.py:161 windows/views.py:1100 msgid "Password" msgstr "" -#: windows/__init__.py:163 +#: windows/views.py:177 +msgid "Use TLS" +msgstr "" + +#: windows/views.py:180 msgid "Use SSH tunnel" msgstr "" -#: windows/__init__.py:185 windows/__init__.py:1897 +#: windows/views.py:202 windows/views.py:2668 msgid "Filename" msgstr "" -#: windows/__init__.py:190 windows/__init__.py:1902 +#: windows/views.py:207 windows/views.py:324 windows/views.py:2673 +#: windows/views.py:2856 msgid "Select a file" msgstr "" -#: windows/__init__.py:190 +#: windows/views.py:207 windows/views.py:324 windows/views.py:2856 msgid "*.*" msgstr "" -#: windows/__init__.py:204 windows/__init__.py:827 windows/__init__.py:893 -#: windows/__init__.py:1991 windows/__init__.py:2174 windows/__init__.py:2290 #: windows/components/dataview.py:70 windows/components/dataview.py:92 +#: windows/views.py:221 windows/views.py:1313 windows/views.py:1426 +#: windows/views.py:2748 windows/views.py:3034 windows/views.py:3150 msgid "Comments" msgstr "" -#: windows/__init__.py:218 windows/__init__.py:520 +#: windows/main/controller.py:219 windows/views.py:235 windows/views.py:683 +#: windows/views.py:837 msgid "Settings" msgstr "" -#: windows/__init__.py:227 +#: windows/views.py:244 msgid "SSH executable" msgstr "" -#: windows/__init__.py:232 +#: windows/views.py:249 msgid "ssh" msgstr "" -#: windows/__init__.py:240 +#: windows/views.py:257 msgid "SSH host + port" msgstr "" -#: windows/__init__.py:256 +#: windows/views.py:269 +msgid "SSH host + port (the SSH server that forwards traffic to the DB)" +msgstr "" + +#: windows/views.py:278 msgid "SSH username" msgstr "" -#: windows/__init__.py:269 +#: windows/views.py:291 msgid "SSH password" msgstr "" -#: windows/__init__.py:282 +#: windows/views.py:304 msgid "Local port" msgstr "" -#: windows/__init__.py:288 +#: windows/views.py:310 msgid "if the value is set to 0, the first available port will be used" msgstr "" -#: windows/__init__.py:299 +#: windows/views.py:319 +msgid "Identity file" +msgstr "" + +#: windows/views.py:335 +msgid "Remote host + port" +msgstr "" + +#: windows/views.py:347 +msgid "Remote host/port is the real DB target (defaults to DB Host/Port)." +msgstr "" + +#: windows/views.py:358 msgid "SSH Tunnel" msgstr "" -#: windows/__init__.py:305 windows/__init__.py:823 windows/__init__.py:1988 +#: windows/views.py:364 windows/views.py:1309 windows/views.py:2745 msgid "Created at" msgstr "" -#: windows/__init__.py:339 +#: windows/views.py:398 msgid "Successful connections" msgstr "" -#: windows/__init__.py:356 +#: windows/views.py:415 +msgid "Last successful connection" +msgstr "" + +#: windows/views.py:432 msgid "Unsuccessful connections" msgstr "" -#: windows/__init__.py:375 +#: windows/views.py:449 +msgid "Last failure reason" +msgstr "" + +#: windows/views.py:466 +msgid "Total connection attempts" +msgstr "" + +#: windows/views.py:483 +msgid " Average connection time (ms)" +msgstr "" + +#: windows/views.py:500 +msgid " Most recent connection duration" +msgstr "" + +#: windows/views.py:519 msgid "Statistics" msgstr "" -#: windows/__init__.py:393 windows/__init__.py:1139 +#: windows/views.py:537 windows/views.py:1672 msgid "Create" msgstr "" -#: windows/__init__.py:403 windows/__init__.py:806 windows/__init__.py:1173 -#: windows/__init__.py:1205 windows/__init__.py:1340 windows/__init__.py:2413 -#: windows/__init__.py:2445 -msgid "Delete" +#: windows/views.py:541 +msgid "Create connection" msgstr "" -#: windows/__init__.py:420 windows/__init__.py:634 windows/__init__.py:1208 -#: windows/__init__.py:1343 windows/__init__.py:1419 windows/__init__.py:2221 -#: windows/__init__.py:2448 +#: windows/views.py:544 +msgid "Create directory" +msgstr "" + +#: windows/views.py:573 windows/views.py:797 windows/views.py:1328 +#: windows/views.py:1741 windows/views.py:2002 windows/views.py:2078 +#: windows/views.py:3081 windows/views.py:3308 msgid "Cancel" msgstr "" -#: windows/__init__.py:425 windows/__init__.py:1348 windows/__init__.py:2231 -#: windows/__init__.py:2453 +#: windows/views.py:578 windows/views.py:2007 windows/views.py:3091 +#: windows/views.py:3313 msgid "Save" msgstr "" -#: windows/__init__.py:432 +#: windows/views.py:585 msgid "Test" msgstr "" -#: windows/__init__.py:439 +#: windows/views.py:592 msgid "Connect" msgstr "" -#: windows/__init__.py:532 +#: windows/views.py:695 msgid "Language" msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "English" msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "Italian" msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "French" msgstr "" -#: windows/__init__.py:549 +#: windows/views.py:712 msgid "Locale" msgstr "" -#: windows/__init__.py:570 +#: windows/views.py:733 msgid "Edit Value" msgstr "" -#: windows/__init__.py:580 +#: windows/views.py:743 msgid "Syntax" msgstr "" -#: windows/__init__.py:637 +#: windows/views.py:800 msgid "Ok" msgstr "" -#: windows/__init__.py:668 +#: windows/views.py:831 msgid "PeterSQL" msgstr "" -#: windows/__init__.py:674 +#: windows/views.py:840 msgid "File" msgstr "" -#: windows/__init__.py:677 +#: windows/views.py:843 msgid "About" msgstr "" -#: windows/__init__.py:680 +#: windows/views.py:846 msgid "Help" msgstr "" -#: windows/__init__.py:685 +#: windows/views.py:851 msgid "Open connection manager" msgstr "" -#: windows/__init__.py:687 +#: windows/views.py:853 msgid "Disconnect from server" msgstr "" -#: windows/__init__.py:691 +#: windows/views.py:857 msgid "tool" msgstr "" -#: windows/__init__.py:691 +#: windows/views.py:857 msgid "Refresh" msgstr "" -#: windows/__init__.py:695 windows/__init__.py:697 +#: windows/views.py:861 windows/views.py:863 msgid "Add" msgstr "" -#: windows/__init__.py:731 windows/__init__.py:735 windows/__init__.py:1507 -#: windows/__init__.py:1603 +#: windows/views.py:897 windows/views.py:901 windows/views.py:2166 +#: windows/views.py:2654 msgid "MyMenuItem" msgstr "" -#: windows/__init__.py:738 windows/__init__.py:1236 windows/__init__.py:2476 +#: windows/views.py:904 windows/views.py:1769 windows/views.py:3336 msgid "MyMenu" msgstr "" -#: windows/__init__.py:753 +#: windows/views.py:919 windows/views.py:1350 windows/views.py:1357 +#: windows/views.py:1364 msgid "MyLabel" msgstr "" -#: windows/__init__.py:759 +#: windows/views.py:925 msgid "Databases" msgstr "" -#: windows/__init__.py:760 windows/__init__.py:822 windows/__init__.py:1959 -#: windows/__init__.py:1987 +#: windows/views.py:926 windows/views.py:1308 windows/views.py:2716 +#: windows/views.py:2744 msgid "Size" msgstr "" -#: windows/__init__.py:761 +#: windows/views.py:927 msgid "Elements" msgstr "" -#: windows/__init__.py:762 +#: windows/views.py:928 msgid "Modified at" msgstr "" -#: windows/__init__.py:763 windows/__init__.py:834 +#: windows/views.py:929 msgid "Tables" msgstr "" -#: windows/__init__.py:770 +#: windows/views.py:936 msgid "System" msgstr "" -#: windows/__init__.py:786 +#: windows/views.py:979 +msgid "Character set" +msgstr "" + +#: windows/components/dataview.py:43 windows/components/dataview.py:67 +#: windows/components/dataview.py:89 windows/views.py:1000 +#: windows/views.py:1312 windows/views.py:2980 +msgid "Collation" +msgstr "" + +#: windows/views.py:1026 windows/views.py:2862 +msgid "Encryption" +msgstr "" + +#: windows/views.py:1038 +msgid "Read Only" +msgstr "" + +#: windows/views.py:1055 +msgid "Tablespace" +msgstr "" + +#: windows/views.py:1076 +msgid "Connection limit" +msgstr "" + +#: windows/views.py:1119 +msgid "Profile" +msgstr "" + +#: windows/views.py:1145 +msgid "Default tablespace" +msgstr "" + +#: windows/views.py:1166 +msgid "Temporary tablespace" +msgstr "" + +#: windows/views.py:1192 +msgid "Quota" +msgstr "" + +#: windows/views.py:1211 +msgid "Unlimited quota" +msgstr "" + +#: windows/views.py:1228 +msgid "Account status" +msgstr "" + +#: windows/views.py:1249 +msgid "Password expire" +msgstr "" + +#: windows/views.py:1270 msgid "Table:" msgstr "" -#: windows/__init__.py:794 windows/__init__.py:1018 windows/__init__.py:1062 -#: windows/__init__.py:1168 windows/__init__.py:2408 +#: windows/views.py:1278 windows/views.py:1551 windows/views.py:1595 +#: windows/views.py:1701 windows/views.py:3268 msgid "Insert" msgstr "" -#: windows/__init__.py:799 +#: windows/views.py:1283 msgid "Clone" msgstr "" -#: windows/__init__.py:821 +#: windows/views.py:1307 msgid "Rows" msgstr "" -#: windows/__init__.py:824 windows/__init__.py:1989 +#: windows/views.py:1310 windows/views.py:2746 msgid "Updated at" msgstr "" -#: windows/__init__.py:826 windows/__init__.py:2120 -#: windows/components/dataview.py:43 windows/components/dataview.py:67 -#: windows/components/dataview.py:89 -msgid "Collation" +#: windows/views.py:1334 windows/views.py:1746 windows/views.py:2085 +#: windows/views.py:2140 +msgid "Apply" msgstr "" -#: windows/__init__.py:842 +#: windows/views.py:1344 windows/views.py:1503 windows/views.py:1956 +#: windows/views.py:3225 +msgid "Options" +msgstr "" + +#: windows/views.py:1375 msgid "Diagram" msgstr "" -#: windows/__init__.py:853 windows/__init__.py:1958 +#: windows/views.py:1386 windows/views.py:2715 msgid "Database" msgstr "" -#: windows/__init__.py:908 windows/__init__.py:2305 +#: windows/views.py:1441 windows/views.py:3165 msgid "Base" msgstr "" -#: windows/__init__.py:922 windows/__init__.py:2319 +#: windows/views.py:1455 windows/views.py:3179 msgid "Auto Increment" msgstr "" -#: windows/__init__.py:950 windows/__init__.py:2347 +#: windows/views.py:1483 windows/views.py:3207 msgid "Default Collation" msgstr "" -#: windows/__init__.py:970 windows/__init__.py:1301 windows/__init__.py:2365 -msgid "Options" -msgstr "" - -#: windows/__init__.py:982 windows/__init__.py:1023 windows/__init__.py:1067 +#: windows/views.py:1515 windows/views.py:1556 windows/views.py:1600 msgid "Remove" msgstr "" -#: windows/__init__.py:989 windows/__init__.py:1030 windows/__init__.py:1074 +#: windows/views.py:1522 windows/views.py:1563 windows/views.py:1607 msgid "Clear" msgstr "" -#: windows/__init__.py:1004 windows/__init__.py:2379 +#: windows/views.py:1537 windows/views.py:3239 msgid "Indexes" msgstr "" -#: windows/__init__.py:1048 +#: windows/views.py:1581 msgid "Foreign Keys" msgstr "" -#: windows/__init__.py:1092 +#: windows/views.py:1625 msgid "Checks" msgstr "" -#: windows/__init__.py:1160 windows/__init__.py:2400 +#: windows/views.py:1693 windows/views.py:3260 msgid "Columns:" msgstr "" -#: windows/__init__.py:1180 windows/__init__.py:2420 +#: windows/views.py:1713 windows/views.py:3280 msgid "Up" msgstr "" -#: windows/__init__.py:1187 windows/__init__.py:2427 +#: windows/views.py:1720 windows/views.py:3287 msgid "Down" msgstr "" -#: windows/__init__.py:1213 windows/__init__.py:1426 windows/__init__.py:1481 -msgid "Apply" -msgstr "" - -#: windows/__init__.py:1226 windows/__init__.py:1233 windows/__init__.py:2466 -#: windows/__init__.py:2473 +#: windows/views.py:1759 windows/views.py:1766 windows/views.py:3326 +#: windows/views.py:3333 msgid "Add Index" msgstr "" -#: windows/__init__.py:1230 windows/__init__.py:2470 +#: windows/views.py:1763 windows/views.py:3330 msgid "Add PrimaryKey" msgstr "" -#: windows/__init__.py:1247 +#: windows/views.py:1780 msgid "Table" msgstr "" -#: windows/__init__.py:1280 -msgid "Temporary" +#: windows/views.py:1816 +msgid "Definer" +msgstr "" + +#: windows/views.py:1836 +msgid "Schema" +msgstr "" + +#: windows/views.py:1862 +msgid "SQL security" +msgstr "" + +#: windows/views.py:1869 +msgid "DEFINER" +msgstr "" + +#: windows/views.py:1869 +msgid "INVOKER" +msgstr "" + +#: windows/views.py:1881 windows/views.py:2558 windows/views.py:2577 +#: windows/views.py:2820 +msgid "Algorithm" +msgstr "" + +#: windows/views.py:1883 windows/views.py:2543 windows/views.py:2576 +#: windows/views.py:2825 +msgid "UNDEFINED" +msgstr "" + +#: windows/views.py:1886 windows/views.py:2546 windows/views.py:2576 +#: windows/views.py:2828 +msgid "MERGE" +msgstr "" + +#: windows/views.py:1889 windows/views.py:2555 windows/views.py:2576 +#: windows/views.py:2831 +msgid "TEMPTABLE" +msgstr "" + +#: windows/views.py:1899 windows/views.py:2582 +msgid "View constraint" +msgstr "" + +#: windows/views.py:1901 windows/views.py:2581 +msgid "None" +msgstr "" + +#: windows/views.py:1904 windows/views.py:2581 +msgid "LOCAL" +msgstr "" + +#: windows/views.py:1907 +msgid "CASCADE" +msgstr "" + +#: windows/views.py:1910 +msgid "CHECK ONLY" +msgstr "" + +#: windows/views.py:1913 windows/views.py:2581 +msgid "READ ONLY" +msgstr "" + +#: windows/views.py:1925 +msgid "Force" msgstr "" -#: windows/__init__.py:1360 +#: windows/views.py:1937 +msgid "Security barrier" +msgstr "" + +#: windows/views.py:2019 msgid "Views" msgstr "" -#: windows/__init__.py:1368 +#: windows/views.py:2027 msgid "Triggers" msgstr "" -#: windows/__init__.py:1380 +#: windows/views.py:2039 #, python-format msgid "Table `%(database_name)s`.`%(table_name)s`: %(total_rows) rows total" msgstr "" -#: windows/__init__.py:1390 +#: windows/views.py:2049 msgid "Insert record" msgstr "" -#: windows/__init__.py:1395 +#: windows/views.py:2054 msgid "Duplicate record" msgstr "" -#: windows/__init__.py:1402 +#: windows/views.py:2061 msgid "Delete record" msgstr "" -#: windows/__init__.py:1412 +#: windows/views.py:2071 msgid "Apply changes automatically" msgstr "" -#: windows/__init__.py:1414 windows/__init__.py:1415 +#: windows/views.py:2073 windows/views.py:2074 msgid "" "If enabled, table edits are applied immediately without pressing Apply or" " Cancel" msgstr "" -#: windows/__init__.py:1436 +#: windows/views.py:2095 msgid "Next" msgstr "" -#: windows/__init__.py:1444 +#: windows/views.py:2103 msgid "Filters" msgstr "" -#: windows/__init__.py:1484 +#: windows/views.py:2143 msgid "CTRL+ENTER" msgstr "" -#: windows/__init__.py:1504 +#: windows/views.py:2163 msgid "Insert row" msgstr "" -#: windows/__init__.py:1512 +#: windows/views.py:2171 msgid "Data" msgstr "" -#: windows/__init__.py:1561 -msgid "Query" +#: windows/views.py:2225 windows/views.py:2275 +msgid "New" msgstr "" -#: windows/__init__.py:1581 -msgid "Close" +#: windows/views.py:2252 +msgid "Query" msgstr "" -#: windows/__init__.py:1584 windows/__init__.py:2019 -msgid "New" +#: windows/views.py:2272 +msgid "Close" msgstr "" -#: windows/__init__.py:1594 +#: windows/views.py:2285 msgid "Query #2" msgstr "" -#: windows/__init__.py:1835 +#: windows/views.py:2537 msgid "Column5" msgstr "" -#: windows/__init__.py:1842 +#: windows/views.py:2548 +msgid "Import" +msgstr "" + +#: windows/views.py:2573 +msgid "Read only" +msgstr "" + +#: windows/views.py:2581 +msgid "CASCADED" +msgstr "" + +#: windows/views.py:2581 +msgid "CHECK OPTION" +msgstr "" + +#: windows/views.py:2613 msgid "collapsible" msgstr "" -#: windows/__init__.py:1864 +#: windows/views.py:2629 msgid "Column3" msgstr "" -#: windows/__init__.py:1865 +#: windows/views.py:2630 msgid "Column4" msgstr "" -#: windows/__init__.py:1902 +#: windows/views.py:2673 msgid "" "Database " "(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" msgstr "" -#: windows/__init__.py:1934 +#: windows/views.py:2685 msgid "Port" msgstr "" -#: windows/__init__.py:1951 +#: windows/views.py:2708 msgid "Usage" msgstr "" -#: windows/__init__.py:1962 +#: windows/views.py:2719 #, python-format msgid "%(total_rows)s" msgstr "" -#: windows/__init__.py:1967 +#: windows/views.py:2724 msgid "rows total" msgstr "" -#: windows/__init__.py:1986 +#: windows/views.py:2743 msgid "Lines" msgstr "" -#: windows/__init__.py:2068 +#: windows/views.py:2775 +msgid "Temporary" +msgstr "" + +#: windows/views.py:2786 +msgid "Engine options" +msgstr "" + +#: windows/views.py:2845 +msgid "RadioBtn" +msgstr "" + +#: windows/views.py:2928 msgid "Edit Column" msgstr "" -#: windows/__init__.py:2084 +#: windows/views.py:2944 msgid "Datatype" msgstr "" -#: windows/__init__.py:2099 windows/components/dataview.py:121 +#: windows/components/dataview.py:121 windows/views.py:2959 msgid "Length/Set" msgstr "" -#: windows/__init__.py:2138 windows/components/dataview.py:51 +#: windows/components/dataview.py:51 windows/views.py:2998 msgid "Unsigned" msgstr "" -#: windows/__init__.py:2144 windows/components/dataview.py:25 -#: windows/components/dataview.py:52 windows/components/dataview.py:75 +#: windows/components/dataview.py:25 windows/components/dataview.py:52 +#: windows/components/dataview.py:75 windows/views.py:3004 msgid "Allow NULL" msgstr "" -#: windows/__init__.py:2150 +#: windows/views.py:3010 msgid "Zero Fill" msgstr "" -#: windows/__init__.py:2161 windows/components/dataview.py:32 -#: windows/components/dataview.py:56 windows/components/dataview.py:78 +#: windows/components/dataview.py:32 windows/components/dataview.py:56 +#: windows/components/dataview.py:78 windows/views.py:3021 msgid "Default" msgstr "" -#: windows/__init__.py:2187 windows/components/dataview.py:36 -#: windows/components/dataview.py:60 windows/components/dataview.py:82 +#: windows/components/dataview.py:36 windows/components/dataview.py:60 +#: windows/components/dataview.py:82 windows/views.py:3047 msgid "Virtuality" msgstr "" -#: windows/__init__.py:2202 windows/components/dataview.py:39 -#: windows/components/dataview.py:63 windows/components/dataview.py:85 -#: windows/components/dataview.py:241 +#: windows/components/dataview.py:39 windows/components/dataview.py:63 +#: windows/components/dataview.py:85 windows/components/dataview.py:241 +#: windows/views.py:3062 msgid "Expression" msgstr "" @@ -619,130 +824,225 @@ msgstr "" msgid "Remove foreign key" msgstr "" -#: windows/components/popup.py:24 +#: windows/components/popup.py:26 msgid "No default value" msgstr "" -#: windows/components/popup.py:29 +#: windows/components/popup.py:31 msgid "NULL" msgstr "" -#: windows/components/popup.py:33 +#: windows/components/popup.py:35 msgid "AUTO INCREMENT" msgstr "" -#: windows/components/popup.py:37 +#: windows/components/popup.py:39 msgid "Text/Expression" msgstr "" -#: windows/connections/manager.py:125 +#: windows/dialogs/connections/view.py:119 windows/main/tabs/query.py:376 +msgid "Unknown error" +msgstr "" + +#: windows/dialogs/connections/view.py:394 +msgid "Connection established successfully" +msgstr "" + +#: windows/dialogs/connections/view.py:407 msgid "Confirm save" msgstr "" -#: windows/connections/manager.py:193 -msgid "Connection error" +#: windows/dialogs/connections/view.py:459 +msgid "You have unsaved changes. Do you want to save them before continuing?" msgstr "" -#: windows/connections/manager.py:214 -msgid "connection" +#: windows/dialogs/connections/view.py:461 +msgid "Unsaved changes" msgstr "" -#: windows/connections/manager.py:215 windows/connections/manager.py:230 +#: windows/dialogs/connections/view.py:736 +msgid "" +"This connection cannot work without TLS. TLS has been enabled " +"automatically." +msgstr "" + +#: windows/dialogs/connections/view.py:762 +msgid "Connection error" +msgstr "" + +#: windows/dialogs/connections/view.py:788 +#: windows/dialogs/connections/view.py:803 msgid "Confirm delete" msgstr "" -#: windows/connections/manager.py:229 -msgid "directory" +#: windows/main/controller.py:172 +msgid "days" msgstr "" -#: windows/connections/model.py:101 -msgid "New connection" +#: windows/main/controller.py:173 +msgid "hours" +msgstr "" + +#: windows/main/controller.py:174 +msgid "minutes" +msgstr "" + +#: windows/main/controller.py:175 +msgid "seconds" +msgstr "" + +#: windows/main/controller.py:183 +#, python-brace-format +msgid "Memory used: {used} ({percentage:.2%})" +msgstr "" + +#: windows/main/controller.py:219 +msgid "Settings saved successfully" +msgstr "" + +#: windows/main/controller.py:298 +msgid "Version" msgstr "" -#: windows/main/database.py:70 +#: windows/main/controller.py:300 +msgid "Uptime" +msgstr "" + +#: windows/main/controller.py:399 +#, python-brace-format +msgid "" +"Do you want to create a dump before dropping database '{database_name}'?\n" +"\n" +"Dump is not implemented yet.\n" +"- Yes: open dump flow (coming soon, no drop).\n" +"- No: drop the database now." +msgstr "" + +#: windows/main/controller.py:404 windows/main/controller.py:425 +msgid "Delete database" +msgstr "" + +#: windows/main/controller.py:410 +msgid "Dump is not implemented yet. No action has been performed." +msgstr "" + +#: windows/main/controller.py:411 +msgid "Dump not available" +msgstr "" + +#: windows/main/controller.py:424 +msgid "Database deletion is not supported by this engine." +msgstr "" + +#: windows/main/controller.py:439 +msgid "Database deleted successfully" +msgstr "" + +#: windows/main/controller.py:440 windows/main/tabs/view.py:253 +#: windows/main/tabs/view.py:279 +msgid "Success" +msgstr "" + +#: windows/main/controller.py:582 +msgid "Delete table" +msgstr "" + +#: windows/main/controller.py:699 +msgid "Do you want delete the records?" +msgstr "" + +#: windows/main/tabs/database.py:71 msgid "The connection to the database was lost." msgstr "" -#: windows/main/database.py:72 +#: windows/main/tabs/database.py:73 msgid "Do you want to reconnect?" msgstr "" -#: windows/main/database.py:74 +#: windows/main/tabs/database.py:75 msgid "Connection lost" msgstr "" -#: windows/main/database.py:83 +#: windows/main/tabs/database.py:85 msgid "Reconnection failed:" msgstr "" -#: windows/main/database.py:84 +#: windows/main/tabs/database.py:86 windows/main/tabs/query.py:450 +#: windows/main/tabs/view.py:256 windows/main/tabs/view.py:282 msgid "Error" msgstr "" -#: windows/main/main_frame.py:124 -msgid "days" +#: windows/main/tabs/query.py:305 +#, python-brace-format +msgid "{} rows affected" msgstr "" -#: windows/main/main_frame.py:125 -msgid "hours" +#: windows/main/tabs/query.py:309 windows/main/tabs/query.py:331 +#, python-brace-format +msgid "Query {}" msgstr "" -#: windows/main/main_frame.py:126 -msgid "minutes" +#: windows/main/tabs/query.py:314 +#, python-brace-format +msgid "Query {} (Error)" msgstr "" -#: windows/main/main_frame.py:127 -msgid "seconds" +#: windows/main/tabs/query.py:326 +#, python-brace-format +msgid "Query {} ({} rows × {} cols)" msgstr "" -#: windows/main/main_frame.py:135 +#: windows/main/tabs/query.py:353 #, python-brace-format -msgid "Memory used: {used} ({percentage:.2%})" +msgid "{} rows" msgstr "" -#: windows/main/main_frame.py:226 -msgid "Version" +#: windows/main/tabs/query.py:355 +#, python-brace-format +msgid "{:.1f} ms" msgstr "" -#: windows/main/main_frame.py:228 -msgid "Uptime" +#: windows/main/tabs/query.py:358 +#, python-brace-format +msgid "{} warnings" msgstr "" -#: windows/main/main_frame.py:431 -msgid "Delete table" +#: windows/main/tabs/query.py:370 +msgid "Error:" msgstr "" -#: windows/main/main_frame.py:586 -msgid "Do you want delete the records?" +#: windows/main/tabs/query.py:449 +msgid "No active database connection" msgstr "" -#~ msgid "Created at:" -#~ msgstr "" - -#~ msgid "Last connection:" -#~ msgstr "" - -#~ msgid "Successful connections:" -#~ msgstr "" - -#~ msgid "Unsuccessful connections:" -#~ msgstr "" +#: windows/main/tabs/view.py:252 +msgid "View created successfully" +msgstr "" -#~ msgid "Session Manager" -#~ msgstr "" +#: windows/main/tabs/view.py:252 +msgid "View updated successfully" +msgstr "" -#~ msgid "Session name" -#~ msgstr "" +#: windows/main/tabs/view.py:256 +#, python-brace-format +msgid "Error saving view: {}" +msgstr "" -#~ msgid "Connection type" -#~ msgstr "" +#: windows/main/tabs/view.py:269 +#, python-brace-format +msgid "Are you sure you want to delete view '{}'?" +msgstr "" -#~ msgid "Open" -#~ msgstr "" +#: windows/main/tabs/view.py:270 +msgid "Confirm Delete" +msgstr "" -#~ msgid "Open session manager" -#~ msgstr "" +#: windows/main/tabs/view.py:279 +msgid "View deleted successfully" +msgstr "" -#~ msgid "Foreign Key" -#~ msgstr "" +#: windows/main/tabs/view.py:282 +#, python-brace-format +msgid "Error deleting view: {}" +msgstr "" diff --git a/locale/es_ES/LC_MESSAGES/petersql.mo b/locale/es_ES/LC_MESSAGES/petersql.mo deleted file mode 100644 index 0913f7bc4d2121345b66ff9d6b5e2b29bbcfd979..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8405 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 diff --git a/locale/es_ES/LC_MESSAGES/petersql.po b/locale/es_ES/LC_MESSAGES/petersql.po index 2eb83b6..da6114d 100644 --- a/locale/es_ES/LC_MESSAGES/petersql.po +++ b/locale/es_ES/LC_MESSAGES/petersql.po @@ -1,761 +1,1048 @@ -# Spanish translations for petersql. -# Copyright (C) 2024 -# This file is distributed under the same license as the petersql package. -# -msgid "" -msgstr "" -"Language: es\n" -"Content-Type: text/plain; charset=UTF-8\n" - -#: helpers/__init__.py:14 +#: helpers/__init__.py:16 msgctxt "unit" msgid "B" -msgstr "B" +msgstr "" -#: helpers/__init__.py:15 +#: helpers/__init__.py:17 msgctxt "unit" msgid "KB" -msgstr "KB" +msgstr "" -#: helpers/__init__.py:16 +#: helpers/__init__.py:18 msgctxt "unit" msgid "MB" -msgstr "MB" +msgstr "" -#: helpers/__init__.py:17 +#: helpers/__init__.py:19 msgctxt "unit" msgid "GB" -msgstr "GB" +msgstr "" -#: helpers/__init__.py:18 +#: helpers/__init__.py:20 msgctxt "unit" msgid "TB" -msgstr "TB" +msgstr "" -#: structures/ssh_tunnel.py:117 +#: structures/ssh_tunnel.py:166 msgid "OpenSSH client not found." -msgstr "Cliente OpenSSH no encontrado." +msgstr "" -#: windows/__init__.py:31 windows/main/main_frame.py:224 +#: windows/dialogs/connections/view.py:395 +#: windows/dialogs/connections/view.py:738 windows/main/controller.py:296 +#: windows/views.py:33 msgid "Connection" -msgstr "Conexión" - -#: windows/__init__.py:45 windows/__init__.py:83 windows/__init__.py:820 -#: windows/__init__.py:880 windows/__init__.py:1267 windows/__init__.py:1950 -#: windows/__init__.py:1973 windows/__init__.py:1974 windows/__init__.py:1975 -#: windows/__init__.py:1976 windows/__init__.py:1977 windows/__init__.py:1978 -#: windows/__init__.py:1979 windows/__init__.py:1980 windows/__init__.py:1981 -#: windows/__init__.py:1985 windows/__init__.py:2076 windows/__init__.py:2277 +msgstr "" + #: windows/components/dataview.py:113 windows/components/dataview.py:225 #: windows/components/dataview.py:238 windows/components/dataview.py:253 +#: windows/views.py:47 windows/views.py:97 windows/views.py:956 +#: windows/views.py:1306 windows/views.py:1413 windows/views.py:1796 +#: windows/views.py:2707 windows/views.py:2730 windows/views.py:2731 +#: windows/views.py:2732 windows/views.py:2733 windows/views.py:2734 +#: windows/views.py:2735 windows/views.py:2736 windows/views.py:2737 +#: windows/views.py:2738 windows/views.py:2742 windows/views.py:2936 +#: windows/views.py:3137 msgid "Name" -msgstr "Nombre" +msgstr "" -#: windows/__init__.py:46 windows/__init__.py:322 +#: windows/views.py:48 windows/views.py:381 msgid "Last connection" -msgstr "Última conexión" +msgstr "" -#: windows/__init__.py:59 windows/connections/manager.py:174 +#: windows/dialogs/connections/view.py:631 windows/views.py:61 msgid "New directory" -msgstr "Nuevo directorio" +msgstr "" -#: windows/__init__.py:62 -msgid "New Session" -msgstr "Nueva sesión" +#: windows/dialogs/connections/model.py:187 +#: windows/dialogs/connections/view.py:591 windows/views.py:65 +msgid "New connection" +msgstr "" -#: windows/__init__.py:67 -msgid "Import" -msgstr "Importar" +#: windows/views.py:71 +msgid "Rename" +msgstr "" -#: windows/__init__.py:97 windows/__init__.py:825 windows/__init__.py:935 -#: windows/__init__.py:1990 windows/__init__.py:2332 +#: windows/views.py:76 +msgid "Clone connection" +msgstr "" + +#: windows/views.py:81 windows/views.py:556 windows/views.py:1290 +#: windows/views.py:1331 windows/views.py:1706 windows/views.py:1738 +#: windows/views.py:1997 windows/views.py:3273 windows/views.py:3305 +msgid "Delete" +msgstr "" + +#: windows/views.py:111 windows/views.py:1311 windows/views.py:1468 +#: windows/views.py:2747 windows/views.py:3192 msgid "Engine" -msgstr "Motor" +msgstr "" -#: windows/__init__.py:118 +#: windows/views.py:132 msgid "Host + port" -msgstr "Host + puerto" +msgstr "" -#: windows/__init__.py:134 +#: windows/views.py:148 msgid "Username" -msgstr "Nombre de usuario" +msgstr "" -#: windows/__init__.py:147 +#: windows/views.py:161 windows/views.py:1100 msgid "Password" -msgstr "Contraseña" +msgstr "" -#: windows/__init__.py:163 +#: windows/views.py:177 +msgid "Use TLS" +msgstr "" + +#: windows/views.py:180 msgid "Use SSH tunnel" -msgstr "Usar túnel SSH" +msgstr "" -#: windows/__init__.py:185 windows/__init__.py:1897 +#: windows/views.py:202 windows/views.py:2668 msgid "Filename" -msgstr "Nombre de archivo" +msgstr "" -#: windows/__init__.py:190 windows/__init__.py:1902 +#: windows/views.py:207 windows/views.py:324 windows/views.py:2673 +#: windows/views.py:2856 msgid "Select a file" -msgstr "Seleccionar un archivo" +msgstr "" -#: windows/__init__.py:190 -msgid "*. *" -msgstr "*. *" +#: windows/views.py:207 windows/views.py:324 windows/views.py:2856 +msgid "*.*" +msgstr "" -#: windows/__init__.py:204 windows/__init__.py:827 windows/__init__.py:893 -#: windows/__init__.py:1991 windows/__init__.py:2174 windows/__init__.py:2290 #: windows/components/dataview.py:70 windows/components/dataview.py:92 +#: windows/views.py:221 windows/views.py:1313 windows/views.py:1426 +#: windows/views.py:2748 windows/views.py:3034 windows/views.py:3150 msgid "Comments" -msgstr "Comentarios" +msgstr "" -#: windows/__init__.py:218 windows/__init__.py:520 +#: windows/main/controller.py:219 windows/views.py:235 windows/views.py:683 +#: windows/views.py:837 msgid "Settings" -msgstr "Configuraciones" +msgstr "" -#: windows/__init__.py:227 +#: windows/views.py:244 msgid "SSH executable" -msgstr "Ejecutable SSH" +msgstr "" -#: windows/__init__.py:232 +#: windows/views.py:249 msgid "ssh" -msgstr "ssh" +msgstr "" -#: windows/__init__.py:240 +#: windows/views.py:257 msgid "SSH host + port" -msgstr "Host SSH + puerto" +msgstr "" + +#: windows/views.py:269 +msgid "SSH host + port (the SSH server that forwards traffic to the DB)" +msgstr "" -#: windows/__init__.py:256 +#: windows/views.py:278 msgid "SSH username" -msgstr "Nombre de usuario SSH" +msgstr "" -#: windows/__init__.py:269 +#: windows/views.py:291 msgid "SSH password" -msgstr "Contraseña SSH" +msgstr "" -#: windows/__init__.py:282 +#: windows/views.py:304 msgid "Local port" -msgstr "Puerto local" +msgstr "" -#: windows/__init__.py:288 +#: windows/views.py:310 msgid "if the value is set to 0, the first available port will be used" -msgstr "si el valor se establece en 0, se utilizará el primer puerto disponible" +msgstr "" + +#: windows/views.py:319 +msgid "Identity file" +msgstr "" + +#: windows/views.py:335 +msgid "Remote host + port" +msgstr "" -#: windows/__init__.py:299 +#: windows/views.py:347 +msgid "Remote host/port is the real DB target (defaults to DB Host/Port)." +msgstr "" + +#: windows/views.py:358 msgid "SSH Tunnel" -msgstr "Túnel SSH" +msgstr "" -#: windows/__init__.py:305 windows/__init__.py:823 windows/__init__.py:1988 +#: windows/views.py:364 windows/views.py:1309 windows/views.py:2745 msgid "Created at" -msgstr "Creado en" +msgstr "" -#: windows/__init__.py:339 +#: windows/views.py:398 msgid "Successful connections" -msgstr "Conexiones exitosas" +msgstr "" + +#: windows/views.py:415 +msgid "Last successful connection" +msgstr "" -#: windows/__init__.py:356 +#: windows/views.py:432 msgid "Unsuccessful connections" -msgstr "Conexiones fallidas" +msgstr "" + +#: windows/views.py:449 +msgid "Last failure reason" +msgstr "" + +#: windows/views.py:466 +msgid "Total connection attempts" +msgstr "" -#: windows/__init__.py:375 +#: windows/views.py:483 +msgid " Average connection time (ms)" +msgstr "" + +#: windows/views.py:500 +msgid " Most recent connection duration" +msgstr "" + +#: windows/views.py:519 msgid "Statistics" -msgstr "Estadísticas" +msgstr "" -#: windows/__init__.py:393 windows/__init__.py:1139 +#: windows/views.py:537 windows/views.py:1672 msgid "Create" -msgstr "Crear" +msgstr "" -#: windows/__init__.py:403 windows/__init__.py:806 windows/__init__.py:1173 -#: windows/__init__.py:1205 windows/__init__.py:1340 windows/__init__.py:2413 -#: windows/__init__.py:2445 -msgid "Delete" -msgstr "Eliminar" +#: windows/views.py:541 +msgid "Create connection" +msgstr "" + +#: windows/views.py:544 +msgid "Create directory" +msgstr "" -#: windows/__init__.py:420 windows/__init__.py:634 windows/__init__.py:1208 -#: windows/__init__.py:1343 windows/__init__.py:1419 windows/__init__.py:2221 -#: windows/__init__.py:2448 +#: windows/views.py:573 windows/views.py:797 windows/views.py:1328 +#: windows/views.py:1741 windows/views.py:2002 windows/views.py:2078 +#: windows/views.py:3081 windows/views.py:3308 msgid "Cancel" -msgstr "Cancelar" +msgstr "" -#: windows/__init__.py:425 windows/__init__.py:1348 windows/__init__.py:2231 -#: windows/__init__.py:2453 +#: windows/views.py:578 windows/views.py:2007 windows/views.py:3091 +#: windows/views.py:3313 msgid "Save" -msgstr "Guardar" +msgstr "" -#: windows/__init__.py:432 +#: windows/views.py:585 msgid "Test" -msgstr "Probar" +msgstr "" -#: windows/__init__.py:439 +#: windows/views.py:592 msgid "Connect" -msgstr "Conectar" +msgstr "" -#: windows/__init__.py:532 +#: windows/views.py:695 msgid "Language" -msgstr "Idioma" +msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "English" -msgstr "Inglés" +msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "Italian" -msgstr "Italiano" +msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "French" -msgstr "Francés" +msgstr "" -#: windows/__init__.py:549 +#: windows/views.py:712 msgid "Locale" -msgstr "Localización" +msgstr "" -#: windows/__init__.py:570 +#: windows/views.py:733 msgid "Edit Value" -msgstr "Editar valor" +msgstr "" -#: windows/__init__.py:580 +#: windows/views.py:743 msgid "Syntax" -msgstr "Sintaxis" +msgstr "" -#: windows/__init__.py:637 +#: windows/views.py:800 msgid "Ok" -msgstr "Ok" +msgstr "" -#: windows/__init__.py:668 +#: windows/views.py:831 msgid "PeterSQL" -msgstr "PeterSQL" +msgstr "" -#: windows/__init__.py:674 +#: windows/views.py:840 msgid "File" -msgstr "Archivo" +msgstr "" -#: windows/__init__.py:677 +#: windows/views.py:843 msgid "About" -msgstr "Acerca de" +msgstr "" -#: windows/__init__.py:680 +#: windows/views.py:846 msgid "Help" -msgstr "Ayuda" +msgstr "" -#: windows/__init__.py:685 +#: windows/views.py:851 msgid "Open connection manager" -msgstr "Abrir administrador de conexiones" +msgstr "" -#: windows/__init__.py:687 +#: windows/views.py:853 msgid "Disconnect from server" -msgstr "Desconectar del servidor" +msgstr "" -#: windows/__init__.py:691 +#: windows/views.py:857 msgid "tool" -msgstr "herramienta" +msgstr "" -#: windows/__init__.py:691 +#: windows/views.py:857 msgid "Refresh" -msgstr "Actualizar" +msgstr "" -#: windows/__init__.py:695 windows/__init__.py:697 +#: windows/views.py:861 windows/views.py:863 msgid "Add" -msgstr "Agregar" +msgstr "" -#: windows/__init__.py:731 windows/__init__.py:735 windows/__init__.py:1507 -#: windows/__init__.py:1603 +#: windows/views.py:897 windows/views.py:901 windows/views.py:2166 +#: windows/views.py:2654 msgid "MyMenuItem" -msgstr "MiElementoMenu" +msgstr "" -#: windows/__init__.py:738 windows/__init__.py:1236 windows/__init__.py:2476 +#: windows/views.py:904 windows/views.py:1769 windows/views.py:3336 msgid "MyMenu" -msgstr "MiMenu" +msgstr "" -#: windows/__init__.py:753 +#: windows/views.py:919 windows/views.py:1350 windows/views.py:1357 +#: windows/views.py:1364 msgid "MyLabel" -msgstr "MiEtiqueta" +msgstr "" -#: windows/__init__.py:759 +#: windows/views.py:925 msgid "Databases" -msgstr "Bases de datos" +msgstr "" -#: windows/__init__.py:760 windows/__init__.py:822 windows/__init__.py:1959 -#: windows/__init__.py:1987 +#: windows/views.py:926 windows/views.py:1308 windows/views.py:2716 +#: windows/views.py:2744 msgid "Size" -msgstr "Tamaño" +msgstr "" -#: windows/__init__.py:761 +#: windows/views.py:927 msgid "Elements" -msgstr "Elementos" +msgstr "" -#: windows/__init__.py:762 +#: windows/views.py:928 msgid "Modified at" -msgstr "Modificado en" +msgstr "" -#: windows/__init__.py:763 windows/__init__.py:834 +#: windows/views.py:929 msgid "Tables" -msgstr "Tablas" +msgstr "" -#: windows/__init__.py:770 +#: windows/views.py:936 msgid "System" -msgstr "Sistema" +msgstr "" + +#: windows/views.py:979 +msgid "Character set" +msgstr "" + +#: windows/components/dataview.py:43 windows/components/dataview.py:67 +#: windows/components/dataview.py:89 windows/views.py:1000 +#: windows/views.py:1312 windows/views.py:2980 +msgid "Collation" +msgstr "" + +#: windows/views.py:1026 windows/views.py:2862 +msgid "Encryption" +msgstr "" + +#: windows/views.py:1038 +msgid "Read Only" +msgstr "" -#: windows/__init__.py:786 +#: windows/views.py:1055 +msgid "Tablespace" +msgstr "" + +#: windows/views.py:1076 +msgid "Connection limit" +msgstr "" + +#: windows/views.py:1119 +msgid "Profile" +msgstr "" + +#: windows/views.py:1145 +msgid "Default tablespace" +msgstr "" + +#: windows/views.py:1166 +msgid "Temporary tablespace" +msgstr "" + +#: windows/views.py:1192 +msgid "Quota" +msgstr "" + +#: windows/views.py:1211 +msgid "Unlimited quota" +msgstr "" + +#: windows/views.py:1228 +msgid "Account status" +msgstr "" + +#: windows/views.py:1249 +msgid "Password expire" +msgstr "" + +#: windows/views.py:1270 msgid "Table:" -msgstr "Tabla:" +msgstr "" -#: windows/__init__.py:794 windows/__init__.py:1018 windows/__init__.py:1062 -#: windows/__init__.py:1168 windows/__init__.py:2408 +#: windows/views.py:1278 windows/views.py:1551 windows/views.py:1595 +#: windows/views.py:1701 windows/views.py:3268 msgid "Insert" -msgstr "Insertar" +msgstr "" -#: windows/__init__.py:799 +#: windows/views.py:1283 msgid "Clone" -msgstr "Clonar" +msgstr "" -#: windows/__init__.py:821 +#: windows/views.py:1307 msgid "Rows" -msgstr "Filas" +msgstr "" -#: windows/__init__.py:824 windows/__init__.py:1989 +#: windows/views.py:1310 windows/views.py:2746 msgid "Updated at" -msgstr "Actualizado en" +msgstr "" -#: windows/__init__.py:826 windows/__init__.py:2120 -#: windows/components/dataview.py:43 windows/components/dataview.py:67 -#: windows/components/dataview.py:89 -msgid "Collation" -msgstr "Intercalación" +#: windows/views.py:1334 windows/views.py:1746 windows/views.py:2085 +#: windows/views.py:2140 +msgid "Apply" +msgstr "" + +#: windows/views.py:1344 windows/views.py:1503 windows/views.py:1956 +#: windows/views.py:3225 +msgid "Options" +msgstr "" -#: windows/__init__.py:842 +#: windows/views.py:1375 msgid "Diagram" -msgstr "Diagrama" +msgstr "" -#: windows/__init__.py:853 windows/__init__.py:1958 +#: windows/views.py:1386 windows/views.py:2715 msgid "Database" -msgstr "Base de datos" +msgstr "" -#: windows/__init__.py:908 windows/__init__.py:2305 +#: windows/views.py:1441 windows/views.py:3165 msgid "Base" -msgstr "Base" +msgstr "" -#: windows/__init__.py:922 windows/__init__.py:2319 +#: windows/views.py:1455 windows/views.py:3179 msgid "Auto Increment" -msgstr "Auto incremento" +msgstr "" -#: windows/__init__.py:950 windows/__init__.py:2347 +#: windows/views.py:1483 windows/views.py:3207 msgid "Default Collation" -msgstr "Intercalación predeterminada" - -#: windows/__init__.py:970 windows/__init__.py:1301 windows/__init__.py:2365 -msgid "Options" -msgstr "Opciones" +msgstr "" -#: windows/__init__.py:982 windows/__init__.py:1023 windows/__init__.py:1067 +#: windows/views.py:1515 windows/views.py:1556 windows/views.py:1600 msgid "Remove" -msgstr "Eliminar" +msgstr "" -#: windows/__init__.py:989 windows/__init__.py:1030 windows/__init__.py:1074 +#: windows/views.py:1522 windows/views.py:1563 windows/views.py:1607 msgid "Clear" -msgstr "Limpiar" +msgstr "" -#: windows/__init__.py:1004 windows/__init__.py:2379 +#: windows/views.py:1537 windows/views.py:3239 msgid "Indexes" -msgstr "Índices" +msgstr "" -#: windows/__init__.py:1048 +#: windows/views.py:1581 msgid "Foreign Keys" -msgstr "Claves foráneas" +msgstr "" -#: windows/__init__.py:1092 +#: windows/views.py:1625 msgid "Checks" -msgstr "Comprobaciones" +msgstr "" -#: windows/__init__.py:1160 windows/__init__.py:2400 +#: windows/views.py:1693 windows/views.py:3260 msgid "Columns:" -msgstr "Columnas:" +msgstr "" -#: windows/__init__.py:1180 windows/__init__.py:2420 +#: windows/views.py:1713 windows/views.py:3280 msgid "Up" -msgstr "Arriba" +msgstr "" -#: windows/__init__.py:1187 windows/__init__.py:2427 +#: windows/views.py:1720 windows/views.py:3287 msgid "Down" -msgstr "Abajo" - -#: windows/__init__.py:1213 windows/__init__.py:1426 windows/__init__.py:1481 -msgid "Apply" -msgstr "Aplicar" +msgstr "" -#: windows/__init__.py:1226 windows/__init__.py:1233 windows/__init__.py:2466 -#: windows/__init__.py:2473 +#: windows/views.py:1759 windows/views.py:1766 windows/views.py:3326 +#: windows/views.py:3333 msgid "Add Index" -msgstr "Agregar índice" +msgstr "" -#: windows/__init__.py:1230 windows/__init__.py:2470 +#: windows/views.py:1763 windows/views.py:3330 msgid "Add PrimaryKey" -msgstr "Agregar clave primaria" +msgstr "" -#: windows/__init__.py:1247 +#: windows/views.py:1780 msgid "Table" -msgstr "Tabla" +msgstr "" -#: windows/__init__.py:1280 -msgid "Temporary" -msgstr "Temporal" +#: windows/views.py:1816 +msgid "Definer" +msgstr "" + +#: windows/views.py:1836 +msgid "Schema" +msgstr "" + +#: windows/views.py:1862 +msgid "SQL security" +msgstr "" + +#: windows/views.py:1869 +msgid "DEFINER" +msgstr "" + +#: windows/views.py:1869 +msgid "INVOKER" +msgstr "" + +#: windows/views.py:1881 windows/views.py:2558 windows/views.py:2577 +#: windows/views.py:2820 +msgid "Algorithm" +msgstr "" -#: windows/__init__.py:1360 +#: windows/views.py:1883 windows/views.py:2543 windows/views.py:2576 +#: windows/views.py:2825 +msgid "UNDEFINED" +msgstr "" + +#: windows/views.py:1886 windows/views.py:2546 windows/views.py:2576 +#: windows/views.py:2828 +msgid "MERGE" +msgstr "" + +#: windows/views.py:1889 windows/views.py:2555 windows/views.py:2576 +#: windows/views.py:2831 +msgid "TEMPTABLE" +msgstr "" + +#: windows/views.py:1899 windows/views.py:2582 +msgid "View constraint" +msgstr "" + +#: windows/views.py:1901 windows/views.py:2581 +msgid "None" +msgstr "" + +#: windows/views.py:1904 windows/views.py:2581 +msgid "LOCAL" +msgstr "" + +#: windows/views.py:1907 +msgid "CASCADE" +msgstr "" + +#: windows/views.py:1910 +msgid "CHECK ONLY" +msgstr "" + +#: windows/views.py:1913 windows/views.py:2581 +msgid "READ ONLY" +msgstr "" + +#: windows/views.py:1925 +msgid "Force" +msgstr "" + +#: windows/views.py:1937 +msgid "Security barrier" +msgstr "" + +#: windows/views.py:2019 msgid "Views" -msgstr "Vistas" +msgstr "" -#: windows/__init__.py:1368 +#: windows/views.py:2027 msgid "Triggers" -msgstr "Disparadores" +msgstr "" -#: windows/__init__.py:1380 +#: windows/views.py:2039 #, python-format msgid "Table `%(database_name)s`.`%(table_name)s`: %(total_rows) rows total" -msgstr "Tabla `%(database_name)s`.`%(table_name)s`: %(total_rows) filas en total" +msgstr "" -#: windows/__init__.py:1390 +#: windows/views.py:2049 msgid "Insert record" -msgstr "Insertar registro" +msgstr "" -#: windows/__init__.py:1395 +#: windows/views.py:2054 msgid "Duplicate record" -msgstr "Duplicar registro" +msgstr "" -#: windows/__init__.py:1402 +#: windows/views.py:2061 msgid "Delete record" -msgstr "Eliminar registro" +msgstr "" -#: windows/__init__.py:1412 +#: windows/views.py:2071 msgid "Apply changes automatically" -msgstr "Aplicar cambios automáticamente" +msgstr "" -#: windows/__init__.py:1414 windows/__init__.py:1415 +#: windows/views.py:2073 windows/views.py:2074 msgid "" "If enabled, table edits are applied immediately without pressing Apply or" " Cancel" msgstr "" -"Si está habilitado, las ediciones de la tabla se aplican inmediatamente sin presionar Aplicar o" -" Cancelar" -#: windows/__init__.py:1436 +#: windows/views.py:2095 msgid "Next" -msgstr "Siguiente" +msgstr "" -#: windows/__init__.py:1444 +#: windows/views.py:2103 msgid "Filters" -msgstr "Filtros" +msgstr "" -#: windows/__init__.py:1484 +#: windows/views.py:2143 msgid "CTRL+ENTER" -msgstr "CTRL+ENTER" +msgstr "" -#: windows/__init__.py:1504 +#: windows/views.py:2163 msgid "Insert row" -msgstr "Insertar fila" +msgstr "" -#: windows/__init__.py:1512 +#: windows/views.py:2171 msgid "Data" -msgstr "Datos" +msgstr "" -#: windows/__init__.py:1561 +#: windows/views.py:2225 windows/views.py:2275 +msgid "New" +msgstr "" + +#: windows/views.py:2252 msgid "Query" -msgstr "Consulta" +msgstr "" -#: windows/__init__.py:1581 +#: windows/views.py:2272 msgid "Close" -msgstr "Cerrar" - -#: windows/__init__.py:1584 windows/__init__.py:2019 -msgid "New" -msgstr "Nuevo" +msgstr "" -#: windows/__init__.py:1594 +#: windows/views.py:2285 msgid "Query #2" -msgstr "Consulta #2" +msgstr "" -#: windows/__init__.py:1835 +#: windows/views.py:2537 msgid "Column5" -msgstr "Columna5" +msgstr "" + +#: windows/views.py:2548 +msgid "Import" +msgstr "" -#: windows/__init__.py:1842 +#: windows/views.py:2573 +msgid "Read only" +msgstr "" + +#: windows/views.py:2581 +msgid "CASCADED" +msgstr "" + +#: windows/views.py:2581 +msgid "CHECK OPTION" +msgstr "" + +#: windows/views.py:2613 msgid "collapsible" -msgstr "colapsable" +msgstr "" -#: windows/__init__.py:1864 +#: windows/views.py:2629 msgid "Column3" -msgstr "Columna3" +msgstr "" -#: windows/__init__.py:1865 +#: windows/views.py:2630 msgid "Column4" -msgstr "Columna4" +msgstr "" -#: windows/__init__.py:1902 +#: windows/views.py:2673 msgid "" "Database " "(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" msgstr "" -"Database " -"(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" -#: windows/__init__.py:1934 +#: windows/views.py:2685 msgid "Port" -msgstr "Puerto" +msgstr "" -#: windows/__init__.py:1951 +#: windows/views.py:2708 msgid "Usage" -msgstr "Uso" +msgstr "" -#: windows/__init__.py:1962 +#: windows/views.py:2719 #, python-format msgid "%(total_rows)s" -msgstr "%(total_rows)s" +msgstr "" -#: windows/__init__.py:1967 +#: windows/views.py:2724 msgid "rows total" -msgstr "filas en total" +msgstr "" -#: windows/__init__.py:1986 +#: windows/views.py:2743 msgid "Lines" -msgstr "Líneas" +msgstr "" + +#: windows/views.py:2775 +msgid "Temporary" +msgstr "" + +#: windows/views.py:2786 +msgid "Engine options" +msgstr "" -#: windows/__init__.py:2068 +#: windows/views.py:2845 +msgid "RadioBtn" +msgstr "" + +#: windows/views.py:2928 msgid "Edit Column" -msgstr "Editar columna" +msgstr "" -#: windows/__init__.py:2084 +#: windows/views.py:2944 msgid "Datatype" -msgstr "Tipo de datos" +msgstr "" -#: windows/__init__.py:2099 windows/components/dataview.py:121 +#: windows/components/dataview.py:121 windows/views.py:2959 msgid "Length/Set" -msgstr "Longitud/Conjunto" +msgstr "" -#: windows/__init__.py:2138 windows/components/dataview.py:51 +#: windows/components/dataview.py:51 windows/views.py:2998 msgid "Unsigned" -msgstr "Sin signo" +msgstr "" -#: windows/__init__.py:2144 windows/components/dataview.py:25 -#: windows/components/dataview.py:52 windows/components/dataview.py:75 +#: windows/components/dataview.py:25 windows/components/dataview.py:52 +#: windows/components/dataview.py:75 windows/views.py:3004 msgid "Allow NULL" -msgstr "Permitir NULL" +msgstr "" -#: windows/__init__.py:2150 +#: windows/views.py:3010 msgid "Zero Fill" -msgstr "Relleno cero" +msgstr "" -#: windows/__init__.py:2161 windows/components/dataview.py:32 -#: windows/components/dataview.py:56 windows/components/dataview.py:78 +#: windows/components/dataview.py:32 windows/components/dataview.py:56 +#: windows/components/dataview.py:78 windows/views.py:3021 msgid "Default" -msgstr "Predeterminado" +msgstr "" -#: windows/__init__.py:2187 windows/components/dataview.py:36 -#: windows/components/dataview.py:60 windows/components/dataview.py:82 +#: windows/components/dataview.py:36 windows/components/dataview.py:60 +#: windows/components/dataview.py:82 windows/views.py:3047 msgid "Virtuality" -msgstr "Virtualidad" +msgstr "" -#: windows/__init__.py:2202 windows/components/dataview.py:39 -#: windows/components/dataview.py:63 windows/components/dataview.py:85 -#: windows/components/dataview.py:241 +#: windows/components/dataview.py:39 windows/components/dataview.py:63 +#: windows/components/dataview.py:85 windows/components/dataview.py:241 +#: windows/views.py:3062 msgid "Expression" -msgstr "Expresión" +msgstr "" #: windows/components/dataview.py:28 msgid "Check" -msgstr "Verificar" +msgstr "" #: windows/components/dataview.py:53 msgid "Zerofill" -msgstr "Relleno cero" +msgstr "" #: windows/components/dataview.py:109 msgid "#" -msgstr "#" +msgstr "" #: windows/components/dataview.py:117 msgid "Data type" -msgstr "Tipo de datos" +msgstr "" #: windows/components/dataview.py:155 msgid "Add column\tCTRL+INS" -msgstr "Agregar columna\tCTRL+INS" +msgstr "" #: windows/components/dataview.py:161 msgid "Remove column\tCTRL+DEL" -msgstr "Eliminar columna\tCTRL+DEL" +msgstr "" #: windows/components/dataview.py:169 msgid "Move up\tCTRL+UP" -msgstr "Mover arriba\tCTRL+UP" +msgstr "" #: windows/components/dataview.py:176 msgid "Move down\tCTRL+D" -msgstr "Mover abajo\tCTRL+D" +msgstr "" #: windows/components/dataview.py:199 msgid "Create new index" -msgstr "Crear nuevo índice" +msgstr "" #: windows/components/dataview.py:214 msgid "Append to index" -msgstr "Agregar al índice" +msgstr "" #: windows/components/dataview.py:228 msgid "Column(s)/Expression" -msgstr "Columna(s)/Expresión" +msgstr "" #: windows/components/dataview.py:229 msgid "Condition" -msgstr "Condición" +msgstr "" #: windows/components/dataview.py:259 msgid "Column(s)" -msgstr "Columna(s)" +msgstr "" #: windows/components/dataview.py:265 msgid "Reference table" -msgstr "Tabla de referencia" +msgstr "" #: windows/components/dataview.py:271 msgid "Reference column(s)" -msgstr "Columna(s) de referencia" +msgstr "" #: windows/components/dataview.py:277 msgid "On UPDATE" -msgstr "En UPDATE" +msgstr "" #: windows/components/dataview.py:283 msgid "On DELETE" -msgstr "En DELETE" +msgstr "" #: windows/components/dataview.py:299 msgid "Add foreign key" -msgstr "Agregar clave foránea" +msgstr "" #: windows/components/dataview.py:305 msgid "Remove foreign key" -msgstr "Eliminar clave foránea" +msgstr "" -#: windows/components/popup.py:24 +#: windows/components/popup.py:26 msgid "No default value" -msgstr "Sin valor predeterminado" +msgstr "" -#: windows/components/popup.py:29 +#: windows/components/popup.py:31 msgid "NULL" -msgstr "NULL" +msgstr "" -#: windows/components/popup.py:33 +#: windows/components/popup.py:35 msgid "AUTO INCREMENT" -msgstr "AUTO INCREMENTO" +msgstr "" -#: windows/components/popup.py:37 +#: windows/components/popup.py:39 msgid "Text/Expression" -msgstr "Texto/Expresión" - -#: windows/connections/manager.py:125 -msgid "Confirm save" -msgstr "Confirmar guardar" - -#: windows/connections/manager.py:193 -msgid "Connection error" -msgstr "Error de conexión" - -#: windows/connections/manager.py:214 -msgid "connection" -msgstr "conexión" +msgstr "" -#: windows/connections/manager.py:215 windows/connections/manager.py:230 -msgid "Confirm delete" -msgstr "Confirmar eliminar" +#: windows/dialogs/connections/view.py:119 windows/main/tabs/query.py:376 +msgid "Unknown error" +msgstr "" -#: windows/connections/manager.py:229 -msgid "directory" -msgstr "directorio" +#: windows/dialogs/connections/view.py:394 +msgid "Connection established successfully" +msgstr "" -#: windows/connections/model.py:101 -msgid "New connection" -msgstr "Nueva conexión" +#: windows/dialogs/connections/view.py:407 +msgid "Confirm save" +msgstr "" -#: windows/main/database.py:70 -msgid "The connection to the database was lost." -msgstr "Se perdió la conexión a la base de datos." +#: windows/dialogs/connections/view.py:459 +msgid "You have unsaved changes. Do you want to save them before continuing?" +msgstr "" -#: windows/main/database.py:72 -msgid "Do you want to reconnect?" -msgstr "¿Quieres reconectar?" +#: windows/dialogs/connections/view.py:461 +msgid "Unsaved changes" +msgstr "" -#: windows/main/database.py:74 -msgid "Connection lost" -msgstr "Conexión perdida" +#: windows/dialogs/connections/view.py:736 +msgid "" +"This connection cannot work without TLS. TLS has been enabled " +"automatically." +msgstr "" -#: windows/main/database.py:83 -msgid "Reconnection failed:" -msgstr "Reconexión fallida:" +#: windows/dialogs/connections/view.py:762 +msgid "Connection error" +msgstr "" -#: windows/main/database.py:84 -msgid "Error" -msgstr "Error" +#: windows/dialogs/connections/view.py:788 +#: windows/dialogs/connections/view.py:803 +msgid "Confirm delete" +msgstr "" -#: windows/main/main_frame.py:124 +#: windows/main/controller.py:172 msgid "days" -msgstr "días" +msgstr "" -#: windows/main/main_frame.py:125 +#: windows/main/controller.py:173 msgid "hours" -msgstr "horas" +msgstr "" -#: windows/main/main_frame.py:126 +#: windows/main/controller.py:174 msgid "minutes" -msgstr "minutos" +msgstr "" -#: windows/main/main_frame.py:127 +#: windows/main/controller.py:175 msgid "seconds" -msgstr "segundos" +msgstr "" -#: windows/main/main_frame.py:135 +#: windows/main/controller.py:183 #, python-brace-format msgid "Memory used: {used} ({percentage:.2%})" -msgstr "Memoria utilizada: {used} ({percentage:.2%})" +msgstr "" + +#: windows/main/controller.py:219 +msgid "Settings saved successfully" +msgstr "" -#: windows/main/main_frame.py:226 +#: windows/main/controller.py:298 msgid "Version" -msgstr "Versión" +msgstr "" -#: windows/main/main_frame.py:228 +#: windows/main/controller.py:300 msgid "Uptime" -msgstr "Tiempo de actividad" +msgstr "" + +#: windows/main/controller.py:399 +#, python-brace-format +msgid "" +"Do you want to create a dump before dropping database '{database_name}'?\n" +"\n" +"Dump is not implemented yet.\n" +"- Yes: open dump flow (coming soon, no drop).\n" +"- No: drop the database now." +msgstr "" -#: windows/main/main_frame.py:431 +#: windows/main/controller.py:404 windows/main/controller.py:425 +msgid "Delete database" +msgstr "" + +#: windows/main/controller.py:410 +msgid "Dump is not implemented yet. No action has been performed." +msgstr "" + +#: windows/main/controller.py:411 +msgid "Dump not available" +msgstr "" + +#: windows/main/controller.py:424 +msgid "Database deletion is not supported by this engine." +msgstr "" + +#: windows/main/controller.py:439 +msgid "Database deleted successfully" +msgstr "" + +#: windows/main/controller.py:440 windows/main/tabs/view.py:253 +#: windows/main/tabs/view.py:279 +msgid "Success" +msgstr "" + +#: windows/main/controller.py:582 msgid "Delete table" -msgstr "Eliminar tabla" +msgstr "" -#: windows/main/main_frame.py:586 +#: windows/main/controller.py:699 msgid "Do you want delete the records?" -msgstr "¿Quieres eliminar los registros?" +msgstr "" -#~ msgid "Created at:" -#~ msgstr "" +#: windows/main/tabs/database.py:71 +msgid "The connection to the database was lost." +msgstr "" + +#: windows/main/tabs/database.py:73 +msgid "Do you want to reconnect?" +msgstr "" + +#: windows/main/tabs/database.py:75 +msgid "Connection lost" +msgstr "" -#~ msgid "Last connection:" -#~ msgstr "" +#: windows/main/tabs/database.py:85 +msgid "Reconnection failed:" +msgstr "" -#~ msgid "Successful connections:" -#~ msgstr "" +#: windows/main/tabs/database.py:86 windows/main/tabs/query.py:450 +#: windows/main/tabs/view.py:256 windows/main/tabs/view.py:282 +msgid "Error" +msgstr "" -#~ msgid "Unsuccessful connections:" -#~ msgstr "" +#: windows/main/tabs/query.py:305 +#, python-brace-format +msgid "{} rows affected" +msgstr "" -#~ msgid "Session Manager" -#~ msgstr "" +#: windows/main/tabs/query.py:309 windows/main/tabs/query.py:331 +#, python-brace-format +msgid "Query {}" +msgstr "" -#~ msgid "Session name" -#~ msgstr "" +#: windows/main/tabs/query.py:314 +#, python-brace-format +msgid "Query {} (Error)" +msgstr "" -#~ msgid "Connection type" -#~ msgstr "" +#: windows/main/tabs/query.py:326 +#, python-brace-format +msgid "Query {} ({} rows × {} cols)" +msgstr "" -#~ msgid "Open" -#~ msgstr "" +#: windows/main/tabs/query.py:353 +#, python-brace-format +msgid "{} rows" +msgstr "" -#~ msgid "Open session manager" -#~ msgstr "" +#: windows/main/tabs/query.py:355 +#, python-brace-format +msgid "{:.1f} ms" +msgstr "" -#~ msgid "Foreign Key" -#~ msgstr "" +#: windows/main/tabs/query.py:358 +#, python-brace-format +msgid "{} warnings" +msgstr "" + +#: windows/main/tabs/query.py:370 +msgid "Error:" +msgstr "" + +#: windows/main/tabs/query.py:449 +msgid "No active database connection" +msgstr "" + +#: windows/main/tabs/view.py:252 +msgid "View created successfully" +msgstr "" + +#: windows/main/tabs/view.py:252 +msgid "View updated successfully" +msgstr "" + +#: windows/main/tabs/view.py:256 +#, python-brace-format +msgid "Error saving view: {}" +msgstr "" + +#: windows/main/tabs/view.py:269 +#, python-brace-format +msgid "Are you sure you want to delete view '{}'?" +msgstr "" + +#: windows/main/tabs/view.py:270 +msgid "Confirm Delete" +msgstr "" + +#: windows/main/tabs/view.py:279 +msgid "View deleted successfully" +msgstr "" + +#: windows/main/tabs/view.py:282 +#, python-brace-format +msgid "Error deleting view: {}" +msgstr "" diff --git a/locale/fr_FR/LC_MESSAGES/petersql.mo b/locale/fr_FR/LC_MESSAGES/petersql.mo deleted file mode 100644 index 16435e89f91630768090981b90df856ac3f4afbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8614 zcmcJTe{fyJb;s8M5^zYs6w-zS*n*8M{}99u4iR;XEXi0{l7%HX{0PBU@7*U|bKkv> zd+(Dhz=e`P;)J^7hY3GILQH5ZAo-)v$&WVuF|SO?gv`)(CQ}lIW|--embNrgI+J!X zO+Vkg`<|YNXPW-eJ3jBTyLa#I+1+!_9{J_7PCjaQo_z}Np4>ibvWd*Hu9{+X3@ z);#Zq>VH0bE9`;lf2pr;_4RF@1D?ZBdhGGM4xUZD0^bG?dfw^zNq8#l_jx`D_5Cp@ zeV&4+!#{%h{y8YUUVvx9A4C3`f96Me{@k~p#7p%%8)`ilK#jW^YQA2$AAZ2MKj7Ps zLCy0dTm_$o8uwZF4){EjK0ku$_Yd%0@O5|rd@CQl8(s`G@0E~OW(d9=?t{{68cL5k z)cA8y;~j$9=ewc$eHv<<`=R$_*ee@g=dxD^+5H1 zAJlp-hni=ne}4_sJl8?(>$HELLFskK*FOd|&JlPqJPI}MA49G8IjHYngwppPq5A(6 zYQCRA>3b4JDxL~u$M1j|XBE`>p9iJqMyP&$P~&fhZ-L`b=Q`neBm8yhGf?Mn2h{uz zLapm7o=-rH_cYYHzUABh1j=6j67tWy#E;hTZ&2gtrPgsO)VZGlS&F$BYP=6Zt?xrn z`VT{We=XE`&3ZPW`rie$zE42ucht9k32L0jpyv4o)V$AnJ`bh$-@rlmqHjO{oU-2< zsD0T8HP2QkJ+6T3;BKh-3eQ9EH>lqOwXa`>n*SKoykCc!?-{6deFy6M??LU$U;Fnz zg8J?!Q1ibA)$c!`&h@|j`;{z8dYuE+?_8+ySHtaaJ=C}dpvKF5{ScI$+zs{pVJQ8M z`1eOWAMt$L^Xu?3`u!nPTzw5{9sdF~&d+^)1;!(N&V-u(Tqt|H5XzpmK*j4(&jV27 zd;-c2?uYt*5pIA_L+$(DLCy0z)Oy~48vhqi{olsst9=zz`+K3*wc2xof4|vt8)PfZ zE~x$esOOAl18TelD7*VO)I6Vo{4)>p!_?*(-~PPk525V-Cs6Bt14@rmSUg)~PKO%j zBB*&cLG|0}c@^AG{aQ%qG2iy>--kN?m!QUZ6>6McK;1v5om=kT8mRWmA^*${ezcD_ z!t>!_sPjJ#_1#lY`aTD>|1UwU`*kS2{|jnASI}AY)1cm;3Dy1{sPntX*Ed6r(+8!; zDAc)q7-}Cs0#g`4t^Y+RyM4viUxiF*ehM|t=@^UVxd3YZwNU!@Lha8E-#!VY*9}nf z)L<{nq4w<<1es`Ty-u`rP69DY%;YQOMHFcc8}oA(Xx^L+!_FQ2X_Uum2~M{%=8P?aMo%=IMci zEHeN#&$UqdU4i^FbcD_0rma`&sU(<^9I!Zt@zDS|F=W+ zUj?Q2d2kn84K;2ZY9E_W>%JSxUXDPm|Fdufd;rQG9`by|^HI;o;0oUBxes|C@(}V> zr2VX;(1SdNd>Gk{+<~;8Pf^esWmkHn--Ih+7oOXo?6v)DrZ9$l+Bf_@#08wLn1Jc? z?Q@==^OOxn8%^cM9+R?S_PipMJ`3|NBWT)kw+2P!w%## z$Z_NbWGk{4sUU}u_Osm=I5u+xku4^^?N&H~tVKS8d=dFFBD;D7xdpiz$&g8;iHsw^ zg~-O*&jS?X<9g(S2a%5>2N3zl5F(q^a}+ttmGD8&HhzynfqbCd(8bSFPLV^%Z~OMk z;N8e5Qb(RZu0SHB{ha3uAA=A3@-BF`FSoHkVbE8m;75^5kYOZ8PC}kUZbvMlXBOG# zO2&K*2FN)`uWwuJDXc*nNQlJ9RmcqTJIIHSdy#t(Jyqm`uEf3Qc^BM>3?SDaryw=t z4CHzQV_mbZNQ)r8DNE<`p4_bOwd+m))Z`vJJT|a*Xmn_7()3TK&BF9os}eh$RKtU= zG@eEEAY0fSF1TtXjhpr4w1LUJBO8XtCR|%B&BACVu{Wzv9H;YkY-(i0^fww|Qnf{D zqjI!HBc_9XZDlq{X2RSC%_6M_MN|o5y`#p|l`O1>Nnv&bd1%~BC^)p&30U&CNg@1{uF;Lf1Ie2WcELVw$LguIuuirP3us2OC+K=S#1*_?PeZyv#H2qfnO~ zOp+$m$Pby+qO5MKVH_5ktE}e1oU0^ZrBI_-PSGyfFw0UD?Yy%nOW!OEG>(H!!g;?} zWkb~lg&7Qr!2Q@_p%J>$v^GZVD%f@Fd#lq|=w}PRxhv;eO574hMW|L+*wSM*aAyI^ZsTDN(a&4rm6 zOzlG2wDUny_}#U|Z0JYIw=a8-m1qXHw|X<5n89Wv#)_ED&ppJ6Nk1&WxbnUrZiZ$k zcD9w9p=2h)WNld%f*iI&X$?^(aWp2tL*fW$VPQttgxrjz zIAE!~D~4t?tf$$6ZRTOM&)y~+vg>YZgjofTV9dVW&1(+zn9;Nv)wCWAbu^s|ZI#0) zqr+g?&}@{|sc|#9FcM7Tz@rPJVbb&%F2cHT@nTFHJr>SO?1XnxmAf=owHk561=`W^ zU}45m?8TX-opVRL=Vr4fv4cY+Lz6=)O^pxst3)*F8p_s#gmq-DePUvlt#B^bUXm8J zmNt`WFK^_;xfu`gd|viEjx}c!*Nm8P9rHEK5FOmFy=b%9+qOu)v9$opsrJ!W3uPrC zGq;1$Qd{X5)Cozm^sTdl9=^Zd%At{Vb9b!WOEfo9eX@xw#IA^eRGQ_udNbQ)mZ~?} zOHqYW&75^lD11%etd+tBwkFHRHi{^jVf^S;Y6THBCrndsRw|rLtr>T1Cd~_&#X*qk zJSUyUyI;G1&AO@&RX1t2o_v2VZBEB_L!VuCLs{KZTpZYOIaFVX+)RdY1@0qCgwII~ zpgW{b5;2ynMOTkvNPBvMOYXvMubIrE84f=;Q%TO%6;@R^@yezeW~#vurIAlz6PW2# zF29;0`f4MKa{sB8`?-&iY*Ot^V4hv{kiojVg-;@D1y&XG+$N}2`k z-nG5CthSn)90M%UG&ap7Do)-};wsPGo}-?VJ36tYk08EsAWaHLDvzWaoVXf(mXTZ=fd@=ngA+AOoGCRUYVT(@qTOPhp?&iJz0>o5*m=GCKVp_YDfupq+N zouQazNmyl89x+Cq$XVB-vPuMHUoccg%59euD<=z**4H#ze}p~mYCS_r=<~9P$Oh!) zbFIaVC-7UwZ*Q&5320@ic1u&%rgW{f=<+pbZu3Bn-e@!z*ohqDR>oEa1ajE6N0(_E z>X&~_k)h$@u38pOu!rqFyh0t_Ol39~`fLsd;EstDu)4fyBqh;fCodTyC9LZv8PTU? zF61fPcqDgaiWlYOUq+*xK#H&1W)YG8vPAp8XL;TMTZ_bYVv;3m=Q<57e}uF%zQJF5 zIg8~lC4gztI$UU<3jXLBO_NcV2ddTEglj0X!IZR6F(7D`jF}35a~G?7p$2F3E~5Cp zmH#oZ$F;vZ!J^TWro{5oQ}&{!CS1PZS$vgq zJJ#zp3$4kXNG{9l=*^A&6_<8!qb)_2rINES@1{#x=2DpHT&-R1)i$d#-&E>@vl4j9 zgwxeahtqC&r|Vw7>*0KPZE1L9RcyH%rxWhbwza5KRGW^O5ewUXPc#kct>e!3CrNXZ zr~1qkn{pRddFSNXohG(IW0{%Klhm|zoM`!fO-^x*<=Lt8OI`HcDJtjV2JYmjGC(D% zY`@afu=`RT36{*rD#HlmMa!-)5^;83sg1iJdkwx-;jdAwL#f~x7NN|wwYYTCDil`8 z6y5!}OIOY)Ng~6O%E*+_quy{ARxM5Z{wkcADHjFG&`_4keJ}Ic6Q7#tB)Qu8PL7K{ z{2fCu%#|VATd~me1T7p7nOCK}$T}Wnq$ufWmkVpPjGsr^Bk#a$s=HL9Bnq`W@?ZRv z+@#K3QKr4sC~u^Rl4{+AK_||2Qs6hAVVMVOCK6$?&M4;p6t7Cn?vyxXCR6i2{b&A9 diff --git a/locale/fr_FR/LC_MESSAGES/petersql.po b/locale/fr_FR/LC_MESSAGES/petersql.po index 4e7c519..da6114d 100644 --- a/locale/fr_FR/LC_MESSAGES/petersql.po +++ b/locale/fr_FR/LC_MESSAGES/petersql.po @@ -1,761 +1,1048 @@ -# French translations for petersql. -# Copyright (C) 2024 -# This file is distributed under the same license as the petersql package. -# -msgid "" -msgstr "" -"Language: fr\n" -"Content-Type: text/plain; charset=UTF-8\n" - -#: helpers/__init__.py:14 +#: helpers/__init__.py:16 msgctxt "unit" msgid "B" -msgstr "o" +msgstr "" -#: helpers/__init__.py:15 +#: helpers/__init__.py:17 msgctxt "unit" msgid "KB" -msgstr "Ko" +msgstr "" -#: helpers/__init__.py:16 +#: helpers/__init__.py:18 msgctxt "unit" msgid "MB" -msgstr "Mo" +msgstr "" -#: helpers/__init__.py:17 +#: helpers/__init__.py:19 msgctxt "unit" msgid "GB" -msgstr "Go" +msgstr "" -#: helpers/__init__.py:18 +#: helpers/__init__.py:20 msgctxt "unit" msgid "TB" -msgstr "To" +msgstr "" -#: structures/ssh_tunnel.py:117 +#: structures/ssh_tunnel.py:166 msgid "OpenSSH client not found." -msgstr "Client OpenSSH introuvable." +msgstr "" -#: windows/__init__.py:31 windows/main/main_frame.py:224 +#: windows/dialogs/connections/view.py:395 +#: windows/dialogs/connections/view.py:738 windows/main/controller.py:296 +#: windows/views.py:33 msgid "Connection" -msgstr "Connexion" - -#: windows/__init__.py:45 windows/__init__.py:83 windows/__init__.py:820 -#: windows/__init__.py:880 windows/__init__.py:1267 windows/__init__.py:1950 -#: windows/__init__.py:1973 windows/__init__.py:1974 windows/__init__.py:1975 -#: windows/__init__.py:1976 windows/__init__.py:1977 windows/__init__.py:1978 -#: windows/__init__.py:1979 windows/__init__.py:1980 windows/__init__.py:1981 -#: windows/__init__.py:1985 windows/__init__.py:2076 windows/__init__.py:2277 +msgstr "" + #: windows/components/dataview.py:113 windows/components/dataview.py:225 #: windows/components/dataview.py:238 windows/components/dataview.py:253 +#: windows/views.py:47 windows/views.py:97 windows/views.py:956 +#: windows/views.py:1306 windows/views.py:1413 windows/views.py:1796 +#: windows/views.py:2707 windows/views.py:2730 windows/views.py:2731 +#: windows/views.py:2732 windows/views.py:2733 windows/views.py:2734 +#: windows/views.py:2735 windows/views.py:2736 windows/views.py:2737 +#: windows/views.py:2738 windows/views.py:2742 windows/views.py:2936 +#: windows/views.py:3137 msgid "Name" -msgstr "Nom" +msgstr "" -#: windows/__init__.py:46 windows/__init__.py:322 +#: windows/views.py:48 windows/views.py:381 msgid "Last connection" -msgstr "Dernière connexion" +msgstr "" -#: windows/__init__.py:59 windows/connections/manager.py:174 +#: windows/dialogs/connections/view.py:631 windows/views.py:61 msgid "New directory" -msgstr "Nouveau répertoire" +msgstr "" -#: windows/__init__.py:62 -msgid "New Session" -msgstr "Nouvelle session" +#: windows/dialogs/connections/model.py:187 +#: windows/dialogs/connections/view.py:591 windows/views.py:65 +msgid "New connection" +msgstr "" -#: windows/__init__.py:67 -msgid "Import" -msgstr "Importer" +#: windows/views.py:71 +msgid "Rename" +msgstr "" -#: windows/__init__.py:97 windows/__init__.py:825 windows/__init__.py:935 -#: windows/__init__.py:1990 windows/__init__.py:2332 +#: windows/views.py:76 +msgid "Clone connection" +msgstr "" + +#: windows/views.py:81 windows/views.py:556 windows/views.py:1290 +#: windows/views.py:1331 windows/views.py:1706 windows/views.py:1738 +#: windows/views.py:1997 windows/views.py:3273 windows/views.py:3305 +msgid "Delete" +msgstr "" + +#: windows/views.py:111 windows/views.py:1311 windows/views.py:1468 +#: windows/views.py:2747 windows/views.py:3192 msgid "Engine" -msgstr "Moteur" +msgstr "" -#: windows/__init__.py:118 +#: windows/views.py:132 msgid "Host + port" -msgstr "Hôte + port" +msgstr "" -#: windows/__init__.py:134 +#: windows/views.py:148 msgid "Username" -msgstr "Nom d'utilisateur" +msgstr "" -#: windows/__init__.py:147 +#: windows/views.py:161 windows/views.py:1100 msgid "Password" -msgstr "Mot de passe" +msgstr "" -#: windows/__init__.py:163 +#: windows/views.py:177 +msgid "Use TLS" +msgstr "" + +#: windows/views.py:180 msgid "Use SSH tunnel" -msgstr "Utiliser un tunnel SSH" +msgstr "" -#: windows/__init__.py:185 windows/__init__.py:1897 +#: windows/views.py:202 windows/views.py:2668 msgid "Filename" -msgstr "Nom de fichier" +msgstr "" -#: windows/__init__.py:190 windows/__init__.py:1902 +#: windows/views.py:207 windows/views.py:324 windows/views.py:2673 +#: windows/views.py:2856 msgid "Select a file" -msgstr "Sélectionner un fichier" +msgstr "" -#: windows/__init__.py:190 -msgid "*. *" -msgstr "*. *" +#: windows/views.py:207 windows/views.py:324 windows/views.py:2856 +msgid "*.*" +msgstr "" -#: windows/__init__.py:204 windows/__init__.py:827 windows/__init__.py:893 -#: windows/__init__.py:1991 windows/__init__.py:2174 windows/__init__.py:2290 #: windows/components/dataview.py:70 windows/components/dataview.py:92 +#: windows/views.py:221 windows/views.py:1313 windows/views.py:1426 +#: windows/views.py:2748 windows/views.py:3034 windows/views.py:3150 msgid "Comments" -msgstr "Commentaires" +msgstr "" -#: windows/__init__.py:218 windows/__init__.py:520 +#: windows/main/controller.py:219 windows/views.py:235 windows/views.py:683 +#: windows/views.py:837 msgid "Settings" -msgstr "Paramètres" +msgstr "" -#: windows/__init__.py:227 +#: windows/views.py:244 msgid "SSH executable" -msgstr "Exécutable SSH" +msgstr "" -#: windows/__init__.py:232 +#: windows/views.py:249 msgid "ssh" -msgstr "ssh" +msgstr "" -#: windows/__init__.py:240 +#: windows/views.py:257 msgid "SSH host + port" -msgstr "Hôte SSH + port" +msgstr "" + +#: windows/views.py:269 +msgid "SSH host + port (the SSH server that forwards traffic to the DB)" +msgstr "" -#: windows/__init__.py:256 +#: windows/views.py:278 msgid "SSH username" -msgstr "Nom d'utilisateur SSH" +msgstr "" -#: windows/__init__.py:269 +#: windows/views.py:291 msgid "SSH password" -msgstr "Mot de passe SSH" +msgstr "" -#: windows/__init__.py:282 +#: windows/views.py:304 msgid "Local port" -msgstr "Port local" +msgstr "" -#: windows/__init__.py:288 +#: windows/views.py:310 msgid "if the value is set to 0, the first available port will be used" -msgstr "si la valeur est définie à 0, le premier port disponible sera utilisé" +msgstr "" + +#: windows/views.py:319 +msgid "Identity file" +msgstr "" + +#: windows/views.py:335 +msgid "Remote host + port" +msgstr "" -#: windows/__init__.py:299 +#: windows/views.py:347 +msgid "Remote host/port is the real DB target (defaults to DB Host/Port)." +msgstr "" + +#: windows/views.py:358 msgid "SSH Tunnel" -msgstr "Tunnel SSH" +msgstr "" -#: windows/__init__.py:305 windows/__init__.py:823 windows/__init__.py:1988 +#: windows/views.py:364 windows/views.py:1309 windows/views.py:2745 msgid "Created at" -msgstr "Créé le" +msgstr "" -#: windows/__init__.py:339 +#: windows/views.py:398 msgid "Successful connections" -msgstr "Connexions réussies" +msgstr "" + +#: windows/views.py:415 +msgid "Last successful connection" +msgstr "" -#: windows/__init__.py:356 +#: windows/views.py:432 msgid "Unsuccessful connections" -msgstr "Connexions échouées" +msgstr "" + +#: windows/views.py:449 +msgid "Last failure reason" +msgstr "" + +#: windows/views.py:466 +msgid "Total connection attempts" +msgstr "" -#: windows/__init__.py:375 +#: windows/views.py:483 +msgid " Average connection time (ms)" +msgstr "" + +#: windows/views.py:500 +msgid " Most recent connection duration" +msgstr "" + +#: windows/views.py:519 msgid "Statistics" -msgstr "Statistiques" +msgstr "" -#: windows/__init__.py:393 windows/__init__.py:1139 +#: windows/views.py:537 windows/views.py:1672 msgid "Create" -msgstr "Créer" +msgstr "" -#: windows/__init__.py:403 windows/__init__.py:806 windows/__init__.py:1173 -#: windows/__init__.py:1205 windows/__init__.py:1340 windows/__init__.py:2413 -#: windows/__init__.py:2445 -msgid "Delete" -msgstr "Supprimer" +#: windows/views.py:541 +msgid "Create connection" +msgstr "" + +#: windows/views.py:544 +msgid "Create directory" +msgstr "" -#: windows/__init__.py:420 windows/__init__.py:634 windows/__init__.py:1208 -#: windows/__init__.py:1343 windows/__init__.py:1419 windows/__init__.py:2221 -#: windows/__init__.py:2448 +#: windows/views.py:573 windows/views.py:797 windows/views.py:1328 +#: windows/views.py:1741 windows/views.py:2002 windows/views.py:2078 +#: windows/views.py:3081 windows/views.py:3308 msgid "Cancel" -msgstr "Annuler" +msgstr "" -#: windows/__init__.py:425 windows/__init__.py:1348 windows/__init__.py:2231 -#: windows/__init__.py:2453 +#: windows/views.py:578 windows/views.py:2007 windows/views.py:3091 +#: windows/views.py:3313 msgid "Save" -msgstr "Enregistrer" +msgstr "" -#: windows/__init__.py:432 +#: windows/views.py:585 msgid "Test" -msgstr "Tester" +msgstr "" -#: windows/__init__.py:439 +#: windows/views.py:592 msgid "Connect" -msgstr "Connecter" +msgstr "" -#: windows/__init__.py:532 +#: windows/views.py:695 msgid "Language" -msgstr "Langue" +msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "English" -msgstr "Anglais" +msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "Italian" -msgstr "Italien" +msgstr "" -#: windows/__init__.py:537 +#: windows/views.py:700 msgid "French" -msgstr "Français" +msgstr "" -#: windows/__init__.py:549 +#: windows/views.py:712 msgid "Locale" -msgstr "Localisation" +msgstr "" -#: windows/__init__.py:570 +#: windows/views.py:733 msgid "Edit Value" -msgstr "Modifier la valeur" +msgstr "" -#: windows/__init__.py:580 +#: windows/views.py:743 msgid "Syntax" -msgstr "Syntaxe" +msgstr "" -#: windows/__init__.py:637 +#: windows/views.py:800 msgid "Ok" -msgstr "Ok" +msgstr "" -#: windows/__init__.py:668 +#: windows/views.py:831 msgid "PeterSQL" -msgstr "PeterSQL" +msgstr "" -#: windows/__init__.py:674 +#: windows/views.py:840 msgid "File" -msgstr "Fichier" +msgstr "" -#: windows/__init__.py:677 +#: windows/views.py:843 msgid "About" -msgstr "À propos" +msgstr "" -#: windows/__init__.py:680 +#: windows/views.py:846 msgid "Help" -msgstr "Aide" +msgstr "" -#: windows/__init__.py:685 +#: windows/views.py:851 msgid "Open connection manager" -msgstr "Ouvrir le gestionnaire de connexions" +msgstr "" -#: windows/__init__.py:687 +#: windows/views.py:853 msgid "Disconnect from server" -msgstr "Se déconnecter du serveur" +msgstr "" -#: windows/__init__.py:691 +#: windows/views.py:857 msgid "tool" -msgstr "outil" +msgstr "" -#: windows/__init__.py:691 +#: windows/views.py:857 msgid "Refresh" -msgstr "Actualiser" +msgstr "" -#: windows/__init__.py:695 windows/__init__.py:697 +#: windows/views.py:861 windows/views.py:863 msgid "Add" -msgstr "Ajouter" +msgstr "" -#: windows/__init__.py:731 windows/__init__.py:735 windows/__init__.py:1507 -#: windows/__init__.py:1603 +#: windows/views.py:897 windows/views.py:901 windows/views.py:2166 +#: windows/views.py:2654 msgid "MyMenuItem" -msgstr "MonÉlémentMenu" +msgstr "" -#: windows/__init__.py:738 windows/__init__.py:1236 windows/__init__.py:2476 +#: windows/views.py:904 windows/views.py:1769 windows/views.py:3336 msgid "MyMenu" -msgstr "MonMenu" +msgstr "" -#: windows/__init__.py:753 +#: windows/views.py:919 windows/views.py:1350 windows/views.py:1357 +#: windows/views.py:1364 msgid "MyLabel" -msgstr "MonÉtiquette" +msgstr "" -#: windows/__init__.py:759 +#: windows/views.py:925 msgid "Databases" -msgstr "Bases de données" +msgstr "" -#: windows/__init__.py:760 windows/__init__.py:822 windows/__init__.py:1959 -#: windows/__init__.py:1987 +#: windows/views.py:926 windows/views.py:1308 windows/views.py:2716 +#: windows/views.py:2744 msgid "Size" -msgstr "Taille" +msgstr "" -#: windows/__init__.py:761 +#: windows/views.py:927 msgid "Elements" -msgstr "Éléments" +msgstr "" -#: windows/__init__.py:762 +#: windows/views.py:928 msgid "Modified at" -msgstr "Modifié le" +msgstr "" -#: windows/__init__.py:763 windows/__init__.py:834 +#: windows/views.py:929 msgid "Tables" -msgstr "Tables" +msgstr "" -#: windows/__init__.py:770 +#: windows/views.py:936 msgid "System" -msgstr "Système" +msgstr "" + +#: windows/views.py:979 +msgid "Character set" +msgstr "" + +#: windows/components/dataview.py:43 windows/components/dataview.py:67 +#: windows/components/dataview.py:89 windows/views.py:1000 +#: windows/views.py:1312 windows/views.py:2980 +msgid "Collation" +msgstr "" + +#: windows/views.py:1026 windows/views.py:2862 +msgid "Encryption" +msgstr "" + +#: windows/views.py:1038 +msgid "Read Only" +msgstr "" -#: windows/__init__.py:786 +#: windows/views.py:1055 +msgid "Tablespace" +msgstr "" + +#: windows/views.py:1076 +msgid "Connection limit" +msgstr "" + +#: windows/views.py:1119 +msgid "Profile" +msgstr "" + +#: windows/views.py:1145 +msgid "Default tablespace" +msgstr "" + +#: windows/views.py:1166 +msgid "Temporary tablespace" +msgstr "" + +#: windows/views.py:1192 +msgid "Quota" +msgstr "" + +#: windows/views.py:1211 +msgid "Unlimited quota" +msgstr "" + +#: windows/views.py:1228 +msgid "Account status" +msgstr "" + +#: windows/views.py:1249 +msgid "Password expire" +msgstr "" + +#: windows/views.py:1270 msgid "Table:" -msgstr "Table :" +msgstr "" -#: windows/__init__.py:794 windows/__init__.py:1018 windows/__init__.py:1062 -#: windows/__init__.py:1168 windows/__init__.py:2408 +#: windows/views.py:1278 windows/views.py:1551 windows/views.py:1595 +#: windows/views.py:1701 windows/views.py:3268 msgid "Insert" -msgstr "Insérer" +msgstr "" -#: windows/__init__.py:799 +#: windows/views.py:1283 msgid "Clone" -msgstr "Cloner" +msgstr "" -#: windows/__init__.py:821 +#: windows/views.py:1307 msgid "Rows" -msgstr "Lignes" +msgstr "" -#: windows/__init__.py:824 windows/__init__.py:1989 +#: windows/views.py:1310 windows/views.py:2746 msgid "Updated at" -msgstr "Mis à jour le" +msgstr "" -#: windows/__init__.py:826 windows/__init__.py:2120 -#: windows/components/dataview.py:43 windows/components/dataview.py:67 -#: windows/components/dataview.py:89 -msgid "Collation" -msgstr "Classement" +#: windows/views.py:1334 windows/views.py:1746 windows/views.py:2085 +#: windows/views.py:2140 +msgid "Apply" +msgstr "" + +#: windows/views.py:1344 windows/views.py:1503 windows/views.py:1956 +#: windows/views.py:3225 +msgid "Options" +msgstr "" -#: windows/__init__.py:842 +#: windows/views.py:1375 msgid "Diagram" -msgstr "Diagramme" +msgstr "" -#: windows/__init__.py:853 windows/__init__.py:1958 +#: windows/views.py:1386 windows/views.py:2715 msgid "Database" -msgstr "Base de données" +msgstr "" -#: windows/__init__.py:908 windows/__init__.py:2305 +#: windows/views.py:1441 windows/views.py:3165 msgid "Base" -msgstr "Base" +msgstr "" -#: windows/__init__.py:922 windows/__init__.py:2319 +#: windows/views.py:1455 windows/views.py:3179 msgid "Auto Increment" -msgstr "Auto incrément" +msgstr "" -#: windows/__init__.py:950 windows/__init__.py:2347 +#: windows/views.py:1483 windows/views.py:3207 msgid "Default Collation" -msgstr "Classement par défaut" - -#: windows/__init__.py:970 windows/__init__.py:1301 windows/__init__.py:2365 -msgid "Options" -msgstr "Options" +msgstr "" -#: windows/__init__.py:982 windows/__init__.py:1023 windows/__init__.py:1067 +#: windows/views.py:1515 windows/views.py:1556 windows/views.py:1600 msgid "Remove" -msgstr "Supprimer" +msgstr "" -#: windows/__init__.py:989 windows/__init__.py:1030 windows/__init__.py:1074 +#: windows/views.py:1522 windows/views.py:1563 windows/views.py:1607 msgid "Clear" -msgstr "Effacer" +msgstr "" -#: windows/__init__.py:1004 windows/__init__.py:2379 +#: windows/views.py:1537 windows/views.py:3239 msgid "Indexes" -msgstr "Index" +msgstr "" -#: windows/__init__.py:1048 +#: windows/views.py:1581 msgid "Foreign Keys" -msgstr "Clés étrangères" +msgstr "" -#: windows/__init__.py:1092 +#: windows/views.py:1625 msgid "Checks" -msgstr "Contrôles" +msgstr "" -#: windows/__init__.py:1160 windows/__init__.py:2400 +#: windows/views.py:1693 windows/views.py:3260 msgid "Columns:" -msgstr "Colonnes :" +msgstr "" -#: windows/__init__.py:1180 windows/__init__.py:2420 +#: windows/views.py:1713 windows/views.py:3280 msgid "Up" -msgstr "Haut" +msgstr "" -#: windows/__init__.py:1187 windows/__init__.py:2427 +#: windows/views.py:1720 windows/views.py:3287 msgid "Down" -msgstr "Bas" - -#: windows/__init__.py:1213 windows/__init__.py:1426 windows/__init__.py:1481 -msgid "Apply" -msgstr "Appliquer" +msgstr "" -#: windows/__init__.py:1226 windows/__init__.py:1233 windows/__init__.py:2466 -#: windows/__init__.py:2473 +#: windows/views.py:1759 windows/views.py:1766 windows/views.py:3326 +#: windows/views.py:3333 msgid "Add Index" -msgstr "Ajouter un index" +msgstr "" -#: windows/__init__.py:1230 windows/__init__.py:2470 +#: windows/views.py:1763 windows/views.py:3330 msgid "Add PrimaryKey" -msgstr "Ajouter une clé primaire" +msgstr "" -#: windows/__init__.py:1247 +#: windows/views.py:1780 msgid "Table" -msgstr "Table" +msgstr "" -#: windows/__init__.py:1280 -msgid "Temporary" -msgstr "Temporaire" +#: windows/views.py:1816 +msgid "Definer" +msgstr "" + +#: windows/views.py:1836 +msgid "Schema" +msgstr "" + +#: windows/views.py:1862 +msgid "SQL security" +msgstr "" + +#: windows/views.py:1869 +msgid "DEFINER" +msgstr "" + +#: windows/views.py:1869 +msgid "INVOKER" +msgstr "" + +#: windows/views.py:1881 windows/views.py:2558 windows/views.py:2577 +#: windows/views.py:2820 +msgid "Algorithm" +msgstr "" -#: windows/__init__.py:1360 +#: windows/views.py:1883 windows/views.py:2543 windows/views.py:2576 +#: windows/views.py:2825 +msgid "UNDEFINED" +msgstr "" + +#: windows/views.py:1886 windows/views.py:2546 windows/views.py:2576 +#: windows/views.py:2828 +msgid "MERGE" +msgstr "" + +#: windows/views.py:1889 windows/views.py:2555 windows/views.py:2576 +#: windows/views.py:2831 +msgid "TEMPTABLE" +msgstr "" + +#: windows/views.py:1899 windows/views.py:2582 +msgid "View constraint" +msgstr "" + +#: windows/views.py:1901 windows/views.py:2581 +msgid "None" +msgstr "" + +#: windows/views.py:1904 windows/views.py:2581 +msgid "LOCAL" +msgstr "" + +#: windows/views.py:1907 +msgid "CASCADE" +msgstr "" + +#: windows/views.py:1910 +msgid "CHECK ONLY" +msgstr "" + +#: windows/views.py:1913 windows/views.py:2581 +msgid "READ ONLY" +msgstr "" + +#: windows/views.py:1925 +msgid "Force" +msgstr "" + +#: windows/views.py:1937 +msgid "Security barrier" +msgstr "" + +#: windows/views.py:2019 msgid "Views" -msgstr "Vues" +msgstr "" -#: windows/__init__.py:1368 +#: windows/views.py:2027 msgid "Triggers" -msgstr "Déclencheurs" +msgstr "" -#: windows/__init__.py:1380 +#: windows/views.py:2039 #, python-format msgid "Table `%(database_name)s`.`%(table_name)s`: %(total_rows) rows total" -msgstr "Table `%(database_name)s`.`%(table_name)s` : %(total_rows) lignes au total" +msgstr "" -#: windows/__init__.py:1390 +#: windows/views.py:2049 msgid "Insert record" -msgstr "Insérer un enregistrement" +msgstr "" -#: windows/__init__.py:1395 +#: windows/views.py:2054 msgid "Duplicate record" -msgstr "Dupliquer un enregistrement" +msgstr "" -#: windows/__init__.py:1402 +#: windows/views.py:2061 msgid "Delete record" -msgstr "Supprimer un enregistrement" +msgstr "" -#: windows/__init__.py:1412 +#: windows/views.py:2071 msgid "Apply changes automatically" -msgstr "Appliquer les modifications automatiquement" +msgstr "" -#: windows/__init__.py:1414 windows/__init__.py:1415 +#: windows/views.py:2073 windows/views.py:2074 msgid "" "If enabled, table edits are applied immediately without pressing Apply or" " Cancel" msgstr "" -"Si activé, les modifications de la table sont appliquées immédiatement sans appuyer sur Appliquer ou" -" Annuler" -#: windows/__init__.py:1436 +#: windows/views.py:2095 msgid "Next" -msgstr "Suivant" +msgstr "" -#: windows/__init__.py:1444 +#: windows/views.py:2103 msgid "Filters" -msgstr "Filtres" +msgstr "" -#: windows/__init__.py:1484 +#: windows/views.py:2143 msgid "CTRL+ENTER" -msgstr "CTRL+ENTER" +msgstr "" -#: windows/__init__.py:1504 +#: windows/views.py:2163 msgid "Insert row" -msgstr "Insérer une ligne" +msgstr "" -#: windows/__init__.py:1512 +#: windows/views.py:2171 msgid "Data" -msgstr "Données" +msgstr "" -#: windows/__init__.py:1561 +#: windows/views.py:2225 windows/views.py:2275 +msgid "New" +msgstr "" + +#: windows/views.py:2252 msgid "Query" -msgstr "Requête" +msgstr "" -#: windows/__init__.py:1581 +#: windows/views.py:2272 msgid "Close" -msgstr "Fermer" - -#: windows/__init__.py:1584 windows/__init__.py:2019 -msgid "New" -msgstr "Nouveau" +msgstr "" -#: windows/__init__.py:1594 +#: windows/views.py:2285 msgid "Query #2" -msgstr "Requête #2" +msgstr "" -#: windows/__init__.py:1835 +#: windows/views.py:2537 msgid "Column5" -msgstr "Colonne5" +msgstr "" + +#: windows/views.py:2548 +msgid "Import" +msgstr "" -#: windows/__init__.py:1842 +#: windows/views.py:2573 +msgid "Read only" +msgstr "" + +#: windows/views.py:2581 +msgid "CASCADED" +msgstr "" + +#: windows/views.py:2581 +msgid "CHECK OPTION" +msgstr "" + +#: windows/views.py:2613 msgid "collapsible" -msgstr "rétractable" +msgstr "" -#: windows/__init__.py:1864 +#: windows/views.py:2629 msgid "Column3" -msgstr "Colonne3" +msgstr "" -#: windows/__init__.py:1865 +#: windows/views.py:2630 msgid "Column4" -msgstr "Colonne4" +msgstr "" -#: windows/__init__.py:1902 +#: windows/views.py:2673 msgid "" "Database " "(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" msgstr "" -"Database " -"(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" -#: windows/__init__.py:1934 +#: windows/views.py:2685 msgid "Port" -msgstr "Port" +msgstr "" -#: windows/__init__.py:1951 +#: windows/views.py:2708 msgid "Usage" -msgstr "Utilisation" +msgstr "" -#: windows/__init__.py:1962 +#: windows/views.py:2719 #, python-format msgid "%(total_rows)s" -msgstr "%(total_rows)s" +msgstr "" -#: windows/__init__.py:1967 +#: windows/views.py:2724 msgid "rows total" -msgstr "lignes au total" +msgstr "" -#: windows/__init__.py:1986 +#: windows/views.py:2743 msgid "Lines" -msgstr "Lignes" +msgstr "" + +#: windows/views.py:2775 +msgid "Temporary" +msgstr "" + +#: windows/views.py:2786 +msgid "Engine options" +msgstr "" -#: windows/__init__.py:2068 +#: windows/views.py:2845 +msgid "RadioBtn" +msgstr "" + +#: windows/views.py:2928 msgid "Edit Column" -msgstr "Modifier la colonne" +msgstr "" -#: windows/__init__.py:2084 +#: windows/views.py:2944 msgid "Datatype" -msgstr "Type de données" +msgstr "" -#: windows/__init__.py:2099 windows/components/dataview.py:121 +#: windows/components/dataview.py:121 windows/views.py:2959 msgid "Length/Set" -msgstr "Longueur/Ensemble" +msgstr "" -#: windows/__init__.py:2138 windows/components/dataview.py:51 +#: windows/components/dataview.py:51 windows/views.py:2998 msgid "Unsigned" -msgstr "Non signé" +msgstr "" -#: windows/__init__.py:2144 windows/components/dataview.py:25 -#: windows/components/dataview.py:52 windows/components/dataview.py:75 +#: windows/components/dataview.py:25 windows/components/dataview.py:52 +#: windows/components/dataview.py:75 windows/views.py:3004 msgid "Allow NULL" -msgstr "Autoriser NULL" +msgstr "" -#: windows/__init__.py:2150 +#: windows/views.py:3010 msgid "Zero Fill" -msgstr "Remplissage zéro" +msgstr "" -#: windows/__init__.py:2161 windows/components/dataview.py:32 -#: windows/components/dataview.py:56 windows/components/dataview.py:78 +#: windows/components/dataview.py:32 windows/components/dataview.py:56 +#: windows/components/dataview.py:78 windows/views.py:3021 msgid "Default" -msgstr "Par défaut" +msgstr "" -#: windows/__init__.py:2187 windows/components/dataview.py:36 -#: windows/components/dataview.py:60 windows/components/dataview.py:82 +#: windows/components/dataview.py:36 windows/components/dataview.py:60 +#: windows/components/dataview.py:82 windows/views.py:3047 msgid "Virtuality" -msgstr "Virtualité" +msgstr "" -#: windows/__init__.py:2202 windows/components/dataview.py:39 -#: windows/components/dataview.py:63 windows/components/dataview.py:85 -#: windows/components/dataview.py:241 +#: windows/components/dataview.py:39 windows/components/dataview.py:63 +#: windows/components/dataview.py:85 windows/components/dataview.py:241 +#: windows/views.py:3062 msgid "Expression" -msgstr "Expression" +msgstr "" #: windows/components/dataview.py:28 msgid "Check" -msgstr "Vérifier" +msgstr "" #: windows/components/dataview.py:53 msgid "Zerofill" -msgstr "Remplissage zéro" +msgstr "" #: windows/components/dataview.py:109 msgid "#" -msgstr "#" +msgstr "" #: windows/components/dataview.py:117 msgid "Data type" -msgstr "Type de données" +msgstr "" #: windows/components/dataview.py:155 msgid "Add column\tCTRL+INS" -msgstr "Ajouter une colonne\tCTRL+INS" +msgstr "" #: windows/components/dataview.py:161 msgid "Remove column\tCTRL+DEL" -msgstr "Supprimer une colonne\tCTRL+DEL" +msgstr "" #: windows/components/dataview.py:169 msgid "Move up\tCTRL+UP" -msgstr "Déplacer vers le haut\tCTRL+UP" +msgstr "" #: windows/components/dataview.py:176 msgid "Move down\tCTRL+D" -msgstr "Déplacer vers le bas\tCTRL+D" +msgstr "" #: windows/components/dataview.py:199 msgid "Create new index" -msgstr "Créer un nouvel index" +msgstr "" #: windows/components/dataview.py:214 msgid "Append to index" -msgstr "Ajouter à l'index" +msgstr "" #: windows/components/dataview.py:228 msgid "Column(s)/Expression" -msgstr "Colonne(s)/Expression" +msgstr "" #: windows/components/dataview.py:229 msgid "Condition" -msgstr "Condition" +msgstr "" #: windows/components/dataview.py:259 msgid "Column(s)" -msgstr "Colonne(s)" +msgstr "" #: windows/components/dataview.py:265 msgid "Reference table" -msgstr "Table de référence" +msgstr "" #: windows/components/dataview.py:271 msgid "Reference column(s)" -msgstr "Colonne(s) de référence" +msgstr "" #: windows/components/dataview.py:277 msgid "On UPDATE" -msgstr "Sur UPDATE" +msgstr "" #: windows/components/dataview.py:283 msgid "On DELETE" -msgstr "Sur DELETE" +msgstr "" #: windows/components/dataview.py:299 msgid "Add foreign key" -msgstr "Ajouter une clé étrangère" +msgstr "" #: windows/components/dataview.py:305 msgid "Remove foreign key" -msgstr "Supprimer une clé étrangère" +msgstr "" -#: windows/components/popup.py:24 +#: windows/components/popup.py:26 msgid "No default value" -msgstr "Aucune valeur par défaut" +msgstr "" -#: windows/components/popup.py:29 +#: windows/components/popup.py:31 msgid "NULL" -msgstr "NULL" +msgstr "" -#: windows/components/popup.py:33 +#: windows/components/popup.py:35 msgid "AUTO INCREMENT" -msgstr "AUTO INCREMENT" +msgstr "" -#: windows/components/popup.py:37 +#: windows/components/popup.py:39 msgid "Text/Expression" -msgstr "Texte/Expression" - -#: windows/connections/manager.py:125 -msgid "Confirm save" -msgstr "Confirmer la sauvegarde" - -#: windows/connections/manager.py:193 -msgid "Connection error" -msgstr "Erreur de connexion" - -#: windows/connections/manager.py:214 -msgid "connection" -msgstr "connexion" +msgstr "" -#: windows/connections/manager.py:215 windows/connections/manager.py:230 -msgid "Confirm delete" -msgstr "Confirmer la suppression" +#: windows/dialogs/connections/view.py:119 windows/main/tabs/query.py:376 +msgid "Unknown error" +msgstr "" -#: windows/connections/manager.py:229 -msgid "directory" -msgstr "répertoire" +#: windows/dialogs/connections/view.py:394 +msgid "Connection established successfully" +msgstr "" -#: windows/connections/model.py:101 -msgid "New connection" -msgstr "Nouvelle connexion" +#: windows/dialogs/connections/view.py:407 +msgid "Confirm save" +msgstr "" -#: windows/main/database.py:70 -msgid "The connection to the database was lost." -msgstr "La connexion à la base de données a été perdue." +#: windows/dialogs/connections/view.py:459 +msgid "You have unsaved changes. Do you want to save them before continuing?" +msgstr "" -#: windows/main/database.py:72 -msgid "Do you want to reconnect?" -msgstr "Voulez-vous vous reconnecter ?" +#: windows/dialogs/connections/view.py:461 +msgid "Unsaved changes" +msgstr "" -#: windows/main/database.py:74 -msgid "Connection lost" -msgstr "Connexion perdue" +#: windows/dialogs/connections/view.py:736 +msgid "" +"This connection cannot work without TLS. TLS has been enabled " +"automatically." +msgstr "" -#: windows/main/database.py:83 -msgid "Reconnection failed:" -msgstr "Échec de la reconnexion :" +#: windows/dialogs/connections/view.py:762 +msgid "Connection error" +msgstr "" -#: windows/main/database.py:84 -msgid "Error" -msgstr "Erreur" +#: windows/dialogs/connections/view.py:788 +#: windows/dialogs/connections/view.py:803 +msgid "Confirm delete" +msgstr "" -#: windows/main/main_frame.py:124 +#: windows/main/controller.py:172 msgid "days" -msgstr "jours" +msgstr "" -#: windows/main/main_frame.py:125 +#: windows/main/controller.py:173 msgid "hours" -msgstr "heures" +msgstr "" -#: windows/main/main_frame.py:126 +#: windows/main/controller.py:174 msgid "minutes" -msgstr "minutes" +msgstr "" -#: windows/main/main_frame.py:127 +#: windows/main/controller.py:175 msgid "seconds" -msgstr "secondes" +msgstr "" -#: windows/main/main_frame.py:135 +#: windows/main/controller.py:183 #, python-brace-format msgid "Memory used: {used} ({percentage:.2%})" -msgstr "Mémoire utilisée : {used} ({percentage:.2%})" +msgstr "" + +#: windows/main/controller.py:219 +msgid "Settings saved successfully" +msgstr "" -#: windows/main/main_frame.py:226 +#: windows/main/controller.py:298 msgid "Version" -msgstr "Version" +msgstr "" -#: windows/main/main_frame.py:228 +#: windows/main/controller.py:300 msgid "Uptime" -msgstr "Temps de fonctionnement" +msgstr "" + +#: windows/main/controller.py:399 +#, python-brace-format +msgid "" +"Do you want to create a dump before dropping database '{database_name}'?\n" +"\n" +"Dump is not implemented yet.\n" +"- Yes: open dump flow (coming soon, no drop).\n" +"- No: drop the database now." +msgstr "" -#: windows/main/main_frame.py:431 +#: windows/main/controller.py:404 windows/main/controller.py:425 +msgid "Delete database" +msgstr "" + +#: windows/main/controller.py:410 +msgid "Dump is not implemented yet. No action has been performed." +msgstr "" + +#: windows/main/controller.py:411 +msgid "Dump not available" +msgstr "" + +#: windows/main/controller.py:424 +msgid "Database deletion is not supported by this engine." +msgstr "" + +#: windows/main/controller.py:439 +msgid "Database deleted successfully" +msgstr "" + +#: windows/main/controller.py:440 windows/main/tabs/view.py:253 +#: windows/main/tabs/view.py:279 +msgid "Success" +msgstr "" + +#: windows/main/controller.py:582 msgid "Delete table" -msgstr "Supprimer la table" +msgstr "" -#: windows/main/main_frame.py:586 +#: windows/main/controller.py:699 msgid "Do you want delete the records?" -msgstr "Voulez-vous supprimer les enregistrements ?" +msgstr "" -#~ msgid "Created at:" -#~ msgstr "" +#: windows/main/tabs/database.py:71 +msgid "The connection to the database was lost." +msgstr "" + +#: windows/main/tabs/database.py:73 +msgid "Do you want to reconnect?" +msgstr "" + +#: windows/main/tabs/database.py:75 +msgid "Connection lost" +msgstr "" -#~ msgid "Last connection:" -#~ msgstr "" +#: windows/main/tabs/database.py:85 +msgid "Reconnection failed:" +msgstr "" -#~ msgid "Successful connections:" -#~ msgstr "" +#: windows/main/tabs/database.py:86 windows/main/tabs/query.py:450 +#: windows/main/tabs/view.py:256 windows/main/tabs/view.py:282 +msgid "Error" +msgstr "" -#~ msgid "Unsuccessful connections:" -#~ msgstr "" +#: windows/main/tabs/query.py:305 +#, python-brace-format +msgid "{} rows affected" +msgstr "" -#~ msgid "Session Manager" -#~ msgstr "" +#: windows/main/tabs/query.py:309 windows/main/tabs/query.py:331 +#, python-brace-format +msgid "Query {}" +msgstr "" -#~ msgid "Session name" -#~ msgstr "" +#: windows/main/tabs/query.py:314 +#, python-brace-format +msgid "Query {} (Error)" +msgstr "" -#~ msgid "Connection type" -#~ msgstr "" +#: windows/main/tabs/query.py:326 +#, python-brace-format +msgid "Query {} ({} rows × {} cols)" +msgstr "" -#~ msgid "Open" -#~ msgstr "" +#: windows/main/tabs/query.py:353 +#, python-brace-format +msgid "{} rows" +msgstr "" -#~ msgid "Open session manager" -#~ msgstr "" +#: windows/main/tabs/query.py:355 +#, python-brace-format +msgid "{:.1f} ms" +msgstr "" -#~ msgid "Foreign Key" -#~ msgstr "" +#: windows/main/tabs/query.py:358 +#, python-brace-format +msgid "{} warnings" +msgstr "" + +#: windows/main/tabs/query.py:370 +msgid "Error:" +msgstr "" + +#: windows/main/tabs/query.py:449 +msgid "No active database connection" +msgstr "" + +#: windows/main/tabs/view.py:252 +msgid "View created successfully" +msgstr "" + +#: windows/main/tabs/view.py:252 +msgid "View updated successfully" +msgstr "" + +#: windows/main/tabs/view.py:256 +#, python-brace-format +msgid "Error saving view: {}" +msgstr "" + +#: windows/main/tabs/view.py:269 +#, python-brace-format +msgid "Are you sure you want to delete view '{}'?" +msgstr "" + +#: windows/main/tabs/view.py:270 +msgid "Confirm Delete" +msgstr "" + +#: windows/main/tabs/view.py:279 +msgid "View deleted successfully" +msgstr "" + +#: windows/main/tabs/view.py:282 +#, python-brace-format +msgid "Error deleting view: {}" +msgstr "" diff --git a/locale/it_IT/LC_MESSAGES/petersql.mo b/locale/it_IT/LC_MESSAGES/petersql.mo deleted file mode 100644 index c9e6904d406aa0bb833173e39b5084fa6b0a121a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8332 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 List[SQLDataType]: + def get_all(cls) -> list[SQLDataType]: types = [ getattr(cls, name) for name in dir(cls) diff --git a/structures/engines/indextype.py b/structures/engines/indextype.py index 3198f23..d271caa 100755 --- a/structures/engines/indextype.py +++ b/structures/engines/indextype.py @@ -38,7 +38,7 @@ class StandardIndexType(): @classmethod @functools.lru_cache() - def get_all(cls) -> List[SQLIndexType]: + def get_all(cls) -> list[SQLIndexType]: types = [] for base in reversed(cls.__mro__): for key, value in base.__dict__.items(): diff --git a/structures/engines/sqlite/database.py b/structures/engines/sqlite/database.py index 79d1363..de82eb6 100644 --- a/structures/engines/sqlite/database.py +++ b/structures/engines/sqlite/database.py @@ -1,6 +1,6 @@ import re import dataclasses -from typing import Self, Optional, Dict +from typing import Self, Optional from helpers.logger import logger @@ -101,7 +101,7 @@ def raw_create(self) -> str: constraints = [] primary_keys = [] unique_indexes = [] - columns_definitions: Dict[str, str] = {} + columns_definitions: dict[str, str] = {} for index in self.indexes: if index.type == SQLiteIndexType.PRIMARY: From b34a2edd54981d6cea93c8c4f0e7c396ae9041bc Mon Sep 17 00:00:00 2001 From: gtripoli Date: Tue, 10 Mar 2026 18:58:46 +0100 Subject: [PATCH 11/12] update locales AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- locale/de_DE/LC_MESSAGES/petersql.po | 492 ++++++---- locale/en_US/LC_MESSAGES/petersql.po | 59 ++ locale/es_ES/LC_MESSAGES/petersql.po | 492 ++++++---- locale/fr_FR/LC_MESSAGES/petersql.po | 492 ++++++---- locale/it_IT/LC_MESSAGES/petersql.po | 314 +++--- scripts/locale/de_DE/LC_MESSAGES/petersql.po | 952 ------------------- scripts/locale/en_US/LC_MESSAGES/petersql.po | 952 ------------------- scripts/locales.py | 66 +- 8 files changed, 1196 insertions(+), 2623 deletions(-) delete mode 100644 scripts/locale/de_DE/LC_MESSAGES/petersql.po delete mode 100644 scripts/locale/en_US/LC_MESSAGES/petersql.po diff --git a/locale/de_DE/LC_MESSAGES/petersql.po b/locale/de_DE/LC_MESSAGES/petersql.po index da6114d..1ead29e 100644 --- a/locale/de_DE/LC_MESSAGES/petersql.po +++ b/locale/de_DE/LC_MESSAGES/petersql.po @@ -1,37 +1,57 @@ +# Deutsch (de_DE) translations for PeterSQL. +# Copyright (C) 2026 ORGANIZATION +# This file is distributed under the same license as the PeterSQL project. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PeterSQL 0.1.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2026-03-10 18:23+0100\n" +"PO-Revision-Date: 2026-03-10 18:21+0000\n" +"Last-Translator: \n" +"Language: de_DE\n" +"Language-Team: de_DE \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.18.0\n" + #: helpers/__init__.py:16 msgctxt "unit" msgid "B" -msgstr "" +msgstr "B" #: helpers/__init__.py:17 msgctxt "unit" msgid "KB" -msgstr "" +msgstr "KB" #: helpers/__init__.py:18 msgctxt "unit" msgid "MB" -msgstr "" +msgstr "MB" #: helpers/__init__.py:19 msgctxt "unit" msgid "GB" -msgstr "" +msgstr "GB" #: helpers/__init__.py:20 msgctxt "unit" msgid "TB" -msgstr "" +msgstr "TB" #: structures/ssh_tunnel.py:166 msgid "OpenSSH client not found." -msgstr "" +msgstr "OpenSSH-Client nicht gefunden." #: windows/dialogs/connections/view.py:395 #: windows/dialogs/connections/view.py:738 windows/main/controller.py:296 #: windows/views.py:33 msgid "Connection" -msgstr "" +msgstr "Verbindung" #: windows/components/dataview.py:113 windows/components/dataview.py:225 #: windows/components/dataview.py:238 windows/components/dataview.py:253 @@ -43,51 +63,53 @@ msgstr "" #: windows/views.py:2738 windows/views.py:2742 windows/views.py:2936 #: windows/views.py:3137 msgid "Name" -msgstr "" +msgstr "Name" #: windows/views.py:48 windows/views.py:381 msgid "Last connection" -msgstr "" +msgstr "Letzte Verbindung" #: windows/dialogs/connections/view.py:631 windows/views.py:61 msgid "New directory" -msgstr "" +msgstr "Neues Verzeichnis" #: windows/dialogs/connections/model.py:187 #: windows/dialogs/connections/view.py:591 windows/views.py:65 msgid "New connection" -msgstr "" +msgstr "Neue Verbindung" #: windows/views.py:71 +#, fuzzy msgid "Rename" -msgstr "" +msgstr "Name" #: windows/views.py:76 +#, fuzzy msgid "Clone connection" -msgstr "" +msgstr "Neue Verbindung" #: windows/views.py:81 windows/views.py:556 windows/views.py:1290 #: windows/views.py:1331 windows/views.py:1706 windows/views.py:1738 #: windows/views.py:1997 windows/views.py:3273 windows/views.py:3305 msgid "Delete" -msgstr "" +msgstr "Löschen" #: windows/views.py:111 windows/views.py:1311 windows/views.py:1468 #: windows/views.py:2747 windows/views.py:3192 msgid "Engine" -msgstr "" +msgstr "Engine" #: windows/views.py:132 msgid "Host + port" -msgstr "" +msgstr "Host + Port" #: windows/views.py:148 msgid "Username" -msgstr "" +msgstr "Benutzername" #: windows/views.py:161 windows/views.py:1100 msgid "Password" -msgstr "" +msgstr "Passwort" #: windows/views.py:177 msgid "Use TLS" @@ -95,43 +117,44 @@ msgstr "" #: windows/views.py:180 msgid "Use SSH tunnel" -msgstr "" +msgstr "SSH-Tunnel verwenden" #: windows/views.py:202 windows/views.py:2668 msgid "Filename" -msgstr "" +msgstr "Dateiname" #: windows/views.py:207 windows/views.py:324 windows/views.py:2673 #: windows/views.py:2856 msgid "Select a file" -msgstr "" +msgstr "Datei auswählen" #: windows/views.py:207 windows/views.py:324 windows/views.py:2856 +#, fuzzy msgid "*.*" -msgstr "" +msgstr "*. *" #: windows/components/dataview.py:70 windows/components/dataview.py:92 #: windows/views.py:221 windows/views.py:1313 windows/views.py:1426 #: windows/views.py:2748 windows/views.py:3034 windows/views.py:3150 msgid "Comments" -msgstr "" +msgstr "Kommentare" #: windows/main/controller.py:219 windows/views.py:235 windows/views.py:683 #: windows/views.py:837 msgid "Settings" -msgstr "" +msgstr "Einstellungen" #: windows/views.py:244 msgid "SSH executable" -msgstr "" +msgstr "SSH-Executable" #: windows/views.py:249 msgid "ssh" -msgstr "" +msgstr "ssh" #: windows/views.py:257 msgid "SSH host + port" -msgstr "" +msgstr "SSH-Host + Port" #: windows/views.py:269 msgid "SSH host + port (the SSH server that forwards traffic to the DB)" @@ -139,27 +162,28 @@ msgstr "" #: windows/views.py:278 msgid "SSH username" -msgstr "" +msgstr "SSH-Benutzername" #: windows/views.py:291 msgid "SSH password" -msgstr "" +msgstr "SSH-Passwort" #: windows/views.py:304 msgid "Local port" -msgstr "" +msgstr "Lokaler Port" #: windows/views.py:310 msgid "if the value is set to 0, the first available port will be used" -msgstr "" +msgstr "wenn der Wert auf 0 gesetzt ist, wird der erste verfügbare Port verwendet" #: windows/views.py:319 msgid "Identity file" msgstr "" #: windows/views.py:335 +#, fuzzy msgid "Remote host + port" -msgstr "" +msgstr "Host + Port" #: windows/views.py:347 msgid "Remote host/port is the real DB target (defaults to DB Host/Port)." @@ -167,191 +191,198 @@ msgstr "" #: windows/views.py:358 msgid "SSH Tunnel" -msgstr "" +msgstr "SSH-Tunnel" #: windows/views.py:364 windows/views.py:1309 windows/views.py:2745 msgid "Created at" -msgstr "" +msgstr "Erstellt am" #: windows/views.py:398 msgid "Successful connections" -msgstr "" +msgstr "Erfolgreiche Verbindungen" #: windows/views.py:415 +#, fuzzy msgid "Last successful connection" -msgstr "" +msgstr "Erfolgreiche Verbindungen" #: windows/views.py:432 msgid "Unsuccessful connections" -msgstr "" +msgstr "Erfolglose Verbindungen" #: windows/views.py:449 msgid "Last failure reason" msgstr "" #: windows/views.py:466 +#, fuzzy msgid "Total connection attempts" -msgstr "" +msgstr "Letzte Verbindung" #: windows/views.py:483 +#, fuzzy msgid " Average connection time (ms)" -msgstr "" +msgstr "Wiederverbindung fehlgeschlagen:" #: windows/views.py:500 +#, fuzzy msgid " Most recent connection duration" -msgstr "" +msgstr "Verbindungsmanager öffnen" #: windows/views.py:519 msgid "Statistics" -msgstr "" +msgstr "Statistiken" #: windows/views.py:537 windows/views.py:1672 msgid "Create" -msgstr "" +msgstr "Erstellen" #: windows/views.py:541 +#, fuzzy msgid "Create connection" -msgstr "" +msgstr "Letzte Verbindung" #: windows/views.py:544 +#, fuzzy msgid "Create directory" -msgstr "" +msgstr "Neues Verzeichnis" #: windows/views.py:573 windows/views.py:797 windows/views.py:1328 #: windows/views.py:1741 windows/views.py:2002 windows/views.py:2078 #: windows/views.py:3081 windows/views.py:3308 msgid "Cancel" -msgstr "" +msgstr "Abbrechen" #: windows/views.py:578 windows/views.py:2007 windows/views.py:3091 #: windows/views.py:3313 msgid "Save" -msgstr "" +msgstr "Speichern" #: windows/views.py:585 msgid "Test" -msgstr "" +msgstr "Testen" #: windows/views.py:592 msgid "Connect" -msgstr "" +msgstr "Verbinden" #: windows/views.py:695 msgid "Language" -msgstr "" +msgstr "Sprache" #: windows/views.py:700 msgid "English" -msgstr "" +msgstr "Englisch" #: windows/views.py:700 msgid "Italian" -msgstr "" +msgstr "Italienisch" #: windows/views.py:700 msgid "French" -msgstr "" +msgstr "Französisch" #: windows/views.py:712 msgid "Locale" -msgstr "" +msgstr "Lokale" #: windows/views.py:733 msgid "Edit Value" -msgstr "" +msgstr "Wert bearbeiten" #: windows/views.py:743 msgid "Syntax" -msgstr "" +msgstr "Syntax" #: windows/views.py:800 msgid "Ok" -msgstr "" +msgstr "Ok" #: windows/views.py:831 msgid "PeterSQL" -msgstr "" +msgstr "PeterSQL" #: windows/views.py:840 msgid "File" -msgstr "" +msgstr "Datei" #: windows/views.py:843 msgid "About" -msgstr "" +msgstr "Über" #: windows/views.py:846 msgid "Help" -msgstr "" +msgstr "Hilfe" #: windows/views.py:851 msgid "Open connection manager" -msgstr "" +msgstr "Verbindungsmanager öffnen" #: windows/views.py:853 msgid "Disconnect from server" -msgstr "" +msgstr "Vom Server trennen" #: windows/views.py:857 msgid "tool" -msgstr "" +msgstr "Werkzeug" #: windows/views.py:857 msgid "Refresh" -msgstr "" +msgstr "Aktualisieren" #: windows/views.py:861 windows/views.py:863 msgid "Add" -msgstr "" +msgstr "Hinzufügen" #: windows/views.py:897 windows/views.py:901 windows/views.py:2166 #: windows/views.py:2654 msgid "MyMenuItem" -msgstr "" +msgstr "MeinMenüElement" #: windows/views.py:904 windows/views.py:1769 windows/views.py:3336 msgid "MyMenu" -msgstr "" +msgstr "MeinMenü" #: windows/views.py:919 windows/views.py:1350 windows/views.py:1357 #: windows/views.py:1364 msgid "MyLabel" -msgstr "" +msgstr "MeinLabel" #: windows/views.py:925 msgid "Databases" -msgstr "" +msgstr "Datenbanken" #: windows/views.py:926 windows/views.py:1308 windows/views.py:2716 #: windows/views.py:2744 msgid "Size" -msgstr "" +msgstr "Größe" #: windows/views.py:927 msgid "Elements" -msgstr "" +msgstr "Elemente" #: windows/views.py:928 msgid "Modified at" -msgstr "" +msgstr "Geändert am" #: windows/views.py:929 msgid "Tables" -msgstr "" +msgstr "Tabellen" #: windows/views.py:936 msgid "System" -msgstr "" +msgstr "System" #: windows/views.py:979 +#, fuzzy msgid "Character set" -msgstr "" +msgstr "Erstellt am" #: windows/components/dataview.py:43 windows/components/dataview.py:67 #: windows/components/dataview.py:89 windows/views.py:1000 #: windows/views.py:1312 windows/views.py:2980 msgid "Collation" -msgstr "" +msgstr "Sortierung" #: windows/views.py:1026 windows/views.py:2862 msgid "Encryption" @@ -362,24 +393,29 @@ msgid "Read Only" msgstr "" #: windows/views.py:1055 +#, fuzzy msgid "Tablespace" -msgstr "" +msgstr "Tabellen" #: windows/views.py:1076 +#, fuzzy msgid "Connection limit" -msgstr "" +msgstr "Verbindung verloren" #: windows/views.py:1119 +#, fuzzy msgid "Profile" -msgstr "" +msgstr "Datei" #: windows/views.py:1145 +#, fuzzy msgid "Default tablespace" -msgstr "" +msgstr "Tabelle löschen" #: windows/views.py:1166 +#, fuzzy msgid "Temporary tablespace" -msgstr "" +msgstr "Temporär" #: windows/views.py:1192 msgid "Quota" @@ -394,108 +430,110 @@ msgid "Account status" msgstr "" #: windows/views.py:1249 +#, fuzzy msgid "Password expire" -msgstr "" +msgstr "Passwort" #: windows/views.py:1270 msgid "Table:" -msgstr "" +msgstr "Tabelle:" #: windows/views.py:1278 windows/views.py:1551 windows/views.py:1595 #: windows/views.py:1701 windows/views.py:3268 msgid "Insert" -msgstr "" +msgstr "Einfügen" #: windows/views.py:1283 msgid "Clone" -msgstr "" +msgstr "Klonen" #: windows/views.py:1307 msgid "Rows" -msgstr "" +msgstr "Zeilen" #: windows/views.py:1310 windows/views.py:2746 msgid "Updated at" -msgstr "" +msgstr "Aktualisiert am" #: windows/views.py:1334 windows/views.py:1746 windows/views.py:2085 #: windows/views.py:2140 msgid "Apply" -msgstr "" +msgstr "Anwenden" #: windows/views.py:1344 windows/views.py:1503 windows/views.py:1956 #: windows/views.py:3225 msgid "Options" -msgstr "" +msgstr "Optionen" #: windows/views.py:1375 msgid "Diagram" -msgstr "" +msgstr "Diagramm" #: windows/views.py:1386 windows/views.py:2715 msgid "Database" -msgstr "" +msgstr "Datenbank" #: windows/views.py:1441 windows/views.py:3165 msgid "Base" -msgstr "" +msgstr "Basis" #: windows/views.py:1455 windows/views.py:3179 msgid "Auto Increment" -msgstr "" +msgstr "Auto Inkrement" #: windows/views.py:1483 windows/views.py:3207 msgid "Default Collation" -msgstr "" +msgstr "Standard-Sortierung" #: windows/views.py:1515 windows/views.py:1556 windows/views.py:1600 msgid "Remove" -msgstr "" +msgstr "Entfernen" #: windows/views.py:1522 windows/views.py:1563 windows/views.py:1607 msgid "Clear" -msgstr "" +msgstr "Löschen" #: windows/views.py:1537 windows/views.py:3239 msgid "Indexes" -msgstr "" +msgstr "Indizes" #: windows/views.py:1581 msgid "Foreign Keys" -msgstr "" +msgstr "Fremdschlüssel" #: windows/views.py:1625 msgid "Checks" -msgstr "" +msgstr "Prüfungen" #: windows/views.py:1693 windows/views.py:3260 msgid "Columns:" -msgstr "" +msgstr "Spalten:" #: windows/views.py:1713 windows/views.py:3280 msgid "Up" -msgstr "" +msgstr "Hoch" #: windows/views.py:1720 windows/views.py:3287 msgid "Down" -msgstr "" +msgstr "Runter" #: windows/views.py:1759 windows/views.py:1766 windows/views.py:3326 #: windows/views.py:3333 msgid "Add Index" -msgstr "" +msgstr "Index hinzufügen" #: windows/views.py:1763 windows/views.py:3330 msgid "Add PrimaryKey" -msgstr "" +msgstr "Primärschlüssel hinzufügen" #: windows/views.py:1780 msgid "Table" -msgstr "" +msgstr "Tabelle" #: windows/views.py:1816 +#, fuzzy msgid "Definer" -msgstr "" +msgstr "Einfügen" #: windows/views.py:1836 msgid "Schema" @@ -506,12 +544,14 @@ msgid "SQL security" msgstr "" #: windows/views.py:1869 +#, fuzzy msgid "DEFINER" -msgstr "" +msgstr "Einfügen" #: windows/views.py:1869 +#, fuzzy msgid "INVOKER" -msgstr "" +msgstr "Einfügen" #: windows/views.py:1881 windows/views.py:2558 windows/views.py:2577 #: windows/views.py:2820 @@ -520,8 +560,9 @@ msgstr "" #: windows/views.py:1883 windows/views.py:2543 windows/views.py:2576 #: windows/views.py:2825 +#, fuzzy msgid "UNDEFINED" -msgstr "" +msgstr "Ohne Vorzeichen" #: windows/views.py:1886 windows/views.py:2546 windows/views.py:2576 #: windows/views.py:2828 @@ -530,28 +571,33 @@ msgstr "" #: windows/views.py:1889 windows/views.py:2555 windows/views.py:2576 #: windows/views.py:2831 +#, fuzzy msgid "TEMPTABLE" -msgstr "" +msgstr "Tabelle" #: windows/views.py:1899 windows/views.py:2582 msgid "View constraint" msgstr "" #: windows/views.py:1901 windows/views.py:2581 +#, fuzzy msgid "None" -msgstr "" +msgstr "Klonen" #: windows/views.py:1904 windows/views.py:2581 +#, fuzzy msgid "LOCAL" -msgstr "" +msgstr "Lokale" #: windows/views.py:1907 +#, fuzzy msgid "CASCADE" -msgstr "" +msgstr "Abbrechen" #: windows/views.py:1910 +#, fuzzy msgid "CHECK ONLY" -msgstr "" +msgstr "Prüfen" #: windows/views.py:1913 windows/views.py:2581 msgid "READ ONLY" @@ -567,82 +613,86 @@ msgstr "" #: windows/views.py:2019 msgid "Views" -msgstr "" +msgstr "Ansichten" #: windows/views.py:2027 msgid "Triggers" -msgstr "" +msgstr "Trigger" #: windows/views.py:2039 #, python-format msgid "Table `%(database_name)s`.`%(table_name)s`: %(total_rows) rows total" msgstr "" +"Tabelle `%(database_name)s`.`%(table_name)s`: %(total_rows) Zeilen " +"insgesamt" #: windows/views.py:2049 msgid "Insert record" -msgstr "" +msgstr "Datensatz einfügen" #: windows/views.py:2054 msgid "Duplicate record" -msgstr "" +msgstr "Datensatz duplizieren" #: windows/views.py:2061 msgid "Delete record" -msgstr "" +msgstr "Datensatz löschen" #: windows/views.py:2071 msgid "Apply changes automatically" -msgstr "" +msgstr "Änderungen automatisch anwenden" #: windows/views.py:2073 windows/views.py:2074 msgid "" "If enabled, table edits are applied immediately without pressing Apply or" " Cancel" msgstr "" +"Wenn aktiviert, werden Tabellenbearbeitungen sofort angewendet, ohne auf " +"Anwenden oder Abbrechen zu drücken" #: windows/views.py:2095 msgid "Next" -msgstr "" +msgstr "Weiter" #: windows/views.py:2103 msgid "Filters" -msgstr "" +msgstr "Filter" #: windows/views.py:2143 msgid "CTRL+ENTER" -msgstr "" +msgstr "CTRL+ENTER" #: windows/views.py:2163 msgid "Insert row" -msgstr "" +msgstr "Zeile einfügen" #: windows/views.py:2171 msgid "Data" -msgstr "" +msgstr "Daten" #: windows/views.py:2225 windows/views.py:2275 msgid "New" -msgstr "" +msgstr "Neu" #: windows/views.py:2252 msgid "Query" -msgstr "" +msgstr "Abfrage" #: windows/views.py:2272 msgid "Close" -msgstr "" +msgstr "Schließen" #: windows/views.py:2285 msgid "Query #2" -msgstr "" +msgstr "Abfrage #2" #: windows/views.py:2537 msgid "Column5" -msgstr "" +msgstr "Spalte5" #: windows/views.py:2548 msgid "Import" -msgstr "" +msgstr "Importieren" #: windows/views.py:2573 msgid "Read only" @@ -653,55 +703,59 @@ msgid "CASCADED" msgstr "" #: windows/views.py:2581 +#, fuzzy msgid "CHECK OPTION" -msgstr "" +msgstr "Verbindung" #: windows/views.py:2613 msgid "collapsible" -msgstr "" +msgstr "zusammenklappbar" #: windows/views.py:2629 msgid "Column3" -msgstr "" +msgstr "Spalte3" #: windows/views.py:2630 msgid "Column4" -msgstr "" +msgstr "Spalte4" #: windows/views.py:2673 msgid "" "Database " "(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" msgstr "" +"Database " +"(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" #: windows/views.py:2685 msgid "Port" -msgstr "" +msgstr "Port" #: windows/views.py:2708 msgid "Usage" -msgstr "" +msgstr "Verwendung" #: windows/views.py:2719 #, python-format msgid "%(total_rows)s" -msgstr "" +msgstr "%(total_rows)s" #: windows/views.py:2724 msgid "rows total" -msgstr "" +msgstr "Zeilen insgesamt" #: windows/views.py:2743 msgid "Lines" -msgstr "" +msgstr "Zeilen" #: windows/views.py:2775 msgid "Temporary" -msgstr "" +msgstr "Temporär" #: windows/views.py:2786 +#, fuzzy msgid "Engine options" -msgstr "" +msgstr "Optionen" #: windows/views.py:2845 msgid "RadioBtn" @@ -709,136 +763,136 @@ msgstr "" #: windows/views.py:2928 msgid "Edit Column" -msgstr "" +msgstr "Spalte bearbeiten" #: windows/views.py:2944 msgid "Datatype" -msgstr "" +msgstr "Datentyp" #: windows/components/dataview.py:121 windows/views.py:2959 msgid "Length/Set" -msgstr "" +msgstr "Länge/Menge" #: windows/components/dataview.py:51 windows/views.py:2998 msgid "Unsigned" -msgstr "" +msgstr "Ohne Vorzeichen" #: windows/components/dataview.py:25 windows/components/dataview.py:52 #: windows/components/dataview.py:75 windows/views.py:3004 msgid "Allow NULL" -msgstr "" +msgstr "NULL erlauben" #: windows/views.py:3010 msgid "Zero Fill" -msgstr "" +msgstr "Nullfüllung" #: windows/components/dataview.py:32 windows/components/dataview.py:56 #: windows/components/dataview.py:78 windows/views.py:3021 msgid "Default" -msgstr "" +msgstr "Standard" #: windows/components/dataview.py:36 windows/components/dataview.py:60 #: windows/components/dataview.py:82 windows/views.py:3047 msgid "Virtuality" -msgstr "" +msgstr "Virtualität" #: windows/components/dataview.py:39 windows/components/dataview.py:63 #: windows/components/dataview.py:85 windows/components/dataview.py:241 #: windows/views.py:3062 msgid "Expression" -msgstr "" +msgstr "Ausdruck" #: windows/components/dataview.py:28 msgid "Check" -msgstr "" +msgstr "Prüfen" #: windows/components/dataview.py:53 msgid "Zerofill" -msgstr "" +msgstr "Nullfüllung" #: windows/components/dataview.py:109 msgid "#" -msgstr "" +msgstr "#" #: windows/components/dataview.py:117 msgid "Data type" -msgstr "" +msgstr "Datentyp" #: windows/components/dataview.py:155 msgid "Add column\tCTRL+INS" -msgstr "" +msgstr "Spalte hinzufügen\tCTRL+INS" #: windows/components/dataview.py:161 msgid "Remove column\tCTRL+DEL" -msgstr "" +msgstr "Spalte entfernen\tCTRL+DEL" #: windows/components/dataview.py:169 msgid "Move up\tCTRL+UP" -msgstr "" +msgstr "Nach oben bewegen\tCTRL+UP" #: windows/components/dataview.py:176 msgid "Move down\tCTRL+D" -msgstr "" +msgstr "Nach unten bewegen\tCTRL+D" #: windows/components/dataview.py:199 msgid "Create new index" -msgstr "" +msgstr "Neuen Index erstellen" #: windows/components/dataview.py:214 msgid "Append to index" -msgstr "" +msgstr "An Index anhängen" #: windows/components/dataview.py:228 msgid "Column(s)/Expression" -msgstr "" +msgstr "Spalte(n)/Ausdruck" #: windows/components/dataview.py:229 msgid "Condition" -msgstr "" +msgstr "Bedingung" #: windows/components/dataview.py:259 msgid "Column(s)" -msgstr "" +msgstr "Spalte(n)" #: windows/components/dataview.py:265 msgid "Reference table" -msgstr "" +msgstr "Referenztabelle" #: windows/components/dataview.py:271 msgid "Reference column(s)" -msgstr "" +msgstr "Referenzspalte(n)" #: windows/components/dataview.py:277 msgid "On UPDATE" -msgstr "" +msgstr "Bei UPDATE" #: windows/components/dataview.py:283 msgid "On DELETE" -msgstr "" +msgstr "Bei DELETE" #: windows/components/dataview.py:299 msgid "Add foreign key" -msgstr "" +msgstr "Fremdschlüssel hinzufügen" #: windows/components/dataview.py:305 msgid "Remove foreign key" -msgstr "" +msgstr "Fremdschlüssel entfernen" #: windows/components/popup.py:26 msgid "No default value" -msgstr "" +msgstr "Kein Standardwert" #: windows/components/popup.py:31 msgid "NULL" -msgstr "" +msgstr "NULL" #: windows/components/popup.py:35 msgid "AUTO INCREMENT" -msgstr "" +msgstr "AUTO INCREMENT" #: windows/components/popup.py:39 msgid "Text/Expression" -msgstr "" +msgstr "Text/Ausdruck" #: windows/dialogs/connections/view.py:119 windows/main/tabs/query.py:376 msgid "Unknown error" @@ -850,7 +904,7 @@ msgstr "" #: windows/dialogs/connections/view.py:407 msgid "Confirm save" -msgstr "" +msgstr "Speichern bestätigen" #: windows/dialogs/connections/view.py:459 msgid "You have unsaved changes. Do you want to save them before continuing?" @@ -868,33 +922,33 @@ msgstr "" #: windows/dialogs/connections/view.py:762 msgid "Connection error" -msgstr "" +msgstr "Verbindungsfehler" #: windows/dialogs/connections/view.py:788 #: windows/dialogs/connections/view.py:803 msgid "Confirm delete" -msgstr "" +msgstr "Löschen bestätigen" #: windows/main/controller.py:172 msgid "days" -msgstr "" +msgstr "Tage" #: windows/main/controller.py:173 msgid "hours" -msgstr "" +msgstr "Stunden" #: windows/main/controller.py:174 msgid "minutes" -msgstr "" +msgstr "Minuten" #: windows/main/controller.py:175 msgid "seconds" -msgstr "" +msgstr "Sekunden" #: windows/main/controller.py:183 #, python-brace-format msgid "Memory used: {used} ({percentage:.2%})" -msgstr "" +msgstr "Verwendeter Speicher: {used} ({percentage:.2%})" #: windows/main/controller.py:219 msgid "Settings saved successfully" @@ -902,11 +956,11 @@ msgstr "" #: windows/main/controller.py:298 msgid "Version" -msgstr "" +msgstr "Version" #: windows/main/controller.py:300 msgid "Uptime" -msgstr "" +msgstr "Betriebszeit" #: windows/main/controller.py:399 #, python-brace-format @@ -919,8 +973,9 @@ msgid "" msgstr "" #: windows/main/controller.py:404 windows/main/controller.py:425 +#, fuzzy msgid "Delete database" -msgstr "" +msgstr "Tabelle löschen" #: windows/main/controller.py:410 msgid "Dump is not implemented yet. No action has been performed." @@ -945,32 +1000,32 @@ msgstr "" #: windows/main/controller.py:582 msgid "Delete table" -msgstr "" +msgstr "Tabelle löschen" #: windows/main/controller.py:699 msgid "Do you want delete the records?" -msgstr "" +msgstr "Möchten Sie die Datensätze löschen?" #: windows/main/tabs/database.py:71 msgid "The connection to the database was lost." -msgstr "" +msgstr "Die Verbindung zur Datenbank wurde verloren." #: windows/main/tabs/database.py:73 msgid "Do you want to reconnect?" -msgstr "" +msgstr "Möchten Sie erneut verbinden?" #: windows/main/tabs/database.py:75 msgid "Connection lost" -msgstr "" +msgstr "Verbindung verloren" #: windows/main/tabs/database.py:85 msgid "Reconnection failed:" -msgstr "" +msgstr "Wiederverbindung fehlgeschlagen:" #: windows/main/tabs/database.py:86 windows/main/tabs/query.py:450 #: windows/main/tabs/view.py:256 windows/main/tabs/view.py:282 msgid "Error" -msgstr "" +msgstr "Fehler" #: windows/main/tabs/query.py:305 #, python-brace-format @@ -978,9 +1033,9 @@ msgid "{} rows affected" msgstr "" #: windows/main/tabs/query.py:309 windows/main/tabs/query.py:331 -#, python-brace-format +#, fuzzy, python-brace-format msgid "Query {}" -msgstr "" +msgstr "Abfrage" #: windows/main/tabs/query.py:314 #, python-brace-format @@ -993,9 +1048,9 @@ msgid "Query {} ({} rows × {} cols)" msgstr "" #: windows/main/tabs/query.py:353 -#, python-brace-format +#, fuzzy, python-brace-format msgid "{} rows" -msgstr "" +msgstr "Zeilen" #: windows/main/tabs/query.py:355 #, python-brace-format @@ -1008,12 +1063,14 @@ msgid "{} warnings" msgstr "" #: windows/main/tabs/query.py:370 +#, fuzzy msgid "Error:" -msgstr "" +msgstr "Fehler" #: windows/main/tabs/query.py:449 +#, fuzzy msgid "No active database connection" -msgstr "" +msgstr "Neue Verbindung" #: windows/main/tabs/view.py:252 msgid "View created successfully" @@ -1034,8 +1091,9 @@ msgid "Are you sure you want to delete view '{}'?" msgstr "" #: windows/main/tabs/view.py:270 +#, fuzzy msgid "Confirm Delete" -msgstr "" +msgstr "Löschen bestätigen" #: windows/main/tabs/view.py:279 msgid "View deleted successfully" @@ -1046,3 +1104,39 @@ msgstr "" msgid "Error deleting view: {}" msgstr "" +#~ msgid "Created at:" +#~ msgstr "" + +#~ msgid "Last connection:" +#~ msgstr "" + +#~ msgid "Successful connections:" +#~ msgstr "" + +#~ msgid "Unsuccessful connections:" +#~ msgstr "" + +#~ msgid "Session Manager" +#~ msgstr "" + +#~ msgid "Session name" +#~ msgstr "" + +#~ msgid "Connection type" +#~ msgstr "" + +#~ msgid "Open" +#~ msgstr "" + +#~ msgid "Open session manager" +#~ msgstr "" + +#~ msgid "Foreign Key" +#~ msgstr "" + +#~ msgid "New Session" +#~ msgstr "Neue Sitzung" + +#~ msgid "directory" +#~ msgstr "Verzeichnis" + diff --git a/locale/en_US/LC_MESSAGES/petersql.po b/locale/en_US/LC_MESSAGES/petersql.po index da6114d..53b78b6 100644 --- a/locale/en_US/LC_MESSAGES/petersql.po +++ b/locale/en_US/LC_MESSAGES/petersql.po @@ -1,3 +1,23 @@ +# English (en_US) translations for PeterSQL. +# Copyright (C) 2026 ORGANIZATION +# This file is distributed under the same license as the PeterSQL project. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PeterSQL 0.1.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2026-03-10 18:23+0100\n" +"PO-Revision-Date: 2026-03-10 18:21+0000\n" +"Last-Translator: \n" +"Language: en_US\n" +"Language-Team: en_US \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.18.0\n" + #: helpers/__init__.py:16 msgctxt "unit" msgid "B" @@ -1046,3 +1066,42 @@ msgstr "" msgid "Error deleting view: {}" msgstr "" +#~ msgid "Created at:" +#~ msgstr "" + +#~ msgid "Last connection:" +#~ msgstr "" + +#~ msgid "Successful connections:" +#~ msgstr "" + +#~ msgid "Unsuccessful connections:" +#~ msgstr "" + +#~ msgid "Session Manager" +#~ msgstr "" + +#~ msgid "Session name" +#~ msgstr "" + +#~ msgid "Connection type" +#~ msgstr "" + +#~ msgid "Open" +#~ msgstr "" + +#~ msgid "Open session manager" +#~ msgstr "" + +#~ msgid "Foreign Key" +#~ msgstr "" + +#~ msgid "New Session" +#~ msgstr "" + +#~ msgid "connection" +#~ msgstr "" + +#~ msgid "directory" +#~ msgstr "" + diff --git a/locale/es_ES/LC_MESSAGES/petersql.po b/locale/es_ES/LC_MESSAGES/petersql.po index da6114d..435a889 100644 --- a/locale/es_ES/LC_MESSAGES/petersql.po +++ b/locale/es_ES/LC_MESSAGES/petersql.po @@ -1,37 +1,57 @@ +# Español (es_ES) translations for PeterSQL. +# Copyright (C) 2026 ORGANIZATION +# This file is distributed under the same license as the PeterSQL project. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PeterSQL 0.1.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2026-03-10 18:23+0100\n" +"PO-Revision-Date: 2026-03-10 18:21+0000\n" +"Last-Translator: \n" +"Language: es_ES\n" +"Language-Team: es_ES \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.18.0\n" + #: helpers/__init__.py:16 msgctxt "unit" msgid "B" -msgstr "" +msgstr "B" #: helpers/__init__.py:17 msgctxt "unit" msgid "KB" -msgstr "" +msgstr "KB" #: helpers/__init__.py:18 msgctxt "unit" msgid "MB" -msgstr "" +msgstr "MB" #: helpers/__init__.py:19 msgctxt "unit" msgid "GB" -msgstr "" +msgstr "GB" #: helpers/__init__.py:20 msgctxt "unit" msgid "TB" -msgstr "" +msgstr "TB" #: structures/ssh_tunnel.py:166 msgid "OpenSSH client not found." -msgstr "" +msgstr "Cliente OpenSSH no encontrado." #: windows/dialogs/connections/view.py:395 #: windows/dialogs/connections/view.py:738 windows/main/controller.py:296 #: windows/views.py:33 msgid "Connection" -msgstr "" +msgstr "Conexión" #: windows/components/dataview.py:113 windows/components/dataview.py:225 #: windows/components/dataview.py:238 windows/components/dataview.py:253 @@ -43,51 +63,53 @@ msgstr "" #: windows/views.py:2738 windows/views.py:2742 windows/views.py:2936 #: windows/views.py:3137 msgid "Name" -msgstr "" +msgstr "Nombre" #: windows/views.py:48 windows/views.py:381 msgid "Last connection" -msgstr "" +msgstr "Última conexión" #: windows/dialogs/connections/view.py:631 windows/views.py:61 msgid "New directory" -msgstr "" +msgstr "Nuevo directorio" #: windows/dialogs/connections/model.py:187 #: windows/dialogs/connections/view.py:591 windows/views.py:65 msgid "New connection" -msgstr "" +msgstr "Nueva conexión" #: windows/views.py:71 +#, fuzzy msgid "Rename" -msgstr "" +msgstr "Nombre" #: windows/views.py:76 +#, fuzzy msgid "Clone connection" -msgstr "" +msgstr "Nueva conexión" #: windows/views.py:81 windows/views.py:556 windows/views.py:1290 #: windows/views.py:1331 windows/views.py:1706 windows/views.py:1738 #: windows/views.py:1997 windows/views.py:3273 windows/views.py:3305 msgid "Delete" -msgstr "" +msgstr "Eliminar" #: windows/views.py:111 windows/views.py:1311 windows/views.py:1468 #: windows/views.py:2747 windows/views.py:3192 msgid "Engine" -msgstr "" +msgstr "Motor" #: windows/views.py:132 msgid "Host + port" -msgstr "" +msgstr "Host + puerto" #: windows/views.py:148 msgid "Username" -msgstr "" +msgstr "Nombre de usuario" #: windows/views.py:161 windows/views.py:1100 msgid "Password" -msgstr "" +msgstr "Contraseña" #: windows/views.py:177 msgid "Use TLS" @@ -95,43 +117,44 @@ msgstr "" #: windows/views.py:180 msgid "Use SSH tunnel" -msgstr "" +msgstr "Usar túnel SSH" #: windows/views.py:202 windows/views.py:2668 msgid "Filename" -msgstr "" +msgstr "Nombre de archivo" #: windows/views.py:207 windows/views.py:324 windows/views.py:2673 #: windows/views.py:2856 msgid "Select a file" -msgstr "" +msgstr "Seleccionar un archivo" #: windows/views.py:207 windows/views.py:324 windows/views.py:2856 +#, fuzzy msgid "*.*" -msgstr "" +msgstr "*. *" #: windows/components/dataview.py:70 windows/components/dataview.py:92 #: windows/views.py:221 windows/views.py:1313 windows/views.py:1426 #: windows/views.py:2748 windows/views.py:3034 windows/views.py:3150 msgid "Comments" -msgstr "" +msgstr "Comentarios" #: windows/main/controller.py:219 windows/views.py:235 windows/views.py:683 #: windows/views.py:837 msgid "Settings" -msgstr "" +msgstr "Configuraciones" #: windows/views.py:244 msgid "SSH executable" -msgstr "" +msgstr "Ejecutable SSH" #: windows/views.py:249 msgid "ssh" -msgstr "" +msgstr "ssh" #: windows/views.py:257 msgid "SSH host + port" -msgstr "" +msgstr "Host SSH + puerto" #: windows/views.py:269 msgid "SSH host + port (the SSH server that forwards traffic to the DB)" @@ -139,27 +162,28 @@ msgstr "" #: windows/views.py:278 msgid "SSH username" -msgstr "" +msgstr "Nombre de usuario SSH" #: windows/views.py:291 msgid "SSH password" -msgstr "" +msgstr "Contraseña SSH" #: windows/views.py:304 msgid "Local port" -msgstr "" +msgstr "Puerto local" #: windows/views.py:310 msgid "if the value is set to 0, the first available port will be used" -msgstr "" +msgstr "si el valor se establece en 0, se utilizará el primer puerto disponible" #: windows/views.py:319 msgid "Identity file" msgstr "" #: windows/views.py:335 +#, fuzzy msgid "Remote host + port" -msgstr "" +msgstr "Host + puerto" #: windows/views.py:347 msgid "Remote host/port is the real DB target (defaults to DB Host/Port)." @@ -167,191 +191,198 @@ msgstr "" #: windows/views.py:358 msgid "SSH Tunnel" -msgstr "" +msgstr "Túnel SSH" #: windows/views.py:364 windows/views.py:1309 windows/views.py:2745 msgid "Created at" -msgstr "" +msgstr "Creado en" #: windows/views.py:398 msgid "Successful connections" -msgstr "" +msgstr "Conexiones exitosas" #: windows/views.py:415 +#, fuzzy msgid "Last successful connection" -msgstr "" +msgstr "Conexiones exitosas" #: windows/views.py:432 msgid "Unsuccessful connections" -msgstr "" +msgstr "Conexiones fallidas" #: windows/views.py:449 msgid "Last failure reason" msgstr "" #: windows/views.py:466 +#, fuzzy msgid "Total connection attempts" -msgstr "" +msgstr "Última conexión" #: windows/views.py:483 +#, fuzzy msgid " Average connection time (ms)" -msgstr "" +msgstr "Reconexión fallida:" #: windows/views.py:500 +#, fuzzy msgid " Most recent connection duration" -msgstr "" +msgstr "Abrir administrador de conexiones" #: windows/views.py:519 msgid "Statistics" -msgstr "" +msgstr "Estadísticas" #: windows/views.py:537 windows/views.py:1672 msgid "Create" -msgstr "" +msgstr "Crear" #: windows/views.py:541 +#, fuzzy msgid "Create connection" -msgstr "" +msgstr "Última conexión" #: windows/views.py:544 +#, fuzzy msgid "Create directory" -msgstr "" +msgstr "Nuevo directorio" #: windows/views.py:573 windows/views.py:797 windows/views.py:1328 #: windows/views.py:1741 windows/views.py:2002 windows/views.py:2078 #: windows/views.py:3081 windows/views.py:3308 msgid "Cancel" -msgstr "" +msgstr "Cancelar" #: windows/views.py:578 windows/views.py:2007 windows/views.py:3091 #: windows/views.py:3313 msgid "Save" -msgstr "" +msgstr "Guardar" #: windows/views.py:585 msgid "Test" -msgstr "" +msgstr "Probar" #: windows/views.py:592 msgid "Connect" -msgstr "" +msgstr "Conectar" #: windows/views.py:695 msgid "Language" -msgstr "" +msgstr "Idioma" #: windows/views.py:700 msgid "English" -msgstr "" +msgstr "Inglés" #: windows/views.py:700 msgid "Italian" -msgstr "" +msgstr "Italiano" #: windows/views.py:700 msgid "French" -msgstr "" +msgstr "Francés" #: windows/views.py:712 msgid "Locale" -msgstr "" +msgstr "Localización" #: windows/views.py:733 msgid "Edit Value" -msgstr "" +msgstr "Editar valor" #: windows/views.py:743 msgid "Syntax" -msgstr "" +msgstr "Sintaxis" #: windows/views.py:800 msgid "Ok" -msgstr "" +msgstr "Ok" #: windows/views.py:831 msgid "PeterSQL" -msgstr "" +msgstr "PeterSQL" #: windows/views.py:840 msgid "File" -msgstr "" +msgstr "Archivo" #: windows/views.py:843 msgid "About" -msgstr "" +msgstr "Acerca de" #: windows/views.py:846 msgid "Help" -msgstr "" +msgstr "Ayuda" #: windows/views.py:851 msgid "Open connection manager" -msgstr "" +msgstr "Abrir administrador de conexiones" #: windows/views.py:853 msgid "Disconnect from server" -msgstr "" +msgstr "Desconectar del servidor" #: windows/views.py:857 msgid "tool" -msgstr "" +msgstr "herramienta" #: windows/views.py:857 msgid "Refresh" -msgstr "" +msgstr "Actualizar" #: windows/views.py:861 windows/views.py:863 msgid "Add" -msgstr "" +msgstr "Agregar" #: windows/views.py:897 windows/views.py:901 windows/views.py:2166 #: windows/views.py:2654 msgid "MyMenuItem" -msgstr "" +msgstr "MiElementoMenu" #: windows/views.py:904 windows/views.py:1769 windows/views.py:3336 msgid "MyMenu" -msgstr "" +msgstr "MiMenu" #: windows/views.py:919 windows/views.py:1350 windows/views.py:1357 #: windows/views.py:1364 msgid "MyLabel" -msgstr "" +msgstr "MiEtiqueta" #: windows/views.py:925 msgid "Databases" -msgstr "" +msgstr "Bases de datos" #: windows/views.py:926 windows/views.py:1308 windows/views.py:2716 #: windows/views.py:2744 msgid "Size" -msgstr "" +msgstr "Tamaño" #: windows/views.py:927 msgid "Elements" -msgstr "" +msgstr "Elementos" #: windows/views.py:928 msgid "Modified at" -msgstr "" +msgstr "Modificado en" #: windows/views.py:929 msgid "Tables" -msgstr "" +msgstr "Tablas" #: windows/views.py:936 msgid "System" -msgstr "" +msgstr "Sistema" #: windows/views.py:979 +#, fuzzy msgid "Character set" -msgstr "" +msgstr "Creado en" #: windows/components/dataview.py:43 windows/components/dataview.py:67 #: windows/components/dataview.py:89 windows/views.py:1000 #: windows/views.py:1312 windows/views.py:2980 msgid "Collation" -msgstr "" +msgstr "Intercalación" #: windows/views.py:1026 windows/views.py:2862 msgid "Encryption" @@ -362,24 +393,29 @@ msgid "Read Only" msgstr "" #: windows/views.py:1055 +#, fuzzy msgid "Tablespace" -msgstr "" +msgstr "Tablas" #: windows/views.py:1076 +#, fuzzy msgid "Connection limit" -msgstr "" +msgstr "Conexión perdida" #: windows/views.py:1119 +#, fuzzy msgid "Profile" -msgstr "" +msgstr "Archivo" #: windows/views.py:1145 +#, fuzzy msgid "Default tablespace" -msgstr "" +msgstr "Eliminar tabla" #: windows/views.py:1166 +#, fuzzy msgid "Temporary tablespace" -msgstr "" +msgstr "Temporal" #: windows/views.py:1192 msgid "Quota" @@ -394,108 +430,110 @@ msgid "Account status" msgstr "" #: windows/views.py:1249 +#, fuzzy msgid "Password expire" -msgstr "" +msgstr "Contraseña" #: windows/views.py:1270 msgid "Table:" -msgstr "" +msgstr "Tabla:" #: windows/views.py:1278 windows/views.py:1551 windows/views.py:1595 #: windows/views.py:1701 windows/views.py:3268 msgid "Insert" -msgstr "" +msgstr "Insertar" #: windows/views.py:1283 msgid "Clone" -msgstr "" +msgstr "Clonar" #: windows/views.py:1307 msgid "Rows" -msgstr "" +msgstr "Filas" #: windows/views.py:1310 windows/views.py:2746 msgid "Updated at" -msgstr "" +msgstr "Actualizado en" #: windows/views.py:1334 windows/views.py:1746 windows/views.py:2085 #: windows/views.py:2140 msgid "Apply" -msgstr "" +msgstr "Aplicar" #: windows/views.py:1344 windows/views.py:1503 windows/views.py:1956 #: windows/views.py:3225 msgid "Options" -msgstr "" +msgstr "Opciones" #: windows/views.py:1375 msgid "Diagram" -msgstr "" +msgstr "Diagrama" #: windows/views.py:1386 windows/views.py:2715 msgid "Database" -msgstr "" +msgstr "Base de datos" #: windows/views.py:1441 windows/views.py:3165 msgid "Base" -msgstr "" +msgstr "Base" #: windows/views.py:1455 windows/views.py:3179 msgid "Auto Increment" -msgstr "" +msgstr "Auto incremento" #: windows/views.py:1483 windows/views.py:3207 msgid "Default Collation" -msgstr "" +msgstr "Intercalación predeterminada" #: windows/views.py:1515 windows/views.py:1556 windows/views.py:1600 msgid "Remove" -msgstr "" +msgstr "Eliminar" #: windows/views.py:1522 windows/views.py:1563 windows/views.py:1607 msgid "Clear" -msgstr "" +msgstr "Limpiar" #: windows/views.py:1537 windows/views.py:3239 msgid "Indexes" -msgstr "" +msgstr "Índices" #: windows/views.py:1581 msgid "Foreign Keys" -msgstr "" +msgstr "Claves foráneas" #: windows/views.py:1625 msgid "Checks" -msgstr "" +msgstr "Comprobaciones" #: windows/views.py:1693 windows/views.py:3260 msgid "Columns:" -msgstr "" +msgstr "Columnas:" #: windows/views.py:1713 windows/views.py:3280 msgid "Up" -msgstr "" +msgstr "Arriba" #: windows/views.py:1720 windows/views.py:3287 msgid "Down" -msgstr "" +msgstr "Abajo" #: windows/views.py:1759 windows/views.py:1766 windows/views.py:3326 #: windows/views.py:3333 msgid "Add Index" -msgstr "" +msgstr "Agregar índice" #: windows/views.py:1763 windows/views.py:3330 msgid "Add PrimaryKey" -msgstr "" +msgstr "Agregar clave primaria" #: windows/views.py:1780 msgid "Table" -msgstr "" +msgstr "Tabla" #: windows/views.py:1816 +#, fuzzy msgid "Definer" -msgstr "" +msgstr "Insertar" #: windows/views.py:1836 msgid "Schema" @@ -506,12 +544,14 @@ msgid "SQL security" msgstr "" #: windows/views.py:1869 +#, fuzzy msgid "DEFINER" -msgstr "" +msgstr "Insertar" #: windows/views.py:1869 +#, fuzzy msgid "INVOKER" -msgstr "" +msgstr "Insertar" #: windows/views.py:1881 windows/views.py:2558 windows/views.py:2577 #: windows/views.py:2820 @@ -520,8 +560,9 @@ msgstr "" #: windows/views.py:1883 windows/views.py:2543 windows/views.py:2576 #: windows/views.py:2825 +#, fuzzy msgid "UNDEFINED" -msgstr "" +msgstr "Sin signo" #: windows/views.py:1886 windows/views.py:2546 windows/views.py:2576 #: windows/views.py:2828 @@ -530,28 +571,33 @@ msgstr "" #: windows/views.py:1889 windows/views.py:2555 windows/views.py:2576 #: windows/views.py:2831 +#, fuzzy msgid "TEMPTABLE" -msgstr "" +msgstr "Tabla" #: windows/views.py:1899 windows/views.py:2582 msgid "View constraint" msgstr "" #: windows/views.py:1901 windows/views.py:2581 +#, fuzzy msgid "None" -msgstr "" +msgstr "Clonar" #: windows/views.py:1904 windows/views.py:2581 +#, fuzzy msgid "LOCAL" -msgstr "" +msgstr "Localización" #: windows/views.py:1907 +#, fuzzy msgid "CASCADE" -msgstr "" +msgstr "Cancelar" #: windows/views.py:1910 +#, fuzzy msgid "CHECK ONLY" -msgstr "" +msgstr "Verificar" #: windows/views.py:1913 windows/views.py:2581 msgid "READ ONLY" @@ -567,82 +613,84 @@ msgstr "" #: windows/views.py:2019 msgid "Views" -msgstr "" +msgstr "Vistas" #: windows/views.py:2027 msgid "Triggers" -msgstr "" +msgstr "Disparadores" #: windows/views.py:2039 #, python-format msgid "Table `%(database_name)s`.`%(table_name)s`: %(total_rows) rows total" -msgstr "" +msgstr "Tabla `%(database_name)s`.`%(table_name)s`: %(total_rows) filas en total" #: windows/views.py:2049 msgid "Insert record" -msgstr "" +msgstr "Insertar registro" #: windows/views.py:2054 msgid "Duplicate record" -msgstr "" +msgstr "Duplicar registro" #: windows/views.py:2061 msgid "Delete record" -msgstr "" +msgstr "Eliminar registro" #: windows/views.py:2071 msgid "Apply changes automatically" -msgstr "" +msgstr "Aplicar cambios automáticamente" #: windows/views.py:2073 windows/views.py:2074 msgid "" "If enabled, table edits are applied immediately without pressing Apply or" " Cancel" msgstr "" +"Si está habilitado, las ediciones de la tabla se aplican inmediatamente " +"sin presionar Aplicar o Cancelar" #: windows/views.py:2095 msgid "Next" -msgstr "" +msgstr "Siguiente" #: windows/views.py:2103 msgid "Filters" -msgstr "" +msgstr "Filtros" #: windows/views.py:2143 msgid "CTRL+ENTER" -msgstr "" +msgstr "CTRL+ENTER" #: windows/views.py:2163 msgid "Insert row" -msgstr "" +msgstr "Insertar fila" #: windows/views.py:2171 msgid "Data" -msgstr "" +msgstr "Datos" #: windows/views.py:2225 windows/views.py:2275 msgid "New" -msgstr "" +msgstr "Nuevo" #: windows/views.py:2252 msgid "Query" -msgstr "" +msgstr "Consulta" #: windows/views.py:2272 msgid "Close" -msgstr "" +msgstr "Cerrar" #: windows/views.py:2285 msgid "Query #2" -msgstr "" +msgstr "Consulta #2" #: windows/views.py:2537 msgid "Column5" -msgstr "" +msgstr "Columna5" #: windows/views.py:2548 msgid "Import" -msgstr "" +msgstr "Importar" #: windows/views.py:2573 msgid "Read only" @@ -653,55 +701,59 @@ msgid "CASCADED" msgstr "" #: windows/views.py:2581 +#, fuzzy msgid "CHECK OPTION" -msgstr "" +msgstr "conexión" #: windows/views.py:2613 msgid "collapsible" -msgstr "" +msgstr "colapsable" #: windows/views.py:2629 msgid "Column3" -msgstr "" +msgstr "Columna3" #: windows/views.py:2630 msgid "Column4" -msgstr "" +msgstr "Columna4" #: windows/views.py:2673 msgid "" "Database " "(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" msgstr "" +"Database " +"(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" #: windows/views.py:2685 msgid "Port" -msgstr "" +msgstr "Puerto" #: windows/views.py:2708 msgid "Usage" -msgstr "" +msgstr "Uso" #: windows/views.py:2719 #, python-format msgid "%(total_rows)s" -msgstr "" +msgstr "%(total_rows)s" #: windows/views.py:2724 msgid "rows total" -msgstr "" +msgstr "filas en total" #: windows/views.py:2743 msgid "Lines" -msgstr "" +msgstr "Líneas" #: windows/views.py:2775 msgid "Temporary" -msgstr "" +msgstr "Temporal" #: windows/views.py:2786 +#, fuzzy msgid "Engine options" -msgstr "" +msgstr "Opciones" #: windows/views.py:2845 msgid "RadioBtn" @@ -709,136 +761,136 @@ msgstr "" #: windows/views.py:2928 msgid "Edit Column" -msgstr "" +msgstr "Editar columna" #: windows/views.py:2944 msgid "Datatype" -msgstr "" +msgstr "Tipo de datos" #: windows/components/dataview.py:121 windows/views.py:2959 msgid "Length/Set" -msgstr "" +msgstr "Longitud/Conjunto" #: windows/components/dataview.py:51 windows/views.py:2998 msgid "Unsigned" -msgstr "" +msgstr "Sin signo" #: windows/components/dataview.py:25 windows/components/dataview.py:52 #: windows/components/dataview.py:75 windows/views.py:3004 msgid "Allow NULL" -msgstr "" +msgstr "Permitir NULL" #: windows/views.py:3010 msgid "Zero Fill" -msgstr "" +msgstr "Relleno cero" #: windows/components/dataview.py:32 windows/components/dataview.py:56 #: windows/components/dataview.py:78 windows/views.py:3021 msgid "Default" -msgstr "" +msgstr "Predeterminado" #: windows/components/dataview.py:36 windows/components/dataview.py:60 #: windows/components/dataview.py:82 windows/views.py:3047 msgid "Virtuality" -msgstr "" +msgstr "Virtualidad" #: windows/components/dataview.py:39 windows/components/dataview.py:63 #: windows/components/dataview.py:85 windows/components/dataview.py:241 #: windows/views.py:3062 msgid "Expression" -msgstr "" +msgstr "Expresión" #: windows/components/dataview.py:28 msgid "Check" -msgstr "" +msgstr "Verificar" #: windows/components/dataview.py:53 msgid "Zerofill" -msgstr "" +msgstr "Relleno cero" #: windows/components/dataview.py:109 msgid "#" -msgstr "" +msgstr "#" #: windows/components/dataview.py:117 msgid "Data type" -msgstr "" +msgstr "Tipo de datos" #: windows/components/dataview.py:155 msgid "Add column\tCTRL+INS" -msgstr "" +msgstr "Agregar columna\tCTRL+INS" #: windows/components/dataview.py:161 msgid "Remove column\tCTRL+DEL" -msgstr "" +msgstr "Eliminar columna\tCTRL+DEL" #: windows/components/dataview.py:169 msgid "Move up\tCTRL+UP" -msgstr "" +msgstr "Mover arriba\tCTRL+UP" #: windows/components/dataview.py:176 msgid "Move down\tCTRL+D" -msgstr "" +msgstr "Mover abajo\tCTRL+D" #: windows/components/dataview.py:199 msgid "Create new index" -msgstr "" +msgstr "Crear nuevo índice" #: windows/components/dataview.py:214 msgid "Append to index" -msgstr "" +msgstr "Agregar al índice" #: windows/components/dataview.py:228 msgid "Column(s)/Expression" -msgstr "" +msgstr "Columna(s)/Expresión" #: windows/components/dataview.py:229 msgid "Condition" -msgstr "" +msgstr "Condición" #: windows/components/dataview.py:259 msgid "Column(s)" -msgstr "" +msgstr "Columna(s)" #: windows/components/dataview.py:265 msgid "Reference table" -msgstr "" +msgstr "Tabla de referencia" #: windows/components/dataview.py:271 msgid "Reference column(s)" -msgstr "" +msgstr "Columna(s) de referencia" #: windows/components/dataview.py:277 msgid "On UPDATE" -msgstr "" +msgstr "En UPDATE" #: windows/components/dataview.py:283 msgid "On DELETE" -msgstr "" +msgstr "En DELETE" #: windows/components/dataview.py:299 msgid "Add foreign key" -msgstr "" +msgstr "Agregar clave foránea" #: windows/components/dataview.py:305 msgid "Remove foreign key" -msgstr "" +msgstr "Eliminar clave foránea" #: windows/components/popup.py:26 msgid "No default value" -msgstr "" +msgstr "Sin valor predeterminado" #: windows/components/popup.py:31 msgid "NULL" -msgstr "" +msgstr "NULL" #: windows/components/popup.py:35 msgid "AUTO INCREMENT" -msgstr "" +msgstr "AUTO INCREMENTO" #: windows/components/popup.py:39 msgid "Text/Expression" -msgstr "" +msgstr "Texto/Expresión" #: windows/dialogs/connections/view.py:119 windows/main/tabs/query.py:376 msgid "Unknown error" @@ -850,7 +902,7 @@ msgstr "" #: windows/dialogs/connections/view.py:407 msgid "Confirm save" -msgstr "" +msgstr "Confirmar guardar" #: windows/dialogs/connections/view.py:459 msgid "You have unsaved changes. Do you want to save them before continuing?" @@ -868,33 +920,33 @@ msgstr "" #: windows/dialogs/connections/view.py:762 msgid "Connection error" -msgstr "" +msgstr "Error de conexión" #: windows/dialogs/connections/view.py:788 #: windows/dialogs/connections/view.py:803 msgid "Confirm delete" -msgstr "" +msgstr "Confirmar eliminar" #: windows/main/controller.py:172 msgid "days" -msgstr "" +msgstr "días" #: windows/main/controller.py:173 msgid "hours" -msgstr "" +msgstr "horas" #: windows/main/controller.py:174 msgid "minutes" -msgstr "" +msgstr "minutos" #: windows/main/controller.py:175 msgid "seconds" -msgstr "" +msgstr "segundos" #: windows/main/controller.py:183 #, python-brace-format msgid "Memory used: {used} ({percentage:.2%})" -msgstr "" +msgstr "Memoria utilizada: {used} ({percentage:.2%})" #: windows/main/controller.py:219 msgid "Settings saved successfully" @@ -902,11 +954,11 @@ msgstr "" #: windows/main/controller.py:298 msgid "Version" -msgstr "" +msgstr "Versión" #: windows/main/controller.py:300 msgid "Uptime" -msgstr "" +msgstr "Tiempo de actividad" #: windows/main/controller.py:399 #, python-brace-format @@ -919,8 +971,9 @@ msgid "" msgstr "" #: windows/main/controller.py:404 windows/main/controller.py:425 +#, fuzzy msgid "Delete database" -msgstr "" +msgstr "Eliminar tabla" #: windows/main/controller.py:410 msgid "Dump is not implemented yet. No action has been performed." @@ -945,32 +998,32 @@ msgstr "" #: windows/main/controller.py:582 msgid "Delete table" -msgstr "" +msgstr "Eliminar tabla" #: windows/main/controller.py:699 msgid "Do you want delete the records?" -msgstr "" +msgstr "¿Quieres eliminar los registros?" #: windows/main/tabs/database.py:71 msgid "The connection to the database was lost." -msgstr "" +msgstr "Se perdió la conexión a la base de datos." #: windows/main/tabs/database.py:73 msgid "Do you want to reconnect?" -msgstr "" +msgstr "¿Quieres reconectar?" #: windows/main/tabs/database.py:75 msgid "Connection lost" -msgstr "" +msgstr "Conexión perdida" #: windows/main/tabs/database.py:85 msgid "Reconnection failed:" -msgstr "" +msgstr "Reconexión fallida:" #: windows/main/tabs/database.py:86 windows/main/tabs/query.py:450 #: windows/main/tabs/view.py:256 windows/main/tabs/view.py:282 msgid "Error" -msgstr "" +msgstr "Error" #: windows/main/tabs/query.py:305 #, python-brace-format @@ -978,9 +1031,9 @@ msgid "{} rows affected" msgstr "" #: windows/main/tabs/query.py:309 windows/main/tabs/query.py:331 -#, python-brace-format +#, fuzzy, python-brace-format msgid "Query {}" -msgstr "" +msgstr "Consulta" #: windows/main/tabs/query.py:314 #, python-brace-format @@ -993,9 +1046,9 @@ msgid "Query {} ({} rows × {} cols)" msgstr "" #: windows/main/tabs/query.py:353 -#, python-brace-format +#, fuzzy, python-brace-format msgid "{} rows" -msgstr "" +msgstr "Filas" #: windows/main/tabs/query.py:355 #, python-brace-format @@ -1008,12 +1061,14 @@ msgid "{} warnings" msgstr "" #: windows/main/tabs/query.py:370 +#, fuzzy msgid "Error:" -msgstr "" +msgstr "Error" #: windows/main/tabs/query.py:449 +#, fuzzy msgid "No active database connection" -msgstr "" +msgstr "Nueva conexión" #: windows/main/tabs/view.py:252 msgid "View created successfully" @@ -1034,8 +1089,9 @@ msgid "Are you sure you want to delete view '{}'?" msgstr "" #: windows/main/tabs/view.py:270 +#, fuzzy msgid "Confirm Delete" -msgstr "" +msgstr "Confirmar eliminar" #: windows/main/tabs/view.py:279 msgid "View deleted successfully" @@ -1046,3 +1102,39 @@ msgstr "" msgid "Error deleting view: {}" msgstr "" +#~ msgid "Created at:" +#~ msgstr "" + +#~ msgid "Last connection:" +#~ msgstr "" + +#~ msgid "Successful connections:" +#~ msgstr "" + +#~ msgid "Unsuccessful connections:" +#~ msgstr "" + +#~ msgid "Session Manager" +#~ msgstr "" + +#~ msgid "Session name" +#~ msgstr "" + +#~ msgid "Connection type" +#~ msgstr "" + +#~ msgid "Open" +#~ msgstr "" + +#~ msgid "Open session manager" +#~ msgstr "" + +#~ msgid "Foreign Key" +#~ msgstr "" + +#~ msgid "New Session" +#~ msgstr "Nueva sesión" + +#~ msgid "directory" +#~ msgstr "directorio" + diff --git a/locale/fr_FR/LC_MESSAGES/petersql.po b/locale/fr_FR/LC_MESSAGES/petersql.po index da6114d..028bb82 100644 --- a/locale/fr_FR/LC_MESSAGES/petersql.po +++ b/locale/fr_FR/LC_MESSAGES/petersql.po @@ -1,37 +1,57 @@ +# Français (fr_FR) translations for PeterSQL. +# Copyright (C) 2026 ORGANIZATION +# This file is distributed under the same license as the PeterSQL project. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PeterSQL 0.1.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2026-03-10 18:23+0100\n" +"PO-Revision-Date: 2026-03-10 18:21+0000\n" +"Last-Translator: \n" +"Language: fr_FR\n" +"Language-Team: fr_FR \n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.18.0\n" + #: helpers/__init__.py:16 msgctxt "unit" msgid "B" -msgstr "" +msgstr "o" #: helpers/__init__.py:17 msgctxt "unit" msgid "KB" -msgstr "" +msgstr "Ko" #: helpers/__init__.py:18 msgctxt "unit" msgid "MB" -msgstr "" +msgstr "Mo" #: helpers/__init__.py:19 msgctxt "unit" msgid "GB" -msgstr "" +msgstr "Go" #: helpers/__init__.py:20 msgctxt "unit" msgid "TB" -msgstr "" +msgstr "To" #: structures/ssh_tunnel.py:166 msgid "OpenSSH client not found." -msgstr "" +msgstr "Client OpenSSH introuvable." #: windows/dialogs/connections/view.py:395 #: windows/dialogs/connections/view.py:738 windows/main/controller.py:296 #: windows/views.py:33 msgid "Connection" -msgstr "" +msgstr "Connexion" #: windows/components/dataview.py:113 windows/components/dataview.py:225 #: windows/components/dataview.py:238 windows/components/dataview.py:253 @@ -43,51 +63,53 @@ msgstr "" #: windows/views.py:2738 windows/views.py:2742 windows/views.py:2936 #: windows/views.py:3137 msgid "Name" -msgstr "" +msgstr "Nom" #: windows/views.py:48 windows/views.py:381 msgid "Last connection" -msgstr "" +msgstr "Dernière connexion" #: windows/dialogs/connections/view.py:631 windows/views.py:61 msgid "New directory" -msgstr "" +msgstr "Nouveau répertoire" #: windows/dialogs/connections/model.py:187 #: windows/dialogs/connections/view.py:591 windows/views.py:65 msgid "New connection" -msgstr "" +msgstr "Nouvelle connexion" #: windows/views.py:71 +#, fuzzy msgid "Rename" -msgstr "" +msgstr "Nom" #: windows/views.py:76 +#, fuzzy msgid "Clone connection" -msgstr "" +msgstr "Nouvelle connexion" #: windows/views.py:81 windows/views.py:556 windows/views.py:1290 #: windows/views.py:1331 windows/views.py:1706 windows/views.py:1738 #: windows/views.py:1997 windows/views.py:3273 windows/views.py:3305 msgid "Delete" -msgstr "" +msgstr "Supprimer" #: windows/views.py:111 windows/views.py:1311 windows/views.py:1468 #: windows/views.py:2747 windows/views.py:3192 msgid "Engine" -msgstr "" +msgstr "Moteur" #: windows/views.py:132 msgid "Host + port" -msgstr "" +msgstr "Hôte + port" #: windows/views.py:148 msgid "Username" -msgstr "" +msgstr "Nom d'utilisateur" #: windows/views.py:161 windows/views.py:1100 msgid "Password" -msgstr "" +msgstr "Mot de passe" #: windows/views.py:177 msgid "Use TLS" @@ -95,43 +117,44 @@ msgstr "" #: windows/views.py:180 msgid "Use SSH tunnel" -msgstr "" +msgstr "Utiliser un tunnel SSH" #: windows/views.py:202 windows/views.py:2668 msgid "Filename" -msgstr "" +msgstr "Nom de fichier" #: windows/views.py:207 windows/views.py:324 windows/views.py:2673 #: windows/views.py:2856 msgid "Select a file" -msgstr "" +msgstr "Sélectionner un fichier" #: windows/views.py:207 windows/views.py:324 windows/views.py:2856 +#, fuzzy msgid "*.*" -msgstr "" +msgstr "*. *" #: windows/components/dataview.py:70 windows/components/dataview.py:92 #: windows/views.py:221 windows/views.py:1313 windows/views.py:1426 #: windows/views.py:2748 windows/views.py:3034 windows/views.py:3150 msgid "Comments" -msgstr "" +msgstr "Commentaires" #: windows/main/controller.py:219 windows/views.py:235 windows/views.py:683 #: windows/views.py:837 msgid "Settings" -msgstr "" +msgstr "Paramètres" #: windows/views.py:244 msgid "SSH executable" -msgstr "" +msgstr "Exécutable SSH" #: windows/views.py:249 msgid "ssh" -msgstr "" +msgstr "ssh" #: windows/views.py:257 msgid "SSH host + port" -msgstr "" +msgstr "Hôte SSH + port" #: windows/views.py:269 msgid "SSH host + port (the SSH server that forwards traffic to the DB)" @@ -139,27 +162,28 @@ msgstr "" #: windows/views.py:278 msgid "SSH username" -msgstr "" +msgstr "Nom d'utilisateur SSH" #: windows/views.py:291 msgid "SSH password" -msgstr "" +msgstr "Mot de passe SSH" #: windows/views.py:304 msgid "Local port" -msgstr "" +msgstr "Port local" #: windows/views.py:310 msgid "if the value is set to 0, the first available port will be used" -msgstr "" +msgstr "si la valeur est définie à 0, le premier port disponible sera utilisé" #: windows/views.py:319 msgid "Identity file" msgstr "" #: windows/views.py:335 +#, fuzzy msgid "Remote host + port" -msgstr "" +msgstr "Hôte + port" #: windows/views.py:347 msgid "Remote host/port is the real DB target (defaults to DB Host/Port)." @@ -167,191 +191,198 @@ msgstr "" #: windows/views.py:358 msgid "SSH Tunnel" -msgstr "" +msgstr "Tunnel SSH" #: windows/views.py:364 windows/views.py:1309 windows/views.py:2745 msgid "Created at" -msgstr "" +msgstr "Créé le" #: windows/views.py:398 msgid "Successful connections" -msgstr "" +msgstr "Connexions réussies" #: windows/views.py:415 +#, fuzzy msgid "Last successful connection" -msgstr "" +msgstr "Connexions réussies" #: windows/views.py:432 msgid "Unsuccessful connections" -msgstr "" +msgstr "Connexions échouées" #: windows/views.py:449 msgid "Last failure reason" msgstr "" #: windows/views.py:466 +#, fuzzy msgid "Total connection attempts" -msgstr "" +msgstr "Dernière connexion" #: windows/views.py:483 +#, fuzzy msgid " Average connection time (ms)" -msgstr "" +msgstr "Échec de la reconnexion :" #: windows/views.py:500 +#, fuzzy msgid " Most recent connection duration" -msgstr "" +msgstr "Ouvrir le gestionnaire de connexions" #: windows/views.py:519 msgid "Statistics" -msgstr "" +msgstr "Statistiques" #: windows/views.py:537 windows/views.py:1672 msgid "Create" -msgstr "" +msgstr "Créer" #: windows/views.py:541 +#, fuzzy msgid "Create connection" -msgstr "" +msgstr "Dernière connexion" #: windows/views.py:544 +#, fuzzy msgid "Create directory" -msgstr "" +msgstr "Nouveau répertoire" #: windows/views.py:573 windows/views.py:797 windows/views.py:1328 #: windows/views.py:1741 windows/views.py:2002 windows/views.py:2078 #: windows/views.py:3081 windows/views.py:3308 msgid "Cancel" -msgstr "" +msgstr "Annuler" #: windows/views.py:578 windows/views.py:2007 windows/views.py:3091 #: windows/views.py:3313 msgid "Save" -msgstr "" +msgstr "Enregistrer" #: windows/views.py:585 msgid "Test" -msgstr "" +msgstr "Tester" #: windows/views.py:592 msgid "Connect" -msgstr "" +msgstr "Connecter" #: windows/views.py:695 msgid "Language" -msgstr "" +msgstr "Langue" #: windows/views.py:700 msgid "English" -msgstr "" +msgstr "Anglais" #: windows/views.py:700 msgid "Italian" -msgstr "" +msgstr "Italien" #: windows/views.py:700 msgid "French" -msgstr "" +msgstr "Français" #: windows/views.py:712 msgid "Locale" -msgstr "" +msgstr "Localisation" #: windows/views.py:733 msgid "Edit Value" -msgstr "" +msgstr "Modifier la valeur" #: windows/views.py:743 msgid "Syntax" -msgstr "" +msgstr "Syntaxe" #: windows/views.py:800 msgid "Ok" -msgstr "" +msgstr "Ok" #: windows/views.py:831 msgid "PeterSQL" -msgstr "" +msgstr "PeterSQL" #: windows/views.py:840 msgid "File" -msgstr "" +msgstr "Fichier" #: windows/views.py:843 msgid "About" -msgstr "" +msgstr "À propos" #: windows/views.py:846 msgid "Help" -msgstr "" +msgstr "Aide" #: windows/views.py:851 msgid "Open connection manager" -msgstr "" +msgstr "Ouvrir le gestionnaire de connexions" #: windows/views.py:853 msgid "Disconnect from server" -msgstr "" +msgstr "Se déconnecter du serveur" #: windows/views.py:857 msgid "tool" -msgstr "" +msgstr "outil" #: windows/views.py:857 msgid "Refresh" -msgstr "" +msgstr "Actualiser" #: windows/views.py:861 windows/views.py:863 msgid "Add" -msgstr "" +msgstr "Ajouter" #: windows/views.py:897 windows/views.py:901 windows/views.py:2166 #: windows/views.py:2654 msgid "MyMenuItem" -msgstr "" +msgstr "MonÉlémentMenu" #: windows/views.py:904 windows/views.py:1769 windows/views.py:3336 msgid "MyMenu" -msgstr "" +msgstr "MonMenu" #: windows/views.py:919 windows/views.py:1350 windows/views.py:1357 #: windows/views.py:1364 msgid "MyLabel" -msgstr "" +msgstr "MonÉtiquette" #: windows/views.py:925 msgid "Databases" -msgstr "" +msgstr "Bases de données" #: windows/views.py:926 windows/views.py:1308 windows/views.py:2716 #: windows/views.py:2744 msgid "Size" -msgstr "" +msgstr "Taille" #: windows/views.py:927 msgid "Elements" -msgstr "" +msgstr "Éléments" #: windows/views.py:928 msgid "Modified at" -msgstr "" +msgstr "Modifié le" #: windows/views.py:929 msgid "Tables" -msgstr "" +msgstr "Tables" #: windows/views.py:936 msgid "System" -msgstr "" +msgstr "Système" #: windows/views.py:979 +#, fuzzy msgid "Character set" -msgstr "" +msgstr "Créé le" #: windows/components/dataview.py:43 windows/components/dataview.py:67 #: windows/components/dataview.py:89 windows/views.py:1000 #: windows/views.py:1312 windows/views.py:2980 msgid "Collation" -msgstr "" +msgstr "Classement" #: windows/views.py:1026 windows/views.py:2862 msgid "Encryption" @@ -362,24 +393,29 @@ msgid "Read Only" msgstr "" #: windows/views.py:1055 +#, fuzzy msgid "Tablespace" -msgstr "" +msgstr "Tables" #: windows/views.py:1076 +#, fuzzy msgid "Connection limit" -msgstr "" +msgstr "Connexion perdue" #: windows/views.py:1119 +#, fuzzy msgid "Profile" -msgstr "" +msgstr "Fichier" #: windows/views.py:1145 +#, fuzzy msgid "Default tablespace" -msgstr "" +msgstr "Supprimer la table" #: windows/views.py:1166 +#, fuzzy msgid "Temporary tablespace" -msgstr "" +msgstr "Temporaire" #: windows/views.py:1192 msgid "Quota" @@ -394,108 +430,110 @@ msgid "Account status" msgstr "" #: windows/views.py:1249 +#, fuzzy msgid "Password expire" -msgstr "" +msgstr "Mot de passe" #: windows/views.py:1270 msgid "Table:" -msgstr "" +msgstr "Table :" #: windows/views.py:1278 windows/views.py:1551 windows/views.py:1595 #: windows/views.py:1701 windows/views.py:3268 msgid "Insert" -msgstr "" +msgstr "Insérer" #: windows/views.py:1283 msgid "Clone" -msgstr "" +msgstr "Cloner" #: windows/views.py:1307 msgid "Rows" -msgstr "" +msgstr "Lignes" #: windows/views.py:1310 windows/views.py:2746 msgid "Updated at" -msgstr "" +msgstr "Mis à jour le" #: windows/views.py:1334 windows/views.py:1746 windows/views.py:2085 #: windows/views.py:2140 msgid "Apply" -msgstr "" +msgstr "Appliquer" #: windows/views.py:1344 windows/views.py:1503 windows/views.py:1956 #: windows/views.py:3225 msgid "Options" -msgstr "" +msgstr "Options" #: windows/views.py:1375 msgid "Diagram" -msgstr "" +msgstr "Diagramme" #: windows/views.py:1386 windows/views.py:2715 msgid "Database" -msgstr "" +msgstr "Base de données" #: windows/views.py:1441 windows/views.py:3165 msgid "Base" -msgstr "" +msgstr "Base" #: windows/views.py:1455 windows/views.py:3179 msgid "Auto Increment" -msgstr "" +msgstr "Auto incrément" #: windows/views.py:1483 windows/views.py:3207 msgid "Default Collation" -msgstr "" +msgstr "Classement par défaut" #: windows/views.py:1515 windows/views.py:1556 windows/views.py:1600 msgid "Remove" -msgstr "" +msgstr "Supprimer" #: windows/views.py:1522 windows/views.py:1563 windows/views.py:1607 msgid "Clear" -msgstr "" +msgstr "Effacer" #: windows/views.py:1537 windows/views.py:3239 msgid "Indexes" -msgstr "" +msgstr "Index" #: windows/views.py:1581 msgid "Foreign Keys" -msgstr "" +msgstr "Clés étrangères" #: windows/views.py:1625 msgid "Checks" -msgstr "" +msgstr "Contrôles" #: windows/views.py:1693 windows/views.py:3260 msgid "Columns:" -msgstr "" +msgstr "Colonnes :" #: windows/views.py:1713 windows/views.py:3280 msgid "Up" -msgstr "" +msgstr "Haut" #: windows/views.py:1720 windows/views.py:3287 msgid "Down" -msgstr "" +msgstr "Bas" #: windows/views.py:1759 windows/views.py:1766 windows/views.py:3326 #: windows/views.py:3333 msgid "Add Index" -msgstr "" +msgstr "Ajouter un index" #: windows/views.py:1763 windows/views.py:3330 msgid "Add PrimaryKey" -msgstr "" +msgstr "Ajouter une clé primaire" #: windows/views.py:1780 msgid "Table" -msgstr "" +msgstr "Table" #: windows/views.py:1816 +#, fuzzy msgid "Definer" -msgstr "" +msgstr "Insérer" #: windows/views.py:1836 msgid "Schema" @@ -506,12 +544,14 @@ msgid "SQL security" msgstr "" #: windows/views.py:1869 +#, fuzzy msgid "DEFINER" -msgstr "" +msgstr "Insérer" #: windows/views.py:1869 +#, fuzzy msgid "INVOKER" -msgstr "" +msgstr "Insérer" #: windows/views.py:1881 windows/views.py:2558 windows/views.py:2577 #: windows/views.py:2820 @@ -520,8 +560,9 @@ msgstr "" #: windows/views.py:1883 windows/views.py:2543 windows/views.py:2576 #: windows/views.py:2825 +#, fuzzy msgid "UNDEFINED" -msgstr "" +msgstr "Non signé" #: windows/views.py:1886 windows/views.py:2546 windows/views.py:2576 #: windows/views.py:2828 @@ -530,28 +571,33 @@ msgstr "" #: windows/views.py:1889 windows/views.py:2555 windows/views.py:2576 #: windows/views.py:2831 +#, fuzzy msgid "TEMPTABLE" -msgstr "" +msgstr "Table" #: windows/views.py:1899 windows/views.py:2582 msgid "View constraint" msgstr "" #: windows/views.py:1901 windows/views.py:2581 +#, fuzzy msgid "None" -msgstr "" +msgstr "Cloner" #: windows/views.py:1904 windows/views.py:2581 +#, fuzzy msgid "LOCAL" -msgstr "" +msgstr "Localisation" #: windows/views.py:1907 +#, fuzzy msgid "CASCADE" -msgstr "" +msgstr "Annuler" #: windows/views.py:1910 +#, fuzzy msgid "CHECK ONLY" -msgstr "" +msgstr "Vérifier" #: windows/views.py:1913 windows/views.py:2581 msgid "READ ONLY" @@ -567,82 +613,84 @@ msgstr "" #: windows/views.py:2019 msgid "Views" -msgstr "" +msgstr "Vues" #: windows/views.py:2027 msgid "Triggers" -msgstr "" +msgstr "Déclencheurs" #: windows/views.py:2039 #, python-format msgid "Table `%(database_name)s`.`%(table_name)s`: %(total_rows) rows total" -msgstr "" +msgstr "Table `%(database_name)s`.`%(table_name)s` : %(total_rows) lignes au total" #: windows/views.py:2049 msgid "Insert record" -msgstr "" +msgstr "Insérer un enregistrement" #: windows/views.py:2054 msgid "Duplicate record" -msgstr "" +msgstr "Dupliquer un enregistrement" #: windows/views.py:2061 msgid "Delete record" -msgstr "" +msgstr "Supprimer un enregistrement" #: windows/views.py:2071 msgid "Apply changes automatically" -msgstr "" +msgstr "Appliquer les modifications automatiquement" #: windows/views.py:2073 windows/views.py:2074 msgid "" "If enabled, table edits are applied immediately without pressing Apply or" " Cancel" msgstr "" +"Si activé, les modifications de la table sont appliquées immédiatement " +"sans appuyer sur Appliquer ou Annuler" #: windows/views.py:2095 msgid "Next" -msgstr "" +msgstr "Suivant" #: windows/views.py:2103 msgid "Filters" -msgstr "" +msgstr "Filtres" #: windows/views.py:2143 msgid "CTRL+ENTER" -msgstr "" +msgstr "CTRL+ENTER" #: windows/views.py:2163 msgid "Insert row" -msgstr "" +msgstr "Insérer une ligne" #: windows/views.py:2171 msgid "Data" -msgstr "" +msgstr "Données" #: windows/views.py:2225 windows/views.py:2275 msgid "New" -msgstr "" +msgstr "Nouveau" #: windows/views.py:2252 msgid "Query" -msgstr "" +msgstr "Requête" #: windows/views.py:2272 msgid "Close" -msgstr "" +msgstr "Fermer" #: windows/views.py:2285 msgid "Query #2" -msgstr "" +msgstr "Requête #2" #: windows/views.py:2537 msgid "Column5" -msgstr "" +msgstr "Colonne5" #: windows/views.py:2548 msgid "Import" -msgstr "" +msgstr "Importer" #: windows/views.py:2573 msgid "Read only" @@ -653,55 +701,59 @@ msgid "CASCADED" msgstr "" #: windows/views.py:2581 +#, fuzzy msgid "CHECK OPTION" -msgstr "" +msgstr "connexion" #: windows/views.py:2613 msgid "collapsible" -msgstr "" +msgstr "rétractable" #: windows/views.py:2629 msgid "Column3" -msgstr "" +msgstr "Colonne3" #: windows/views.py:2630 msgid "Column4" -msgstr "" +msgstr "Colonne4" #: windows/views.py:2673 msgid "" "Database " "(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" msgstr "" +"Database " +"(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" #: windows/views.py:2685 msgid "Port" -msgstr "" +msgstr "Port" #: windows/views.py:2708 msgid "Usage" -msgstr "" +msgstr "Utilisation" #: windows/views.py:2719 #, python-format msgid "%(total_rows)s" -msgstr "" +msgstr "%(total_rows)s" #: windows/views.py:2724 msgid "rows total" -msgstr "" +msgstr "lignes au total" #: windows/views.py:2743 msgid "Lines" -msgstr "" +msgstr "Lignes" #: windows/views.py:2775 msgid "Temporary" -msgstr "" +msgstr "Temporaire" #: windows/views.py:2786 +#, fuzzy msgid "Engine options" -msgstr "" +msgstr "Options" #: windows/views.py:2845 msgid "RadioBtn" @@ -709,136 +761,136 @@ msgstr "" #: windows/views.py:2928 msgid "Edit Column" -msgstr "" +msgstr "Modifier la colonne" #: windows/views.py:2944 msgid "Datatype" -msgstr "" +msgstr "Type de données" #: windows/components/dataview.py:121 windows/views.py:2959 msgid "Length/Set" -msgstr "" +msgstr "Longueur/Ensemble" #: windows/components/dataview.py:51 windows/views.py:2998 msgid "Unsigned" -msgstr "" +msgstr "Non signé" #: windows/components/dataview.py:25 windows/components/dataview.py:52 #: windows/components/dataview.py:75 windows/views.py:3004 msgid "Allow NULL" -msgstr "" +msgstr "Autoriser NULL" #: windows/views.py:3010 msgid "Zero Fill" -msgstr "" +msgstr "Remplissage zéro" #: windows/components/dataview.py:32 windows/components/dataview.py:56 #: windows/components/dataview.py:78 windows/views.py:3021 msgid "Default" -msgstr "" +msgstr "Par défaut" #: windows/components/dataview.py:36 windows/components/dataview.py:60 #: windows/components/dataview.py:82 windows/views.py:3047 msgid "Virtuality" -msgstr "" +msgstr "Virtualité" #: windows/components/dataview.py:39 windows/components/dataview.py:63 #: windows/components/dataview.py:85 windows/components/dataview.py:241 #: windows/views.py:3062 msgid "Expression" -msgstr "" +msgstr "Expression" #: windows/components/dataview.py:28 msgid "Check" -msgstr "" +msgstr "Vérifier" #: windows/components/dataview.py:53 msgid "Zerofill" -msgstr "" +msgstr "Remplissage zéro" #: windows/components/dataview.py:109 msgid "#" -msgstr "" +msgstr "#" #: windows/components/dataview.py:117 msgid "Data type" -msgstr "" +msgstr "Type de données" #: windows/components/dataview.py:155 msgid "Add column\tCTRL+INS" -msgstr "" +msgstr "Ajouter une colonne\tCTRL+INS" #: windows/components/dataview.py:161 msgid "Remove column\tCTRL+DEL" -msgstr "" +msgstr "Supprimer une colonne\tCTRL+DEL" #: windows/components/dataview.py:169 msgid "Move up\tCTRL+UP" -msgstr "" +msgstr "Déplacer vers le haut\tCTRL+UP" #: windows/components/dataview.py:176 msgid "Move down\tCTRL+D" -msgstr "" +msgstr "Déplacer vers le bas\tCTRL+D" #: windows/components/dataview.py:199 msgid "Create new index" -msgstr "" +msgstr "Créer un nouvel index" #: windows/components/dataview.py:214 msgid "Append to index" -msgstr "" +msgstr "Ajouter à l'index" #: windows/components/dataview.py:228 msgid "Column(s)/Expression" -msgstr "" +msgstr "Colonne(s)/Expression" #: windows/components/dataview.py:229 msgid "Condition" -msgstr "" +msgstr "Condition" #: windows/components/dataview.py:259 msgid "Column(s)" -msgstr "" +msgstr "Colonne(s)" #: windows/components/dataview.py:265 msgid "Reference table" -msgstr "" +msgstr "Table de référence" #: windows/components/dataview.py:271 msgid "Reference column(s)" -msgstr "" +msgstr "Colonne(s) de référence" #: windows/components/dataview.py:277 msgid "On UPDATE" -msgstr "" +msgstr "Sur UPDATE" #: windows/components/dataview.py:283 msgid "On DELETE" -msgstr "" +msgstr "Sur DELETE" #: windows/components/dataview.py:299 msgid "Add foreign key" -msgstr "" +msgstr "Ajouter une clé étrangère" #: windows/components/dataview.py:305 msgid "Remove foreign key" -msgstr "" +msgstr "Supprimer une clé étrangère" #: windows/components/popup.py:26 msgid "No default value" -msgstr "" +msgstr "Aucune valeur par défaut" #: windows/components/popup.py:31 msgid "NULL" -msgstr "" +msgstr "NULL" #: windows/components/popup.py:35 msgid "AUTO INCREMENT" -msgstr "" +msgstr "AUTO INCREMENT" #: windows/components/popup.py:39 msgid "Text/Expression" -msgstr "" +msgstr "Texte/Expression" #: windows/dialogs/connections/view.py:119 windows/main/tabs/query.py:376 msgid "Unknown error" @@ -850,7 +902,7 @@ msgstr "" #: windows/dialogs/connections/view.py:407 msgid "Confirm save" -msgstr "" +msgstr "Confirmer la sauvegarde" #: windows/dialogs/connections/view.py:459 msgid "You have unsaved changes. Do you want to save them before continuing?" @@ -868,33 +920,33 @@ msgstr "" #: windows/dialogs/connections/view.py:762 msgid "Connection error" -msgstr "" +msgstr "Erreur de connexion" #: windows/dialogs/connections/view.py:788 #: windows/dialogs/connections/view.py:803 msgid "Confirm delete" -msgstr "" +msgstr "Confirmer la suppression" #: windows/main/controller.py:172 msgid "days" -msgstr "" +msgstr "jours" #: windows/main/controller.py:173 msgid "hours" -msgstr "" +msgstr "heures" #: windows/main/controller.py:174 msgid "minutes" -msgstr "" +msgstr "minutes" #: windows/main/controller.py:175 msgid "seconds" -msgstr "" +msgstr "secondes" #: windows/main/controller.py:183 #, python-brace-format msgid "Memory used: {used} ({percentage:.2%})" -msgstr "" +msgstr "Mémoire utilisée : {used} ({percentage:.2%})" #: windows/main/controller.py:219 msgid "Settings saved successfully" @@ -902,11 +954,11 @@ msgstr "" #: windows/main/controller.py:298 msgid "Version" -msgstr "" +msgstr "Version" #: windows/main/controller.py:300 msgid "Uptime" -msgstr "" +msgstr "Temps de fonctionnement" #: windows/main/controller.py:399 #, python-brace-format @@ -919,8 +971,9 @@ msgid "" msgstr "" #: windows/main/controller.py:404 windows/main/controller.py:425 +#, fuzzy msgid "Delete database" -msgstr "" +msgstr "Supprimer la table" #: windows/main/controller.py:410 msgid "Dump is not implemented yet. No action has been performed." @@ -945,32 +998,32 @@ msgstr "" #: windows/main/controller.py:582 msgid "Delete table" -msgstr "" +msgstr "Supprimer la table" #: windows/main/controller.py:699 msgid "Do you want delete the records?" -msgstr "" +msgstr "Voulez-vous supprimer les enregistrements ?" #: windows/main/tabs/database.py:71 msgid "The connection to the database was lost." -msgstr "" +msgstr "La connexion à la base de données a été perdue." #: windows/main/tabs/database.py:73 msgid "Do you want to reconnect?" -msgstr "" +msgstr "Voulez-vous vous reconnecter ?" #: windows/main/tabs/database.py:75 msgid "Connection lost" -msgstr "" +msgstr "Connexion perdue" #: windows/main/tabs/database.py:85 msgid "Reconnection failed:" -msgstr "" +msgstr "Échec de la reconnexion :" #: windows/main/tabs/database.py:86 windows/main/tabs/query.py:450 #: windows/main/tabs/view.py:256 windows/main/tabs/view.py:282 msgid "Error" -msgstr "" +msgstr "Erreur" #: windows/main/tabs/query.py:305 #, python-brace-format @@ -978,9 +1031,9 @@ msgid "{} rows affected" msgstr "" #: windows/main/tabs/query.py:309 windows/main/tabs/query.py:331 -#, python-brace-format +#, fuzzy, python-brace-format msgid "Query {}" -msgstr "" +msgstr "Requête" #: windows/main/tabs/query.py:314 #, python-brace-format @@ -993,9 +1046,9 @@ msgid "Query {} ({} rows × {} cols)" msgstr "" #: windows/main/tabs/query.py:353 -#, python-brace-format +#, fuzzy, python-brace-format msgid "{} rows" -msgstr "" +msgstr "Lignes" #: windows/main/tabs/query.py:355 #, python-brace-format @@ -1008,12 +1061,14 @@ msgid "{} warnings" msgstr "" #: windows/main/tabs/query.py:370 +#, fuzzy msgid "Error:" -msgstr "" +msgstr "Erreur" #: windows/main/tabs/query.py:449 +#, fuzzy msgid "No active database connection" -msgstr "" +msgstr "Nouvelle connexion" #: windows/main/tabs/view.py:252 msgid "View created successfully" @@ -1034,8 +1089,9 @@ msgid "Are you sure you want to delete view '{}'?" msgstr "" #: windows/main/tabs/view.py:270 +#, fuzzy msgid "Confirm Delete" -msgstr "" +msgstr "Confirmer la suppression" #: windows/main/tabs/view.py:279 msgid "View deleted successfully" @@ -1046,3 +1102,39 @@ msgstr "" msgid "Error deleting view: {}" msgstr "" +#~ msgid "Created at:" +#~ msgstr "" + +#~ msgid "Last connection:" +#~ msgstr "" + +#~ msgid "Successful connections:" +#~ msgstr "" + +#~ msgid "Unsuccessful connections:" +#~ msgstr "" + +#~ msgid "Session Manager" +#~ msgstr "" + +#~ msgid "Session name" +#~ msgstr "" + +#~ msgid "Connection type" +#~ msgstr "" + +#~ msgid "Open" +#~ msgstr "" + +#~ msgid "Open session manager" +#~ msgstr "" + +#~ msgid "Foreign Key" +#~ msgstr "" + +#~ msgid "New Session" +#~ msgstr "Nouvelle session" + +#~ msgid "directory" +#~ msgstr "répertoire" + diff --git a/locale/it_IT/LC_MESSAGES/petersql.po b/locale/it_IT/LC_MESSAGES/petersql.po index 55f9a33..94bfe10 100644 --- a/locale/it_IT/LC_MESSAGES/petersql.po +++ b/locale/it_IT/LC_MESSAGES/petersql.po @@ -1,3 +1,23 @@ +# Italiano (it_IT) translations for PeterSQL. +# Copyright (C) 2026 ORGANIZATION +# This file is distributed under the same license as the PeterSQL project. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PeterSQL 0.1.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2026-03-10 18:23+0100\n" +"PO-Revision-Date: 2026-03-10 18:21+0000\n" +"Last-Translator: \n" +"Language: it_IT\n" +"Language-Team: it_IT \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.18.0\n" + #: helpers/__init__.py:16 msgctxt "unit" msgid "B" @@ -59,12 +79,14 @@ msgid "New connection" msgstr "Nuova connessione" #: windows/views.py:71 +#, fuzzy msgid "Rename" -msgstr "Rinomina" +msgstr "Nome" #: windows/views.py:76 +#, fuzzy msgid "Clone connection" -msgstr "Clona connessione" +msgstr "Nuova connessione" #: windows/views.py:81 windows/views.py:556 windows/views.py:1290 #: windows/views.py:1331 windows/views.py:1706 windows/views.py:1738 @@ -91,7 +113,7 @@ msgstr "Password" #: windows/views.py:177 msgid "Use TLS" -msgstr "Usa TLS" +msgstr "" #: windows/views.py:180 msgid "Use SSH tunnel" @@ -135,7 +157,7 @@ msgstr "Host SSH + porta" #: windows/views.py:269 msgid "SSH host + port (the SSH server that forwards traffic to the DB)" -msgstr "Host SSH + porta (il server SSH che inoltra il traffico al DB)" +msgstr "" #: windows/views.py:278 msgid "SSH username" @@ -151,19 +173,20 @@ msgstr "Porta locale" #: windows/views.py:310 msgid "if the value is set to 0, the first available port will be used" -msgstr "se il valore è impostato su 0, verrà utilizzata la prima porta disponibile" +msgstr "se il valore è impostato a 0, verrà utilizzata la prima porta disponibile" #: windows/views.py:319 msgid "Identity file" -msgstr "File identità" +msgstr "" #: windows/views.py:335 +#, fuzzy msgid "Remote host + port" -msgstr "Host remoto + porta" +msgstr "Host + porta" #: windows/views.py:347 msgid "Remote host/port is the real DB target (defaults to DB Host/Port)." -msgstr "Host/porta remota è il vero target DB (predefinito a Host/Porta DB)." +msgstr "" #: windows/views.py:358 msgid "SSH Tunnel" @@ -178,28 +201,32 @@ msgid "Successful connections" msgstr "Connessioni riuscite" #: windows/views.py:415 +#, fuzzy msgid "Last successful connection" -msgstr "Ultima connessione riuscita" +msgstr "Connessioni riuscite" #: windows/views.py:432 msgid "Unsuccessful connections" -msgstr "Connessioni fallite" +msgstr "Connessioni non riuscite" #: windows/views.py:449 msgid "Last failure reason" -msgstr "Motivo ultimo fallimento" +msgstr "" #: windows/views.py:466 +#, fuzzy msgid "Total connection attempts" -msgstr "Totale tentativi connessione" +msgstr "Ultima connessione" #: windows/views.py:483 +#, fuzzy msgid " Average connection time (ms)" -msgstr " Tempo medio connessione (ms)" +msgstr "Riconnessione fallita:" #: windows/views.py:500 +#, fuzzy msgid " Most recent connection duration" -msgstr " Durata connessione più recente" +msgstr "Apri gestore connessioni" #: windows/views.py:519 msgid "Statistics" @@ -210,12 +237,14 @@ msgid "Create" msgstr "Crea" #: windows/views.py:541 +#, fuzzy msgid "Create connection" -msgstr "Crea connessione" +msgstr "Ultima connessione" #: windows/views.py:544 +#, fuzzy msgid "Create directory" -msgstr "Crea directory" +msgstr "Nuova directory" #: windows/views.py:573 windows/views.py:797 windows/views.py:1328 #: windows/views.py:1741 windows/views.py:2002 windows/views.py:2078 @@ -230,7 +259,7 @@ msgstr "Salva" #: windows/views.py:585 msgid "Test" -msgstr "Test" +msgstr "Testa" #: windows/views.py:592 msgid "Connect" @@ -254,11 +283,11 @@ msgstr "Francese" #: windows/views.py:712 msgid "Locale" -msgstr "Locale" +msgstr "Localizzazione" #: windows/views.py:733 msgid "Edit Value" -msgstr "Modifica Valore" +msgstr "Modifica valore" #: windows/views.py:743 msgid "Syntax" @@ -307,16 +336,16 @@ msgstr "Aggiungi" #: windows/views.py:897 windows/views.py:901 windows/views.py:2166 #: windows/views.py:2654 msgid "MyMenuItem" -msgstr "VoceMenu" +msgstr "IlMioElementoMenu" #: windows/views.py:904 windows/views.py:1769 windows/views.py:3336 msgid "MyMenu" -msgstr "Menu" +msgstr "IlMioMenu" #: windows/views.py:919 windows/views.py:1350 windows/views.py:1357 #: windows/views.py:1364 msgid "MyLabel" -msgstr "Etichetta" +msgstr "LaMiaEtichetta" #: windows/views.py:925 msgid "Databases" @@ -344,8 +373,9 @@ msgid "System" msgstr "Sistema" #: windows/views.py:979 +#, fuzzy msgid "Character set" -msgstr "Set caratteri" +msgstr "Creato il" #: windows/components/dataview.py:43 windows/components/dataview.py:67 #: windows/components/dataview.py:89 windows/views.py:1000 @@ -355,47 +385,53 @@ msgstr "Ordinamento" #: windows/views.py:1026 windows/views.py:2862 msgid "Encryption" -msgstr "Crittografia" +msgstr "" #: windows/views.py:1038 msgid "Read Only" -msgstr "Sola lettura" +msgstr "" #: windows/views.py:1055 +#, fuzzy msgid "Tablespace" -msgstr "Spazio tabelle" +msgstr "Tabelle" #: windows/views.py:1076 +#, fuzzy msgid "Connection limit" -msgstr "Limite connessioni" +msgstr "Connessione persa" #: windows/views.py:1119 +#, fuzzy msgid "Profile" -msgstr "Profilo" +msgstr "File" #: windows/views.py:1145 +#, fuzzy msgid "Default tablespace" -msgstr "Spazio tabelle predefinito" +msgstr "Elimina tabella" #: windows/views.py:1166 +#, fuzzy msgid "Temporary tablespace" -msgstr "Spazio tabelle temporaneo" +msgstr "Temporaneo" #: windows/views.py:1192 msgid "Quota" -msgstr "Quota" +msgstr "" #: windows/views.py:1211 msgid "Unlimited quota" -msgstr "Quota illimitata" +msgstr "" #: windows/views.py:1228 msgid "Account status" -msgstr "Stato account" +msgstr "" #: windows/views.py:1249 +#, fuzzy msgid "Password expire" -msgstr "Scadenza password" +msgstr "Password" #: windows/views.py:1270 msgid "Table:" @@ -442,11 +478,11 @@ msgstr "Base" #: windows/views.py:1455 windows/views.py:3179 msgid "Auto Increment" -msgstr "Auto Incremento" +msgstr "Auto incremento" #: windows/views.py:1483 windows/views.py:3207 msgid "Default Collation" -msgstr "Ordinamento Predefinito" +msgstr "Ordinamento predefinito" #: windows/views.py:1515 windows/views.py:1556 windows/views.py:1600 msgid "Remove" @@ -462,11 +498,11 @@ msgstr "Indici" #: windows/views.py:1581 msgid "Foreign Keys" -msgstr "Chiavi Esterne" +msgstr "Chiavi esterne" #: windows/views.py:1625 msgid "Checks" -msgstr "Controlli" +msgstr "Vincoli" #: windows/views.py:1693 windows/views.py:3260 msgid "Columns:" @@ -483,87 +519,96 @@ msgstr "Giù" #: windows/views.py:1759 windows/views.py:1766 windows/views.py:3326 #: windows/views.py:3333 msgid "Add Index" -msgstr "Aggiungi Indice" +msgstr "Aggiungi indice" #: windows/views.py:1763 windows/views.py:3330 msgid "Add PrimaryKey" -msgstr "Aggiungi Chiave Primaria" +msgstr "Aggiungi chiave primaria" #: windows/views.py:1780 msgid "Table" msgstr "Tabella" #: windows/views.py:1816 +#, fuzzy msgid "Definer" -msgstr "Definire" +msgstr "Inserisci" #: windows/views.py:1836 msgid "Schema" -msgstr "Schema" +msgstr "" #: windows/views.py:1862 msgid "SQL security" -msgstr "Sicurezza SQL" +msgstr "" #: windows/views.py:1869 +#, fuzzy msgid "DEFINER" -msgstr "DEFINER" +msgstr "Inserisci" #: windows/views.py:1869 +#, fuzzy msgid "INVOKER" -msgstr "INVOKER" +msgstr "Inserisci" #: windows/views.py:1881 windows/views.py:2558 windows/views.py:2577 #: windows/views.py:2820 msgid "Algorithm" -msgstr "Algoritmo" +msgstr "" #: windows/views.py:1883 windows/views.py:2543 windows/views.py:2576 #: windows/views.py:2825 +#, fuzzy msgid "UNDEFINED" -msgstr "NON DEFINITO" +msgstr "Senza segno" #: windows/views.py:1886 windows/views.py:2546 windows/views.py:2576 #: windows/views.py:2828 msgid "MERGE" -msgstr "MERGE" +msgstr "" #: windows/views.py:1889 windows/views.py:2555 windows/views.py:2576 #: windows/views.py:2831 +#, fuzzy msgid "TEMPTABLE" -msgstr "TEMPTABLE" +msgstr "Tabella" #: windows/views.py:1899 windows/views.py:2582 msgid "View constraint" -msgstr "Vincolo vista" +msgstr "" #: windows/views.py:1901 windows/views.py:2581 +#, fuzzy msgid "None" -msgstr "Nessuno" +msgstr "Clona" #: windows/views.py:1904 windows/views.py:2581 +#, fuzzy msgid "LOCAL" -msgstr "LOCALE" +msgstr "Localizzazione" #: windows/views.py:1907 +#, fuzzy msgid "CASCADE" -msgstr "CASCADE" +msgstr "Annulla" #: windows/views.py:1910 +#, fuzzy msgid "CHECK ONLY" -msgstr "SOLO CONTROLLO" +msgstr "Verifica" #: windows/views.py:1913 windows/views.py:2581 msgid "READ ONLY" -msgstr "SOLA LETTURA" +msgstr "" #: windows/views.py:1925 msgid "Force" -msgstr "Forza" +msgstr "" #: windows/views.py:1937 msgid "Security barrier" -msgstr "Barriera sicurezza" +msgstr "" #: windows/views.py:2019 msgid "Views" @@ -599,12 +644,12 @@ msgid "" "If enabled, table edits are applied immediately without pressing Apply or" " Cancel" msgstr "" -"Se abilitato, le modifiche alla tabella sono applicate immediatamente senza" -" premere Applica o Annulla" +"Se abilitato, le modifiche alla tabella vengono applicate immediatamente " +"senza premere Applica o Annulla" #: windows/views.py:2095 msgid "Next" -msgstr "Successivo" +msgstr "Avanti" #: windows/views.py:2103 msgid "Filters" @@ -648,15 +693,16 @@ msgstr "Importa" #: windows/views.py:2573 msgid "Read only" -msgstr "Sola lettura" +msgstr "" #: windows/views.py:2581 msgid "CASCADED" -msgstr "CASCADE" +msgstr "" #: windows/views.py:2581 +#, fuzzy msgid "CHECK OPTION" -msgstr "OPZIONE CONTROLLO" +msgstr "connessione" #: windows/views.py:2613 msgid "collapsible" @@ -704,24 +750,25 @@ msgid "Temporary" msgstr "Temporaneo" #: windows/views.py:2786 +#, fuzzy msgid "Engine options" -msgstr "Opzioni motore" +msgstr "Opzioni" #: windows/views.py:2845 msgid "RadioBtn" -msgstr "RadioBtn" +msgstr "" #: windows/views.py:2928 msgid "Edit Column" -msgstr "Modifica Colonna" +msgstr "Modifica colonna" #: windows/views.py:2944 msgid "Datatype" -msgstr "Tipo dati" +msgstr "Tipo di dati" #: windows/components/dataview.py:121 windows/views.py:2959 msgid "Length/Set" -msgstr "Lunghezza/Set" +msgstr "Lunghezza/Insieme" #: windows/components/dataview.py:51 windows/views.py:2998 msgid "Unsigned" @@ -730,7 +777,7 @@ msgstr "Senza segno" #: windows/components/dataview.py:25 windows/components/dataview.py:52 #: windows/components/dataview.py:75 windows/views.py:3004 msgid "Allow NULL" -msgstr "Permetti NULL" +msgstr "Consenti NULL" #: windows/views.py:3010 msgid "Zero Fill" @@ -754,11 +801,11 @@ msgstr "Espressione" #: windows/components/dataview.py:28 msgid "Check" -msgstr "Controllo" +msgstr "Verifica" #: windows/components/dataview.py:53 msgid "Zerofill" -msgstr "Zerofill" +msgstr "Riempimento zero" #: windows/components/dataview.py:109 msgid "#" @@ -766,7 +813,7 @@ msgstr "#" #: windows/components/dataview.py:117 msgid "Data type" -msgstr "Tipo dati" +msgstr "Tipo di dati" #: windows/components/dataview.py:155 msgid "Add column\tCTRL+INS" @@ -790,7 +837,7 @@ msgstr "Crea nuovo indice" #: windows/components/dataview.py:214 msgid "Append to index" -msgstr "Aggiungi a indice" +msgstr "Aggiungi all'indice" #: windows/components/dataview.py:228 msgid "Column(s)/Expression" @@ -806,19 +853,19 @@ msgstr "Colonna(e)" #: windows/components/dataview.py:265 msgid "Reference table" -msgstr "Tabella riferimento" +msgstr "Tabella di riferimento" #: windows/components/dataview.py:271 msgid "Reference column(s)" -msgstr "Colonna(e) riferimento" +msgstr "Colonna(e) di riferimento" #: windows/components/dataview.py:277 msgid "On UPDATE" -msgstr "Su UPDATE" +msgstr "Su AGGIORNAMENTO" #: windows/components/dataview.py:283 msgid "On DELETE" -msgstr "Su DELETE" +msgstr "Su ELIMINA" #: windows/components/dataview.py:299 msgid "Add foreign key" @@ -846,11 +893,11 @@ msgstr "Testo/Espressione" #: windows/dialogs/connections/view.py:119 windows/main/tabs/query.py:376 msgid "Unknown error" -msgstr "Errore sconosciuto" +msgstr "" #: windows/dialogs/connections/view.py:394 msgid "Connection established successfully" -msgstr "Connessione stabilita con successo" +msgstr "" #: windows/dialogs/connections/view.py:407 msgid "Confirm save" @@ -858,19 +905,21 @@ msgstr "Conferma salvataggio" #: windows/dialogs/connections/view.py:459 msgid "You have unsaved changes. Do you want to save them before continuing?" -msgstr "Hai modifiche non salvate. Vuoi salvarle prima di continuare?" +msgstr "" #: windows/dialogs/connections/view.py:461 msgid "Unsaved changes" -msgstr "Modifiche non salvate" +msgstr "" #: windows/dialogs/connections/view.py:736 -msgid "This connection cannot work without TLS. TLS has been enabled automatically." -msgstr "Questa connessione non può funzionare senza TLS. TLS è stato abilitato automaticamente." +msgid "" +"This connection cannot work without TLS. TLS has been enabled " +"automatically." +msgstr "" #: windows/dialogs/connections/view.py:762 msgid "Connection error" -msgstr "Errore connessione" +msgstr "Errore di connessione" #: windows/dialogs/connections/view.py:788 #: windows/dialogs/connections/view.py:803 @@ -900,7 +949,7 @@ msgstr "Memoria utilizzata: {used} ({percentage:.2%})" #: windows/main/controller.py:219 msgid "Settings saved successfully" -msgstr "Impostazioni salvate con successo" +msgstr "" #: windows/main/controller.py:298 msgid "Version" @@ -908,45 +957,43 @@ msgstr "Versione" #: windows/main/controller.py:300 msgid "Uptime" -msgstr "Tempo attività" +msgstr "Tempo di attività" #: windows/main/controller.py:399 #, python-brace-format -msgid "Do you want to create a dump before dropping database '{database_name}'?\n" +msgid "" +"Do you want to create a dump before dropping database '{database_name}'?\n" "\n" "Dump is not implemented yet.\n" "- Yes: open dump flow (coming soon, no drop).\n" "- No: drop the database now." -msgstr "Vuoi creare un dump prima di eliminare il database '{database_name}'?\n" -"\n" -"Dump non è ancora implementato.\n" -"- Sì: apri flusso dump (presto disponibile, no eliminazione).\n" -"- No: elimina il database ora." +msgstr "" #: windows/main/controller.py:404 windows/main/controller.py:425 +#, fuzzy msgid "Delete database" -msgstr "Elimina database" +msgstr "Elimina tabella" #: windows/main/controller.py:410 msgid "Dump is not implemented yet. No action has been performed." -msgstr "Dump non è ancora implementato. Nessuna azione è stata eseguita." +msgstr "" #: windows/main/controller.py:411 msgid "Dump not available" -msgstr "Dump non disponibile" +msgstr "" #: windows/main/controller.py:424 msgid "Database deletion is not supported by this engine." -msgstr "L'eliminazione del database non è supportata da questo motore." +msgstr "" #: windows/main/controller.py:439 msgid "Database deleted successfully" -msgstr "Database eliminato con successo" +msgstr "" #: windows/main/controller.py:440 windows/main/tabs/view.py:253 #: windows/main/tabs/view.py:279 msgid "Success" -msgstr "Successo" +msgstr "" #: windows/main/controller.py:582 msgid "Delete table" @@ -962,7 +1009,7 @@ msgstr "La connessione al database è stata persa." #: windows/main/tabs/database.py:73 msgid "Do you want to reconnect?" -msgstr "Vuoi riconnettere?" +msgstr "Vuoi riconnetterti?" #: windows/main/tabs/database.py:75 msgid "Connection lost" @@ -980,74 +1027,107 @@ msgstr "Errore" #: windows/main/tabs/query.py:305 #, python-brace-format msgid "{} rows affected" -msgstr "{} righe influenzate" +msgstr "" #: windows/main/tabs/query.py:309 windows/main/tabs/query.py:331 -#, python-brace-format +#, fuzzy, python-brace-format msgid "Query {}" -msgstr "Query {}" +msgstr "Query" #: windows/main/tabs/query.py:314 #, python-brace-format msgid "Query {} (Error)" -msgstr "Query {} (Errore)" +msgstr "" #: windows/main/tabs/query.py:326 #, python-brace-format msgid "Query {} ({} rows × {} cols)" -msgstr "Query {} ({} righe × {} colonne)" +msgstr "" #: windows/main/tabs/query.py:353 -#, python-brace-format +#, fuzzy, python-brace-format msgid "{} rows" -msgstr "{} righe" +msgstr "Righe" #: windows/main/tabs/query.py:355 #, python-brace-format msgid "{:.1f} ms" -msgstr "{:.1f} ms" +msgstr "" #: windows/main/tabs/query.py:358 #, python-brace-format msgid "{} warnings" -msgstr "{} avvisi" +msgstr "" #: windows/main/tabs/query.py:370 +#, fuzzy msgid "Error:" -msgstr "Errore:" +msgstr "Errore" #: windows/main/tabs/query.py:449 +#, fuzzy msgid "No active database connection" -msgstr "Nessuna connessione database attiva" +msgstr "Nuova connessione" #: windows/main/tabs/view.py:252 msgid "View created successfully" -msgstr "Vista creata con successo" +msgstr "" #: windows/main/tabs/view.py:252 msgid "View updated successfully" -msgstr "Vista aggiornata con successo" +msgstr "" #: windows/main/tabs/view.py:256 #, python-brace-format msgid "Error saving view: {}" -msgstr "Errore salvataggio vista: {}" +msgstr "" #: windows/main/tabs/view.py:269 #, python-brace-format msgid "Are you sure you want to delete view '{}'?" -msgstr "Sei sicuro di voler eliminare la vista '{}'?" +msgstr "" #: windows/main/tabs/view.py:270 +#, fuzzy msgid "Confirm Delete" -msgstr "Conferma Eliminazione" +msgstr "Conferma eliminazione" #: windows/main/tabs/view.py:279 msgid "View deleted successfully" -msgstr "Vista eliminata con successo" +msgstr "" #: windows/main/tabs/view.py:282 #, python-brace-format msgid "Error deleting view: {}" -msgstr "Errore eliminazione vista: {}" +msgstr "" + +#~ msgid "Created at:" +#~ msgstr "" + +#~ msgid "Last connection:" +#~ msgstr "" + +#~ msgid "Successful connections:" +#~ msgstr "" + +#~ msgid "Unsuccessful connections:" +#~ msgstr "" + +#~ msgid "Session name" +#~ msgstr "Nome sessione" + +#~ msgid "Open" +#~ msgstr "" + +#~ msgid "Open session manager" +#~ msgstr "" + +#~ msgid "Foreign Key" +#~ msgstr "" + +#~ msgid "New Session" +#~ msgstr "Nuova sessione" + +#~ msgid "directory" +#~ msgstr "directory" diff --git a/scripts/locale/de_DE/LC_MESSAGES/petersql.po b/scripts/locale/de_DE/LC_MESSAGES/petersql.po deleted file mode 100644 index 4370e80..0000000 --- a/scripts/locale/de_DE/LC_MESSAGES/petersql.po +++ /dev/null @@ -1,952 +0,0 @@ -#: helpers/__init__.py:16 -msgctxt "unit" -msgid "B" -msgstr "" - -#: helpers/__init__.py:17 -msgctxt "unit" -msgid "KB" -msgstr "" - -#: helpers/__init__.py:18 -msgctxt "unit" -msgid "MB" -msgstr "" - -#: helpers/__init__.py:19 -msgctxt "unit" -msgid "GB" -msgstr "" - -#: helpers/__init__.py:20 -msgctxt "unit" -msgid "TB" -msgstr "" - -#: structures/ssh_tunnel.py:166 -msgid "OpenSSH client not found." -msgstr "" - -#: windows/dialogs/connections/view.py:259 -#: windows/dialogs/connections/view.py:547 windows/main/controller.py:294 -#: windows/views.py:33 -msgid "Connection" -msgstr "" - -#: windows/components/dataview.py:113 windows/components/dataview.py:225 -#: windows/components/dataview.py:238 windows/components/dataview.py:253 -#: windows/views.py:47 windows/views.py:97 windows/views.py:898 -#: windows/views.py:958 windows/views.py:1341 windows/views.py:2246 -#: windows/views.py:2269 windows/views.py:2270 windows/views.py:2271 -#: windows/views.py:2272 windows/views.py:2273 windows/views.py:2274 -#: windows/views.py:2275 windows/views.py:2276 windows/views.py:2277 -#: windows/views.py:2281 windows/views.py:2462 windows/views.py:2663 -msgid "Name" -msgstr "" - -#: windows/views.py:48 windows/views.py:381 -msgid "Last connection" -msgstr "" - -#: windows/dialogs/connections/view.py:452 windows/views.py:61 -msgid "New directory" -msgstr "" - -#: windows/dialogs/connections/model.py:142 -#: windows/dialogs/connections/view.py:384 windows/views.py:65 -msgid "New connection" -msgstr "" - -#: windows/views.py:71 -msgid "Rename" -msgstr "" - -#: windows/views.py:76 -msgid "Clone connection" -msgstr "" - -#: windows/views.py:81 windows/views.py:471 windows/views.py:884 -#: windows/views.py:1251 windows/views.py:1283 windows/views.py:1542 -#: windows/views.py:2799 windows/views.py:2831 -msgid "Delete" -msgstr "" - -#: windows/views.py:111 windows/views.py:903 windows/views.py:1013 -#: windows/views.py:2286 windows/views.py:2718 -msgid "Engine" -msgstr "" - -#: windows/views.py:132 -msgid "Host + port" -msgstr "" - -#: windows/views.py:148 -msgid "Username" -msgstr "" - -#: windows/views.py:161 -msgid "Password" -msgstr "" - -#: windows/views.py:177 -msgid "Use TLS" -msgstr "" - -#: windows/views.py:180 -msgid "Use SSH tunnel" -msgstr "" - -#: windows/views.py:202 windows/views.py:2198 -msgid "Filename" -msgstr "" - -#: windows/views.py:207 windows/views.py:324 windows/views.py:2203 -#: windows/views.py:2395 -msgid "Select a file" -msgstr "" - -#: windows/views.py:207 windows/views.py:324 windows/views.py:2395 -msgid "*.*" -msgstr "" - -#: windows/components/dataview.py:70 windows/components/dataview.py:92 -#: windows/views.py:221 windows/views.py:905 windows/views.py:971 -#: windows/views.py:2287 windows/views.py:2560 windows/views.py:2676 -msgid "Comments" -msgstr "" - -#: windows/main/controller.py:217 windows/views.py:235 windows/views.py:598 -msgid "Settings" -msgstr "" - -#: windows/views.py:244 -msgid "SSH executable" -msgstr "" - -#: windows/views.py:249 -msgid "ssh" -msgstr "" - -#: windows/views.py:257 -msgid "SSH host + port" -msgstr "" - -#: windows/views.py:269 -msgid "SSH host + port (the SSH server that forwards traffic to the DB)" -msgstr "" - -#: windows/views.py:278 -msgid "SSH username" -msgstr "" - -#: windows/views.py:291 -msgid "SSH password" -msgstr "" - -#: windows/views.py:304 -msgid "Local port" -msgstr "" - -#: windows/views.py:310 -msgid "if the value is set to 0, the first available port will be used" -msgstr "" - -#: windows/views.py:319 -msgid "Identity file" -msgstr "" - -#: windows/views.py:335 -msgid "Remote host + port" -msgstr "" - -#: windows/views.py:347 -msgid "Remote host/port is the real DB target (defaults to DB Host/Port)." -msgstr "" - -#: windows/views.py:358 -msgid "SSH Tunnel" -msgstr "" - -#: windows/views.py:364 windows/views.py:901 windows/views.py:2284 -msgid "Created at" -msgstr "" - -#: windows/views.py:398 -msgid "Successful connections" -msgstr "" - -#: windows/views.py:415 -msgid "Unsuccessful connections" -msgstr "" - -#: windows/views.py:434 -msgid "Statistics" -msgstr "" - -#: windows/views.py:452 windows/views.py:1217 -msgid "Create" -msgstr "" - -#: windows/views.py:456 -msgid "Create connection" -msgstr "" - -#: windows/views.py:459 -msgid "Create directory" -msgstr "" - -#: windows/views.py:488 windows/views.py:712 windows/views.py:1286 -#: windows/views.py:1547 windows/views.py:1623 windows/views.py:2607 -#: windows/views.py:2834 -msgid "Cancel" -msgstr "" - -#: windows/views.py:493 windows/views.py:1552 windows/views.py:2617 -#: windows/views.py:2839 -msgid "Save" -msgstr "" - -#: windows/views.py:500 -msgid "Test" -msgstr "" - -#: windows/views.py:507 -msgid "Connect" -msgstr "" - -#: windows/views.py:610 -msgid "Language" -msgstr "" - -#: windows/views.py:615 -msgid "English" -msgstr "" - -#: windows/views.py:615 -msgid "Italian" -msgstr "" - -#: windows/views.py:615 -msgid "French" -msgstr "" - -#: windows/views.py:627 -msgid "Locale" -msgstr "" - -#: windows/views.py:648 -msgid "Edit Value" -msgstr "" - -#: windows/views.py:658 -msgid "Syntax" -msgstr "" - -#: windows/views.py:715 -msgid "Ok" -msgstr "" - -#: windows/views.py:746 -msgid "PeterSQL" -msgstr "" - -#: windows/views.py:752 -msgid "File" -msgstr "" - -#: windows/views.py:755 -msgid "About" -msgstr "" - -#: windows/views.py:758 -msgid "Help" -msgstr "" - -#: windows/views.py:763 -msgid "Open connection manager" -msgstr "" - -#: windows/views.py:765 -msgid "Disconnect from server" -msgstr "" - -#: windows/views.py:769 -msgid "tool" -msgstr "" - -#: windows/views.py:769 -msgid "Refresh" -msgstr "" - -#: windows/views.py:773 windows/views.py:775 -msgid "Add" -msgstr "" - -#: windows/views.py:809 windows/views.py:813 windows/views.py:1711 -#: windows/views.py:1839 -msgid "MyMenuItem" -msgstr "" - -#: windows/views.py:816 windows/views.py:1314 windows/views.py:2862 -msgid "MyMenu" -msgstr "" - -#: windows/views.py:831 -msgid "MyLabel" -msgstr "" - -#: windows/views.py:837 -msgid "Databases" -msgstr "" - -#: windows/views.py:838 windows/views.py:900 windows/views.py:2255 -#: windows/views.py:2283 -msgid "Size" -msgstr "" - -#: windows/views.py:839 -msgid "Elements" -msgstr "" - -#: windows/views.py:840 -msgid "Modified at" -msgstr "" - -#: windows/views.py:841 windows/views.py:912 -msgid "Tables" -msgstr "" - -#: windows/views.py:848 -msgid "System" -msgstr "" - -#: windows/views.py:864 -msgid "Table:" -msgstr "" - -#: windows/views.py:872 windows/views.py:1096 windows/views.py:1140 -#: windows/views.py:1246 windows/views.py:2794 -msgid "Insert" -msgstr "" - -#: windows/views.py:877 -msgid "Clone" -msgstr "" - -#: windows/views.py:899 -msgid "Rows" -msgstr "" - -#: windows/views.py:902 windows/views.py:2285 -msgid "Updated at" -msgstr "" - -#: windows/components/dataview.py:43 windows/components/dataview.py:67 -#: windows/components/dataview.py:89 windows/views.py:904 windows/views.py:2506 -msgid "Collation" -msgstr "" - -#: windows/views.py:920 -msgid "Diagram" -msgstr "" - -#: windows/views.py:931 windows/views.py:2254 -msgid "Database" -msgstr "" - -#: windows/views.py:986 windows/views.py:2691 -msgid "Base" -msgstr "" - -#: windows/views.py:1000 windows/views.py:2705 -msgid "Auto Increment" -msgstr "" - -#: windows/views.py:1028 windows/views.py:2733 -msgid "Default Collation" -msgstr "" - -#: windows/views.py:1048 windows/views.py:1501 windows/views.py:2751 -msgid "Options" -msgstr "" - -#: windows/views.py:1060 windows/views.py:1101 windows/views.py:1145 -msgid "Remove" -msgstr "" - -#: windows/views.py:1067 windows/views.py:1108 windows/views.py:1152 -msgid "Clear" -msgstr "" - -#: windows/views.py:1082 windows/views.py:2765 -msgid "Indexes" -msgstr "" - -#: windows/views.py:1126 -msgid "Foreign Keys" -msgstr "" - -#: windows/views.py:1170 -msgid "Checks" -msgstr "" - -#: windows/views.py:1238 windows/views.py:2786 -msgid "Columns:" -msgstr "" - -#: windows/views.py:1258 windows/views.py:2806 -msgid "Up" -msgstr "" - -#: windows/views.py:1265 windows/views.py:2813 -msgid "Down" -msgstr "" - -#: windows/views.py:1291 windows/views.py:1630 windows/views.py:1685 -msgid "Apply" -msgstr "" - -#: windows/views.py:1304 windows/views.py:1311 windows/views.py:2852 -#: windows/views.py:2859 -msgid "Add Index" -msgstr "" - -#: windows/views.py:1308 windows/views.py:2856 -msgid "Add PrimaryKey" -msgstr "" - -#: windows/views.py:1325 -msgid "Table" -msgstr "" - -#: windows/views.py:1361 -msgid "Definer" -msgstr "" - -#: windows/views.py:1381 -msgid "Schema" -msgstr "" - -#: windows/views.py:1407 -msgid "SQL security" -msgstr "" - -#: windows/views.py:1414 -msgid "DEFINER" -msgstr "" - -#: windows/views.py:1414 -msgid "INVOKER" -msgstr "" - -#: windows/views.py:1426 windows/views.py:2096 windows/views.py:2115 -#: windows/views.py:2359 -msgid "Algorithm" -msgstr "" - -#: windows/views.py:1428 windows/views.py:2081 windows/views.py:2114 -#: windows/views.py:2364 -msgid "UNDEFINED" -msgstr "" - -#: windows/views.py:1431 windows/views.py:2084 windows/views.py:2114 -#: windows/views.py:2367 -msgid "MERGE" -msgstr "" - -#: windows/views.py:1434 windows/views.py:2093 windows/views.py:2114 -#: windows/views.py:2370 -msgid "TEMPTABLE" -msgstr "" - -#: windows/views.py:1444 windows/views.py:2120 -msgid "View constraint" -msgstr "" - -#: windows/views.py:1446 windows/views.py:2119 -msgid "None" -msgstr "" - -#: windows/views.py:1449 windows/views.py:2119 -msgid "LOCAL" -msgstr "" - -#: windows/views.py:1452 -msgid "CASCADE" -msgstr "" - -#: windows/views.py:1455 -msgid "CHECK ONLY" -msgstr "" - -#: windows/views.py:1458 windows/views.py:2119 -msgid "READ ONLY" -msgstr "" - -#: windows/views.py:1470 -msgid "Force" -msgstr "" - -#: windows/views.py:1482 -msgid "Security barrier" -msgstr "" - -#: windows/views.py:1564 -msgid "Views" -msgstr "" - -#: windows/views.py:1572 -msgid "Triggers" -msgstr "" - -#: windows/views.py:1584 -#, python-format -msgid "Table `%(database_name)s`.`%(table_name)s`: %(total_rows) rows total" -msgstr "" - -#: windows/views.py:1594 -msgid "Insert record" -msgstr "" - -#: windows/views.py:1599 -msgid "Duplicate record" -msgstr "" - -#: windows/views.py:1606 -msgid "Delete record" -msgstr "" - -#: windows/views.py:1616 -msgid "Apply changes automatically" -msgstr "" - -#: windows/views.py:1618 windows/views.py:1619 -msgid "" -"If enabled, table edits are applied immediately without pressing Apply or" -" Cancel" -msgstr "" - -#: windows/views.py:1640 -msgid "Next" -msgstr "" - -#: windows/views.py:1648 -msgid "Filters" -msgstr "" - -#: windows/views.py:1688 -msgid "CTRL+ENTER" -msgstr "" - -#: windows/views.py:1708 -msgid "Insert row" -msgstr "" - -#: windows/views.py:1716 -msgid "Data" -msgstr "" - -#: windows/views.py:1770 windows/views.py:1820 -msgid "New" -msgstr "" - -#: windows/views.py:1797 -msgid "Query" -msgstr "" - -#: windows/views.py:1817 -msgid "Close" -msgstr "" - -#: windows/views.py:1830 -msgid "Query #2" -msgstr "" - -#: windows/views.py:2075 -msgid "Column5" -msgstr "" - -#: windows/views.py:2086 -msgid "Import" -msgstr "" - -#: windows/views.py:2111 -msgid "Read only" -msgstr "" - -#: windows/views.py:2119 -msgid "CASCADED" -msgstr "" - -#: windows/views.py:2119 -msgid "CHECK OPTION" -msgstr "" - -#: windows/views.py:2143 -msgid "collapsible" -msgstr "" - -#: windows/views.py:2165 -msgid "Column3" -msgstr "" - -#: windows/views.py:2166 -msgid "Column4" -msgstr "" - -#: windows/views.py:2203 -msgid "" -"Database " -"(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" -msgstr "" - -#: windows/views.py:2215 -msgid "Port" -msgstr "" - -#: windows/views.py:2247 -msgid "Usage" -msgstr "" - -#: windows/views.py:2258 -#, python-format -msgid "%(total_rows)s" -msgstr "" - -#: windows/views.py:2263 -msgid "rows total" -msgstr "" - -#: windows/views.py:2282 -msgid "Lines" -msgstr "" - -#: windows/views.py:2314 -msgid "Temporary" -msgstr "" - -#: windows/views.py:2325 -msgid "Engine options" -msgstr "" - -#: windows/views.py:2384 -msgid "RadioBtn" -msgstr "" - -#: windows/views.py:2454 -msgid "Edit Column" -msgstr "" - -#: windows/views.py:2470 -msgid "Datatype" -msgstr "" - -#: windows/components/dataview.py:121 windows/views.py:2485 -msgid "Length/Set" -msgstr "" - -#: windows/components/dataview.py:51 windows/views.py:2524 -msgid "Unsigned" -msgstr "" - -#: windows/components/dataview.py:25 windows/components/dataview.py:52 -#: windows/components/dataview.py:75 windows/views.py:2530 -msgid "Allow NULL" -msgstr "" - -#: windows/views.py:2536 -msgid "Zero Fill" -msgstr "" - -#: windows/components/dataview.py:32 windows/components/dataview.py:56 -#: windows/components/dataview.py:78 windows/views.py:2547 -msgid "Default" -msgstr "" - -#: windows/components/dataview.py:36 windows/components/dataview.py:60 -#: windows/components/dataview.py:82 windows/views.py:2573 -msgid "Virtuality" -msgstr "" - -#: windows/components/dataview.py:39 windows/components/dataview.py:63 -#: windows/components/dataview.py:85 windows/components/dataview.py:241 -#: windows/views.py:2588 -msgid "Expression" -msgstr "" - -#: windows/components/dataview.py:28 -msgid "Check" -msgstr "" - -#: windows/components/dataview.py:53 -msgid "Zerofill" -msgstr "" - -#: windows/components/dataview.py:109 -msgid "#" -msgstr "" - -#: windows/components/dataview.py:117 -msgid "Data type" -msgstr "" - -#: windows/components/dataview.py:155 -msgid "Add column\tCTRL+INS" -msgstr "" - -#: windows/components/dataview.py:161 -msgid "Remove column\tCTRL+DEL" -msgstr "" - -#: windows/components/dataview.py:169 -msgid "Move up\tCTRL+UP" -msgstr "" - -#: windows/components/dataview.py:176 -msgid "Move down\tCTRL+D" -msgstr "" - -#: windows/components/dataview.py:199 -msgid "Create new index" -msgstr "" - -#: windows/components/dataview.py:214 -msgid "Append to index" -msgstr "" - -#: windows/components/dataview.py:228 -msgid "Column(s)/Expression" -msgstr "" - -#: windows/components/dataview.py:229 -msgid "Condition" -msgstr "" - -#: windows/components/dataview.py:259 -msgid "Column(s)" -msgstr "" - -#: windows/components/dataview.py:265 -msgid "Reference table" -msgstr "" - -#: windows/components/dataview.py:271 -msgid "Reference column(s)" -msgstr "" - -#: windows/components/dataview.py:277 -msgid "On UPDATE" -msgstr "" - -#: windows/components/dataview.py:283 -msgid "On DELETE" -msgstr "" - -#: windows/components/dataview.py:299 -msgid "Add foreign key" -msgstr "" - -#: windows/components/dataview.py:305 -msgid "Remove foreign key" -msgstr "" - -#: windows/components/popup.py:26 -msgid "No default value" -msgstr "" - -#: windows/components/popup.py:31 -msgid "NULL" -msgstr "" - -#: windows/components/popup.py:35 -msgid "AUTO INCREMENT" -msgstr "" - -#: windows/components/popup.py:39 -msgid "Text/Expression" -msgstr "" - -#: windows/dialogs/connections/view.py:258 -msgid "Connection established successfully" -msgstr "" - -#: windows/dialogs/connections/view.py:271 -msgid "Confirm save" -msgstr "" - -#: windows/dialogs/connections/view.py:314 -msgid "You have unsaved changes. Do you want to save them before continuing?" -msgstr "" - -#: windows/dialogs/connections/view.py:316 -msgid "Unsaved changes" -msgstr "" - -#: windows/dialogs/connections/view.py:545 -msgid "" -"This connection cannot work without TLS. TLS has been enabled " -"automatically." -msgstr "" - -#: windows/dialogs/connections/view.py:554 -msgid "Connection error" -msgstr "" - -#: windows/dialogs/connections/view.py:580 -#: windows/dialogs/connections/view.py:595 -msgid "Confirm delete" -msgstr "" - -#: windows/main/controller.py:170 -msgid "days" -msgstr "" - -#: windows/main/controller.py:171 -msgid "hours" -msgstr "" - -#: windows/main/controller.py:172 -msgid "minutes" -msgstr "" - -#: windows/main/controller.py:173 -msgid "seconds" -msgstr "" - -#: windows/main/controller.py:181 -#, python-brace-format -msgid "Memory used: {used} ({percentage:.2%})" -msgstr "" - -#: windows/main/controller.py:217 -msgid "Settings saved successfully" -msgstr "" - -#: windows/main/controller.py:296 -msgid "Version" -msgstr "" - -#: windows/main/controller.py:298 -msgid "Uptime" -msgstr "" - -#: windows/main/controller.py:467 -msgid "Delete table" -msgstr "" - -#: windows/main/controller.py:584 -msgid "Do you want delete the records?" -msgstr "" - -#: windows/main/tabs/database.py:71 -msgid "The connection to the database was lost." -msgstr "" - -#: windows/main/tabs/database.py:73 -msgid "Do you want to reconnect?" -msgstr "" - -#: windows/main/tabs/database.py:75 -msgid "Connection lost" -msgstr "" - -#: windows/main/tabs/database.py:85 -msgid "Reconnection failed:" -msgstr "" - -#: windows/main/tabs/database.py:86 windows/main/tabs/query.py:450 -#: windows/main/tabs/view.py:256 windows/main/tabs/view.py:282 -msgid "Error" -msgstr "" - -#: windows/main/tabs/query.py:305 -#, python-brace-format -msgid "{} rows affected" -msgstr "" - -#: windows/main/tabs/query.py:309 windows/main/tabs/query.py:331 -#, python-brace-format -msgid "Query {}" -msgstr "" - -#: windows/main/tabs/query.py:314 -#, python-brace-format -msgid "Query {} (Error)" -msgstr "" - -#: windows/main/tabs/query.py:326 -#, python-brace-format -msgid "Query {} ({} rows × {} cols)" -msgstr "" - -#: windows/main/tabs/query.py:353 -#, python-brace-format -msgid "{} rows" -msgstr "" - -#: windows/main/tabs/query.py:355 -#, python-brace-format -msgid "{:.1f} ms" -msgstr "" - -#: windows/main/tabs/query.py:358 -#, python-brace-format -msgid "{} warnings" -msgstr "" - -#: windows/main/tabs/query.py:370 -msgid "Error:" -msgstr "" - -#: windows/main/tabs/query.py:376 -msgid "Unknown error" -msgstr "" - -#: windows/main/tabs/query.py:449 -msgid "No active database connection" -msgstr "" - -#: windows/main/tabs/view.py:252 -msgid "View created successfully" -msgstr "" - -#: windows/main/tabs/view.py:252 -msgid "View updated successfully" -msgstr "" - -#: windows/main/tabs/view.py:253 windows/main/tabs/view.py:279 -msgid "Success" -msgstr "" - -#: windows/main/tabs/view.py:256 -#, python-brace-format -msgid "Error saving view: {}" -msgstr "" - -#: windows/main/tabs/view.py:269 -#, python-brace-format -msgid "Are you sure you want to delete view '{}'?" -msgstr "" - -#: windows/main/tabs/view.py:270 -msgid "Confirm Delete" -msgstr "" - -#: windows/main/tabs/view.py:279 -msgid "View deleted successfully" -msgstr "" - -#: windows/main/tabs/view.py:282 -#, python-brace-format -msgid "Error deleting view: {}" -msgstr "" - -#~ msgid "New Session" -#~ msgstr "" - -#~ msgid "connection" -#~ msgstr "" - -#~ msgid "directory" -#~ msgstr "" - diff --git a/scripts/locale/en_US/LC_MESSAGES/petersql.po b/scripts/locale/en_US/LC_MESSAGES/petersql.po deleted file mode 100644 index 4370e80..0000000 --- a/scripts/locale/en_US/LC_MESSAGES/petersql.po +++ /dev/null @@ -1,952 +0,0 @@ -#: helpers/__init__.py:16 -msgctxt "unit" -msgid "B" -msgstr "" - -#: helpers/__init__.py:17 -msgctxt "unit" -msgid "KB" -msgstr "" - -#: helpers/__init__.py:18 -msgctxt "unit" -msgid "MB" -msgstr "" - -#: helpers/__init__.py:19 -msgctxt "unit" -msgid "GB" -msgstr "" - -#: helpers/__init__.py:20 -msgctxt "unit" -msgid "TB" -msgstr "" - -#: structures/ssh_tunnel.py:166 -msgid "OpenSSH client not found." -msgstr "" - -#: windows/dialogs/connections/view.py:259 -#: windows/dialogs/connections/view.py:547 windows/main/controller.py:294 -#: windows/views.py:33 -msgid "Connection" -msgstr "" - -#: windows/components/dataview.py:113 windows/components/dataview.py:225 -#: windows/components/dataview.py:238 windows/components/dataview.py:253 -#: windows/views.py:47 windows/views.py:97 windows/views.py:898 -#: windows/views.py:958 windows/views.py:1341 windows/views.py:2246 -#: windows/views.py:2269 windows/views.py:2270 windows/views.py:2271 -#: windows/views.py:2272 windows/views.py:2273 windows/views.py:2274 -#: windows/views.py:2275 windows/views.py:2276 windows/views.py:2277 -#: windows/views.py:2281 windows/views.py:2462 windows/views.py:2663 -msgid "Name" -msgstr "" - -#: windows/views.py:48 windows/views.py:381 -msgid "Last connection" -msgstr "" - -#: windows/dialogs/connections/view.py:452 windows/views.py:61 -msgid "New directory" -msgstr "" - -#: windows/dialogs/connections/model.py:142 -#: windows/dialogs/connections/view.py:384 windows/views.py:65 -msgid "New connection" -msgstr "" - -#: windows/views.py:71 -msgid "Rename" -msgstr "" - -#: windows/views.py:76 -msgid "Clone connection" -msgstr "" - -#: windows/views.py:81 windows/views.py:471 windows/views.py:884 -#: windows/views.py:1251 windows/views.py:1283 windows/views.py:1542 -#: windows/views.py:2799 windows/views.py:2831 -msgid "Delete" -msgstr "" - -#: windows/views.py:111 windows/views.py:903 windows/views.py:1013 -#: windows/views.py:2286 windows/views.py:2718 -msgid "Engine" -msgstr "" - -#: windows/views.py:132 -msgid "Host + port" -msgstr "" - -#: windows/views.py:148 -msgid "Username" -msgstr "" - -#: windows/views.py:161 -msgid "Password" -msgstr "" - -#: windows/views.py:177 -msgid "Use TLS" -msgstr "" - -#: windows/views.py:180 -msgid "Use SSH tunnel" -msgstr "" - -#: windows/views.py:202 windows/views.py:2198 -msgid "Filename" -msgstr "" - -#: windows/views.py:207 windows/views.py:324 windows/views.py:2203 -#: windows/views.py:2395 -msgid "Select a file" -msgstr "" - -#: windows/views.py:207 windows/views.py:324 windows/views.py:2395 -msgid "*.*" -msgstr "" - -#: windows/components/dataview.py:70 windows/components/dataview.py:92 -#: windows/views.py:221 windows/views.py:905 windows/views.py:971 -#: windows/views.py:2287 windows/views.py:2560 windows/views.py:2676 -msgid "Comments" -msgstr "" - -#: windows/main/controller.py:217 windows/views.py:235 windows/views.py:598 -msgid "Settings" -msgstr "" - -#: windows/views.py:244 -msgid "SSH executable" -msgstr "" - -#: windows/views.py:249 -msgid "ssh" -msgstr "" - -#: windows/views.py:257 -msgid "SSH host + port" -msgstr "" - -#: windows/views.py:269 -msgid "SSH host + port (the SSH server that forwards traffic to the DB)" -msgstr "" - -#: windows/views.py:278 -msgid "SSH username" -msgstr "" - -#: windows/views.py:291 -msgid "SSH password" -msgstr "" - -#: windows/views.py:304 -msgid "Local port" -msgstr "" - -#: windows/views.py:310 -msgid "if the value is set to 0, the first available port will be used" -msgstr "" - -#: windows/views.py:319 -msgid "Identity file" -msgstr "" - -#: windows/views.py:335 -msgid "Remote host + port" -msgstr "" - -#: windows/views.py:347 -msgid "Remote host/port is the real DB target (defaults to DB Host/Port)." -msgstr "" - -#: windows/views.py:358 -msgid "SSH Tunnel" -msgstr "" - -#: windows/views.py:364 windows/views.py:901 windows/views.py:2284 -msgid "Created at" -msgstr "" - -#: windows/views.py:398 -msgid "Successful connections" -msgstr "" - -#: windows/views.py:415 -msgid "Unsuccessful connections" -msgstr "" - -#: windows/views.py:434 -msgid "Statistics" -msgstr "" - -#: windows/views.py:452 windows/views.py:1217 -msgid "Create" -msgstr "" - -#: windows/views.py:456 -msgid "Create connection" -msgstr "" - -#: windows/views.py:459 -msgid "Create directory" -msgstr "" - -#: windows/views.py:488 windows/views.py:712 windows/views.py:1286 -#: windows/views.py:1547 windows/views.py:1623 windows/views.py:2607 -#: windows/views.py:2834 -msgid "Cancel" -msgstr "" - -#: windows/views.py:493 windows/views.py:1552 windows/views.py:2617 -#: windows/views.py:2839 -msgid "Save" -msgstr "" - -#: windows/views.py:500 -msgid "Test" -msgstr "" - -#: windows/views.py:507 -msgid "Connect" -msgstr "" - -#: windows/views.py:610 -msgid "Language" -msgstr "" - -#: windows/views.py:615 -msgid "English" -msgstr "" - -#: windows/views.py:615 -msgid "Italian" -msgstr "" - -#: windows/views.py:615 -msgid "French" -msgstr "" - -#: windows/views.py:627 -msgid "Locale" -msgstr "" - -#: windows/views.py:648 -msgid "Edit Value" -msgstr "" - -#: windows/views.py:658 -msgid "Syntax" -msgstr "" - -#: windows/views.py:715 -msgid "Ok" -msgstr "" - -#: windows/views.py:746 -msgid "PeterSQL" -msgstr "" - -#: windows/views.py:752 -msgid "File" -msgstr "" - -#: windows/views.py:755 -msgid "About" -msgstr "" - -#: windows/views.py:758 -msgid "Help" -msgstr "" - -#: windows/views.py:763 -msgid "Open connection manager" -msgstr "" - -#: windows/views.py:765 -msgid "Disconnect from server" -msgstr "" - -#: windows/views.py:769 -msgid "tool" -msgstr "" - -#: windows/views.py:769 -msgid "Refresh" -msgstr "" - -#: windows/views.py:773 windows/views.py:775 -msgid "Add" -msgstr "" - -#: windows/views.py:809 windows/views.py:813 windows/views.py:1711 -#: windows/views.py:1839 -msgid "MyMenuItem" -msgstr "" - -#: windows/views.py:816 windows/views.py:1314 windows/views.py:2862 -msgid "MyMenu" -msgstr "" - -#: windows/views.py:831 -msgid "MyLabel" -msgstr "" - -#: windows/views.py:837 -msgid "Databases" -msgstr "" - -#: windows/views.py:838 windows/views.py:900 windows/views.py:2255 -#: windows/views.py:2283 -msgid "Size" -msgstr "" - -#: windows/views.py:839 -msgid "Elements" -msgstr "" - -#: windows/views.py:840 -msgid "Modified at" -msgstr "" - -#: windows/views.py:841 windows/views.py:912 -msgid "Tables" -msgstr "" - -#: windows/views.py:848 -msgid "System" -msgstr "" - -#: windows/views.py:864 -msgid "Table:" -msgstr "" - -#: windows/views.py:872 windows/views.py:1096 windows/views.py:1140 -#: windows/views.py:1246 windows/views.py:2794 -msgid "Insert" -msgstr "" - -#: windows/views.py:877 -msgid "Clone" -msgstr "" - -#: windows/views.py:899 -msgid "Rows" -msgstr "" - -#: windows/views.py:902 windows/views.py:2285 -msgid "Updated at" -msgstr "" - -#: windows/components/dataview.py:43 windows/components/dataview.py:67 -#: windows/components/dataview.py:89 windows/views.py:904 windows/views.py:2506 -msgid "Collation" -msgstr "" - -#: windows/views.py:920 -msgid "Diagram" -msgstr "" - -#: windows/views.py:931 windows/views.py:2254 -msgid "Database" -msgstr "" - -#: windows/views.py:986 windows/views.py:2691 -msgid "Base" -msgstr "" - -#: windows/views.py:1000 windows/views.py:2705 -msgid "Auto Increment" -msgstr "" - -#: windows/views.py:1028 windows/views.py:2733 -msgid "Default Collation" -msgstr "" - -#: windows/views.py:1048 windows/views.py:1501 windows/views.py:2751 -msgid "Options" -msgstr "" - -#: windows/views.py:1060 windows/views.py:1101 windows/views.py:1145 -msgid "Remove" -msgstr "" - -#: windows/views.py:1067 windows/views.py:1108 windows/views.py:1152 -msgid "Clear" -msgstr "" - -#: windows/views.py:1082 windows/views.py:2765 -msgid "Indexes" -msgstr "" - -#: windows/views.py:1126 -msgid "Foreign Keys" -msgstr "" - -#: windows/views.py:1170 -msgid "Checks" -msgstr "" - -#: windows/views.py:1238 windows/views.py:2786 -msgid "Columns:" -msgstr "" - -#: windows/views.py:1258 windows/views.py:2806 -msgid "Up" -msgstr "" - -#: windows/views.py:1265 windows/views.py:2813 -msgid "Down" -msgstr "" - -#: windows/views.py:1291 windows/views.py:1630 windows/views.py:1685 -msgid "Apply" -msgstr "" - -#: windows/views.py:1304 windows/views.py:1311 windows/views.py:2852 -#: windows/views.py:2859 -msgid "Add Index" -msgstr "" - -#: windows/views.py:1308 windows/views.py:2856 -msgid "Add PrimaryKey" -msgstr "" - -#: windows/views.py:1325 -msgid "Table" -msgstr "" - -#: windows/views.py:1361 -msgid "Definer" -msgstr "" - -#: windows/views.py:1381 -msgid "Schema" -msgstr "" - -#: windows/views.py:1407 -msgid "SQL security" -msgstr "" - -#: windows/views.py:1414 -msgid "DEFINER" -msgstr "" - -#: windows/views.py:1414 -msgid "INVOKER" -msgstr "" - -#: windows/views.py:1426 windows/views.py:2096 windows/views.py:2115 -#: windows/views.py:2359 -msgid "Algorithm" -msgstr "" - -#: windows/views.py:1428 windows/views.py:2081 windows/views.py:2114 -#: windows/views.py:2364 -msgid "UNDEFINED" -msgstr "" - -#: windows/views.py:1431 windows/views.py:2084 windows/views.py:2114 -#: windows/views.py:2367 -msgid "MERGE" -msgstr "" - -#: windows/views.py:1434 windows/views.py:2093 windows/views.py:2114 -#: windows/views.py:2370 -msgid "TEMPTABLE" -msgstr "" - -#: windows/views.py:1444 windows/views.py:2120 -msgid "View constraint" -msgstr "" - -#: windows/views.py:1446 windows/views.py:2119 -msgid "None" -msgstr "" - -#: windows/views.py:1449 windows/views.py:2119 -msgid "LOCAL" -msgstr "" - -#: windows/views.py:1452 -msgid "CASCADE" -msgstr "" - -#: windows/views.py:1455 -msgid "CHECK ONLY" -msgstr "" - -#: windows/views.py:1458 windows/views.py:2119 -msgid "READ ONLY" -msgstr "" - -#: windows/views.py:1470 -msgid "Force" -msgstr "" - -#: windows/views.py:1482 -msgid "Security barrier" -msgstr "" - -#: windows/views.py:1564 -msgid "Views" -msgstr "" - -#: windows/views.py:1572 -msgid "Triggers" -msgstr "" - -#: windows/views.py:1584 -#, python-format -msgid "Table `%(database_name)s`.`%(table_name)s`: %(total_rows) rows total" -msgstr "" - -#: windows/views.py:1594 -msgid "Insert record" -msgstr "" - -#: windows/views.py:1599 -msgid "Duplicate record" -msgstr "" - -#: windows/views.py:1606 -msgid "Delete record" -msgstr "" - -#: windows/views.py:1616 -msgid "Apply changes automatically" -msgstr "" - -#: windows/views.py:1618 windows/views.py:1619 -msgid "" -"If enabled, table edits are applied immediately without pressing Apply or" -" Cancel" -msgstr "" - -#: windows/views.py:1640 -msgid "Next" -msgstr "" - -#: windows/views.py:1648 -msgid "Filters" -msgstr "" - -#: windows/views.py:1688 -msgid "CTRL+ENTER" -msgstr "" - -#: windows/views.py:1708 -msgid "Insert row" -msgstr "" - -#: windows/views.py:1716 -msgid "Data" -msgstr "" - -#: windows/views.py:1770 windows/views.py:1820 -msgid "New" -msgstr "" - -#: windows/views.py:1797 -msgid "Query" -msgstr "" - -#: windows/views.py:1817 -msgid "Close" -msgstr "" - -#: windows/views.py:1830 -msgid "Query #2" -msgstr "" - -#: windows/views.py:2075 -msgid "Column5" -msgstr "" - -#: windows/views.py:2086 -msgid "Import" -msgstr "" - -#: windows/views.py:2111 -msgid "Read only" -msgstr "" - -#: windows/views.py:2119 -msgid "CASCADED" -msgstr "" - -#: windows/views.py:2119 -msgid "CHECK OPTION" -msgstr "" - -#: windows/views.py:2143 -msgid "collapsible" -msgstr "" - -#: windows/views.py:2165 -msgid "Column3" -msgstr "" - -#: windows/views.py:2166 -msgid "Column4" -msgstr "" - -#: windows/views.py:2203 -msgid "" -"Database " -"(*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3)|*.db;*.db3;*.sdb;*.s3db;*.sqlite;*.sqlite3" -msgstr "" - -#: windows/views.py:2215 -msgid "Port" -msgstr "" - -#: windows/views.py:2247 -msgid "Usage" -msgstr "" - -#: windows/views.py:2258 -#, python-format -msgid "%(total_rows)s" -msgstr "" - -#: windows/views.py:2263 -msgid "rows total" -msgstr "" - -#: windows/views.py:2282 -msgid "Lines" -msgstr "" - -#: windows/views.py:2314 -msgid "Temporary" -msgstr "" - -#: windows/views.py:2325 -msgid "Engine options" -msgstr "" - -#: windows/views.py:2384 -msgid "RadioBtn" -msgstr "" - -#: windows/views.py:2454 -msgid "Edit Column" -msgstr "" - -#: windows/views.py:2470 -msgid "Datatype" -msgstr "" - -#: windows/components/dataview.py:121 windows/views.py:2485 -msgid "Length/Set" -msgstr "" - -#: windows/components/dataview.py:51 windows/views.py:2524 -msgid "Unsigned" -msgstr "" - -#: windows/components/dataview.py:25 windows/components/dataview.py:52 -#: windows/components/dataview.py:75 windows/views.py:2530 -msgid "Allow NULL" -msgstr "" - -#: windows/views.py:2536 -msgid "Zero Fill" -msgstr "" - -#: windows/components/dataview.py:32 windows/components/dataview.py:56 -#: windows/components/dataview.py:78 windows/views.py:2547 -msgid "Default" -msgstr "" - -#: windows/components/dataview.py:36 windows/components/dataview.py:60 -#: windows/components/dataview.py:82 windows/views.py:2573 -msgid "Virtuality" -msgstr "" - -#: windows/components/dataview.py:39 windows/components/dataview.py:63 -#: windows/components/dataview.py:85 windows/components/dataview.py:241 -#: windows/views.py:2588 -msgid "Expression" -msgstr "" - -#: windows/components/dataview.py:28 -msgid "Check" -msgstr "" - -#: windows/components/dataview.py:53 -msgid "Zerofill" -msgstr "" - -#: windows/components/dataview.py:109 -msgid "#" -msgstr "" - -#: windows/components/dataview.py:117 -msgid "Data type" -msgstr "" - -#: windows/components/dataview.py:155 -msgid "Add column\tCTRL+INS" -msgstr "" - -#: windows/components/dataview.py:161 -msgid "Remove column\tCTRL+DEL" -msgstr "" - -#: windows/components/dataview.py:169 -msgid "Move up\tCTRL+UP" -msgstr "" - -#: windows/components/dataview.py:176 -msgid "Move down\tCTRL+D" -msgstr "" - -#: windows/components/dataview.py:199 -msgid "Create new index" -msgstr "" - -#: windows/components/dataview.py:214 -msgid "Append to index" -msgstr "" - -#: windows/components/dataview.py:228 -msgid "Column(s)/Expression" -msgstr "" - -#: windows/components/dataview.py:229 -msgid "Condition" -msgstr "" - -#: windows/components/dataview.py:259 -msgid "Column(s)" -msgstr "" - -#: windows/components/dataview.py:265 -msgid "Reference table" -msgstr "" - -#: windows/components/dataview.py:271 -msgid "Reference column(s)" -msgstr "" - -#: windows/components/dataview.py:277 -msgid "On UPDATE" -msgstr "" - -#: windows/components/dataview.py:283 -msgid "On DELETE" -msgstr "" - -#: windows/components/dataview.py:299 -msgid "Add foreign key" -msgstr "" - -#: windows/components/dataview.py:305 -msgid "Remove foreign key" -msgstr "" - -#: windows/components/popup.py:26 -msgid "No default value" -msgstr "" - -#: windows/components/popup.py:31 -msgid "NULL" -msgstr "" - -#: windows/components/popup.py:35 -msgid "AUTO INCREMENT" -msgstr "" - -#: windows/components/popup.py:39 -msgid "Text/Expression" -msgstr "" - -#: windows/dialogs/connections/view.py:258 -msgid "Connection established successfully" -msgstr "" - -#: windows/dialogs/connections/view.py:271 -msgid "Confirm save" -msgstr "" - -#: windows/dialogs/connections/view.py:314 -msgid "You have unsaved changes. Do you want to save them before continuing?" -msgstr "" - -#: windows/dialogs/connections/view.py:316 -msgid "Unsaved changes" -msgstr "" - -#: windows/dialogs/connections/view.py:545 -msgid "" -"This connection cannot work without TLS. TLS has been enabled " -"automatically." -msgstr "" - -#: windows/dialogs/connections/view.py:554 -msgid "Connection error" -msgstr "" - -#: windows/dialogs/connections/view.py:580 -#: windows/dialogs/connections/view.py:595 -msgid "Confirm delete" -msgstr "" - -#: windows/main/controller.py:170 -msgid "days" -msgstr "" - -#: windows/main/controller.py:171 -msgid "hours" -msgstr "" - -#: windows/main/controller.py:172 -msgid "minutes" -msgstr "" - -#: windows/main/controller.py:173 -msgid "seconds" -msgstr "" - -#: windows/main/controller.py:181 -#, python-brace-format -msgid "Memory used: {used} ({percentage:.2%})" -msgstr "" - -#: windows/main/controller.py:217 -msgid "Settings saved successfully" -msgstr "" - -#: windows/main/controller.py:296 -msgid "Version" -msgstr "" - -#: windows/main/controller.py:298 -msgid "Uptime" -msgstr "" - -#: windows/main/controller.py:467 -msgid "Delete table" -msgstr "" - -#: windows/main/controller.py:584 -msgid "Do you want delete the records?" -msgstr "" - -#: windows/main/tabs/database.py:71 -msgid "The connection to the database was lost." -msgstr "" - -#: windows/main/tabs/database.py:73 -msgid "Do you want to reconnect?" -msgstr "" - -#: windows/main/tabs/database.py:75 -msgid "Connection lost" -msgstr "" - -#: windows/main/tabs/database.py:85 -msgid "Reconnection failed:" -msgstr "" - -#: windows/main/tabs/database.py:86 windows/main/tabs/query.py:450 -#: windows/main/tabs/view.py:256 windows/main/tabs/view.py:282 -msgid "Error" -msgstr "" - -#: windows/main/tabs/query.py:305 -#, python-brace-format -msgid "{} rows affected" -msgstr "" - -#: windows/main/tabs/query.py:309 windows/main/tabs/query.py:331 -#, python-brace-format -msgid "Query {}" -msgstr "" - -#: windows/main/tabs/query.py:314 -#, python-brace-format -msgid "Query {} (Error)" -msgstr "" - -#: windows/main/tabs/query.py:326 -#, python-brace-format -msgid "Query {} ({} rows × {} cols)" -msgstr "" - -#: windows/main/tabs/query.py:353 -#, python-brace-format -msgid "{} rows" -msgstr "" - -#: windows/main/tabs/query.py:355 -#, python-brace-format -msgid "{:.1f} ms" -msgstr "" - -#: windows/main/tabs/query.py:358 -#, python-brace-format -msgid "{} warnings" -msgstr "" - -#: windows/main/tabs/query.py:370 -msgid "Error:" -msgstr "" - -#: windows/main/tabs/query.py:376 -msgid "Unknown error" -msgstr "" - -#: windows/main/tabs/query.py:449 -msgid "No active database connection" -msgstr "" - -#: windows/main/tabs/view.py:252 -msgid "View created successfully" -msgstr "" - -#: windows/main/tabs/view.py:252 -msgid "View updated successfully" -msgstr "" - -#: windows/main/tabs/view.py:253 windows/main/tabs/view.py:279 -msgid "Success" -msgstr "" - -#: windows/main/tabs/view.py:256 -#, python-brace-format -msgid "Error saving view: {}" -msgstr "" - -#: windows/main/tabs/view.py:269 -#, python-brace-format -msgid "Are you sure you want to delete view '{}'?" -msgstr "" - -#: windows/main/tabs/view.py:270 -msgid "Confirm Delete" -msgstr "" - -#: windows/main/tabs/view.py:279 -msgid "View deleted successfully" -msgstr "" - -#: windows/main/tabs/view.py:282 -#, python-brace-format -msgid "Error deleting view: {}" -msgstr "" - -#~ msgid "New Session" -#~ msgstr "" - -#~ msgid "connection" -#~ msgstr "" - -#~ msgid "directory" -#~ msgstr "" - diff --git a/scripts/locales.py b/scripts/locales.py index 3e6f5fc..8dd3d49 100755 --- a/scripts/locales.py +++ b/scripts/locales.py @@ -4,6 +4,8 @@ import shutil import subprocess import sys +import toml +from datetime import datetime from pathlib import Path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -15,11 +17,52 @@ LOCALE_DIR = WORKDIR.joinpath("locale") POT_FILE = LOCALE_DIR.joinpath(f"{APP_NAME}.pot") +PYPROJECT_FILE = WORKDIR.joinpath("pyproject.toml") + + +def get_project_info(): + """Read project information from pyproject.toml""" + try: + with open(PYPROJECT_FILE, 'r', encoding='utf-8') as f: + data = toml.load(f) + project = data.get('project', {}) + return { + 'name': project.get('name'), + 'version': project.get('version') + } + except Exception: + return {'name': 'PeterSQL', 'version': '0.1.0'} def run(cmd): subprocess.run(cmd, shell=True, check=True) +def generate_header(lang): + """Generate proper header for .po files based on language""" + project_info = get_project_info() + current_date = datetime.now().strftime("%Y-%m-%d %H:%M%z") + + language = Language.from_code(lang) + lang_name = f"{language.label} ({lang})" + + return f'''# {lang_name} translations for {project_info['name']}. +# Copyright (C) 2026 {project_info['name']} +# This file is distributed under the same license as the {project_info['name']} project. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: {project_info['name']} {project_info['version']}\\n" +"POT-Creation-Date: {current_date}\\n" +"PO-Revision-Date: {current_date}\\n" +"Language: {lang}\\n" +"MIME-Version: 1.0\\n" +"Content-Type: text/plain; charset=utf-8\\n" +"Content-Transfer-Encoding: 8bit\\n" + +''' + + def extract(): LOCALE_DIR.mkdir(parents=True, exist_ok=True) @@ -40,15 +83,32 @@ def update(): po_file = message_dir.joinpath(f"{APP_NAME}.po") if not po_file.exists(): - shutil.copy(POT_FILE, po_file) - + # Create new .po file with proper header + with open(po_file, 'w', encoding='utf-8') as f: + f.write(generate_header(lang)) + + # Then update with babel to add translations + run( + f"pybabel update " + f"-i {POT_FILE} " + f"-o {po_file} " + f"-l {lang} " + ) else: + # Always prepend header for existing files (one-time patch) + with open(po_file, 'r', encoding='utf-8') as f: + existing_content = f.read() + + with open(po_file, 'w', encoding='utf-8') as f: + f.write(generate_header(lang)) + f.write(existing_content) + + # Update with babel run( f"pybabel update " f"-i {POT_FILE} " f"-o {po_file} " f"-l {lang} " - f"--omit-header" ) From 00c20497b0189adffa024e5e97b9e8a29448e223 Mon Sep 17 00:00:00 2001 From: gtripoli Date: Tue, 10 Mar 2026 19:29:54 +0100 Subject: [PATCH 12/12] update locales AI-Assisted-By: Cline AI-Contribution: 80% Tracked-By: CodeShield AI --- .github/workflows/ci.yml | 16 ++++++ locale/de_DE/LC_MESSAGES/petersql.mo | Bin 0 -> 8534 bytes locale/de_DE/LC_MESSAGES/petersql.po | 8 +-- locale/en_US/LC_MESSAGES/petersql.mo | Bin 0 -> 396 bytes locale/en_US/LC_MESSAGES/petersql.po | 76 +++++++++++++-------------- locale/es_ES/LC_MESSAGES/petersql.mo | Bin 0 -> 8524 bytes locale/es_ES/LC_MESSAGES/petersql.po | 8 +-- locale/fr_FR/LC_MESSAGES/petersql.mo | Bin 0 -> 8728 bytes locale/it_IT/LC_MESSAGES/petersql.mo | Bin 0 -> 8491 bytes pyproject.toml | 3 +- uv.lock | 11 ++++ 11 files changed, 75 insertions(+), 47 deletions(-) create mode 100644 locale/de_DE/LC_MESSAGES/petersql.mo create mode 100644 locale/en_US/LC_MESSAGES/petersql.mo create mode 100644 locale/es_ES/LC_MESSAGES/petersql.mo create mode 100644 locale/fr_FR/LC_MESSAGES/petersql.mo create mode 100644 locale/it_IT/LC_MESSAGES/petersql.mo diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc0a35a..dd4e8cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,14 @@ jobs: restore-keys: | pip-${{ runner.os }}-py${{ env.PYTHON_VERSION }}- + - name: Cache uv + uses: actions/cache@v4 + with: + path: ${{ env.UV_CACHE_DIR }} + key: uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-${{ hashFiles('**/uv.lock', '**/pyproject.toml') }} + restore-keys: | + uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}- + - name: Cache wheelhouse uses: actions/cache@v4 with: @@ -165,6 +173,14 @@ jobs: restore-keys: | pip-${{ runner.os }}-py${{ env.PYTHON_VERSION }}- + - name: Cache uv + uses: actions/cache@v4 + with: + path: ${{ env.UV_CACHE_DIR }} + key: uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-${{ hashFiles('**/uv.lock', '**/pyproject.toml') }} + restore-keys: | + uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}- + - name: Cache wheelhouse uses: actions/cache@v4 with: diff --git a/locale/de_DE/LC_MESSAGES/petersql.mo b/locale/de_DE/LC_MESSAGES/petersql.mo new file mode 100644 index 0000000000000000000000000000000000000000..120161d5e0c3f9375fcd6a9e2612ce22d0f0974b GIT binary patch literal 8534 zcmcJTdyFK-ea8!j=V4>8F*YXPI%DvjkJ)?iIOaHx-P^glz1!FA<47=u>z%3D?c1L2 zp}XgnU0yLEae#|44u_4gg9!vHF_BOd{V<<@!Lt1I8j0pDG!8X$wG3hSRtQp zcg@b6^Hij?_Sc`P>guXrRsDXy{>}dNJI*|5xW0h=JTiNhF@FP3Y~aRq?ghr20ndZ{ zne#p03+ZY$!JmU$;CXO|f4&>OllmBZJ3Ig@@IaJ+nMkbRR6EQ zv*9^k0#njJ6iF4p(o>zKqhUe10!}BJn_iK<>%_Mv) zyaVd}B`CczcpftD)xI2Cs%Yq2|34YQ0NP@81hG?+K{>Pe9H0G?czC zczzMej$eX0_dkHrZ_PjdF4VYx2Hyhz5$Zf&^LzuooBF>&?f)ehzvkTvwVs`xLs0V` zgj&y}Z=Zv*(>ox4CgUdkKL$0kXdQ1gyJ&36cDU5BCGKMJ)kp?`iC)O*WN^FIjH?*x=x zoP^TnS*Y<|fP3JJP~*G~_5K^a{w9gE)@9DWNb9=`5*E}Nrs`T*2HGTp$?)^~f zcnE4A9)r^NDc}Aq)cY?&t>;U={gmg|q2Bu*)O>#h_5Ke%f9&}t)Hvs0?3(XFsP`^| z9I?3yvW2GV+oz!V-3GN@2Qf*r?4Lgh)&C^azI+jCJzs{J=M|{&zXp%O??Cn2_TKXO zZqI#C<4*c|9cmpBl>a{9+dtvkpMqD@{vx~rejjRIegvh@PoVUDJBK1W-Uy}7HmH78 zUmx=IgT8(kO8?uS^gimJAA=g_E-1hJC{(|XL#^it7{Sl``rG&@YyU5W8fPQa`Zhz2 zvj^(ENvM7whSIkVHGT(5&j)?|VPAg|ZlV2izWtk!Ej2&zd;@B}3ouIYJy84iekgy~ z;kgsaPj*A;GXypNVW@fQzJ3hKzwd>zg9khxfo!ok3AO((LGACiJ^$SEZ=vRW4HotY zHO`Nrg>T_y&3h%39@jznO$|!_8L0Jr#B&)+zsI4*`81RtJqPvv??dh1SE1JZZK!cx z_4U`F#{X9+ea_?Kt8=&rYM#rX#=pVyW~hEOI0na|*7+IF=b*-a5$e5{p~im&UIYIa z>ivI#(oc&fM3@Vq_GJL7y#l58Zm4w~g!0EZsP)8%v54(JQ4?KhV0jTpo=sE5=CQ zHoy-fztUG%@n+-}MEi9F`6TiJayHUJbp0l>19(yi^*ExlCS(*jJT75o5M2w%PUI?N2+?Jc&mq5x=+b#skeiVGi2U&iWD~Lt zkzL)0=#ou_$T0FWqO;cZ2(lTesls&#*;15@`7Njzyn-wtPa)mwdJ0p>uOXAjL&!zQ z$B^#z7=?#@<(J{2FW&`^Bex?}WH+K~FLD&Q647-@33ILIAdHYz>9T-zk$pn&my|^AUSe@DqQbJegQcPX(QVaUE|0BWu_vLrO`;q67Uqa47ei702K_pS( zN^{jfp5#GvXPPW$n=&&rH#=!ZC-zR)#%mL^W@tX?sS6ikyQDr*lq}ncxv?=b)NZ@DVe`a>vQDf?Q>RK+&&2W%8Q_O^dwP~|Ai0dvgdly}Osk~=qZ{&iM zdlJ(%O7ub!MU0rlDxvE@wy9UzQoFmIx-9EG-r*l!>v^4LR;AFA9!!$NjnEGnH^a1L z8!mFW<|?aMaJ;C*uAZw=ET`xetxMAcMc1EMl%;R#0*zC^#%|f~RoT$6L2gEZJSc89 zUunCdG_Q?Oy9##TLxYX^UApbyo)zV6M~Sx<@qi`wP@r)f9ac9Q01v}t*ZH8-v) z*KQrkVC<>00p=gNh7B>TMu@&R!*ZA};(WHW%y_|;K9Z!?Tl{FN99M4SGBX;pXG~L| zyWVasSvI41c^JfI3?J(R3yuQH`Tdevj=6XtU)(a|ax=yzWM(YE0ZZkJqBG;Jm82`S zletFK-Xq*+2kvRRw2ntGW_57;RrhT&<4GfIYCRb0cyip?28U5bhmo?O(=MxXQ)YZ+ zESSfE$5+N(-0>LAUCR{lVnQ1|;g%)++N#dOAj_6z!&6vr zI&*l;OzCJ3cN{ttcYEb_Gu^d9-m%R9YiU$zY&uznW9DuE>a~^5K%FqONZ%H_=HdJM z?W>J-oBQMIG{M_Q_1O;Q9~DK6qTVUT)sw|OQ&hd(U5YB4WLjACj6&874qDG`V4Jdb z>>&^11;!6ospUk`jL=NIQ?C;=o1Lg{GihGI6z&F@&TzKy^5SlfUNzA0;ptAzwkbP0 zNL!&}x1nlJ`yg3;miSm;yX8>T5}BEGGJn27ln9x#7(jm*pCwB4Y+Iij#f|Rt1eB}@ zwS#6h4Hr24%*@3ZpDWi;;Xhy5T-(gG8KN}oIcx&coXg}(b3|NiWM1w+)p9?NFtU8> zNa#4BBVn3%@WOn>+~(565=f$=#70L3yNH5z7Ah<@0wM|Sk6vN3P`$8d8)ihtT+7)? z6k$btcktriI98^eF1(e{5k+?16@F@3Vcf~_=DwZGWT1`AWY}7sB$4UFVSeUtiT$33 zJjXp}hs{))9K$jyqm7DRSk-nt!EPPgHn?@ev{O*3jAsjBqcYrC$SSi*)z-#`M#pX$ z8X1|c&CG0=nygH_#V(cEREAfoc39EQZXevXlPUhk1Z?fI^F*ctVZ7RDuAN$N zF#(=gnRXEI0qkq)v_n={KHTAXt><-~ zan&3ubGjgPW|$8!MiwuuPmCvJ`P%b&s724TU5GYmd|E5sFt2nLM=wiyzgNU479)=r702eN)_g}PaguiB`4l6(%dKggvKs<9r;c6PsKf%N{e`!I#M=wReRw4W)*wSPxTp7n$cr!@MIk zj?Jy4Qk?L5-6x1eX4p#*N%QuVC%bNHnjkEu3Hwq+C58QtMm>mE*$%Q^QpGT8x^Avm zBow!@bQ>(?;c*nqud&N6#kj2Bx45fV>2g^@Du8|9e}zNRKUs`PDmzWv^--JPp?2NR z>}tn0aJ9P5v&c66+p8C;rHhNj%(T-02O*oxR|z{k zlgFPFH@1-F@xd&HAm>b$0zAVOt1c_@l5!oicirv3%Qp8r24;DMi%c-N)mh6ymmMzK z%{z-H39;<-2g*QMiO)ePq2Jo6qOB$OYu~WdMoL9;b(FX?8qua@!Ol=N>4(f&rMge;`5=v+HE0UllPjTr6QTh zO3SImIiJ2FK$jmJVpQ4C%UplP3-~?MeaD-F*4Rs7E49;7*oyx(RoIGpuCG>=VSYRi z5quikjnbtj;tJXF+VjL<;%yigp|$WGvp-#XnNP`jS}T93rOhN-;GY+mao-v9<9h%gN(so=?wwY4YB~_9k_8`0-&w>kR z`=w8b9b5LF@56(y9%+v_B2I{X;)3WD5;;BI(Am{&m1*^k3(KZU_Ke!pN^;21qAs2i z@Lb>ZoE%%FElW*RFWG2a8CEI|aXOqPgW-6b$3@{}k1WTJy6b0SVUa`N1=q~GV!j7{ z$OHe}BRPpMmX%sa(+I0H2g=B5Eh_3j`7$fEF)LB5{*!Q@sTwAg&*NG#?j%DfDR;^!_o&*WO_SJh=(zWlq z_vPOEB+L2HHl(B^PSdnWLla8s23#^FkW49^GU*hm$uKbO&`@9s%#;pv3WYKq82Ff7Feg7D|9zF^AXI|pxLij3F z|KEof!!JYi|C+D=g|C0p^V^=^h0^0+JiiauQNI`^E`is3-r~6lUP}9R&pV*LuRuOE zWAJi#1nT<}PG`~GKMU3Gb5QH~64bbV05#w1@EH6{-@c2%)Lwy_ zXB7SfoQ4{A9{w0iq4aqGs^6pV8u(#&6MPX~2mcUi-oJ(XGk?#|TKI2JdR@fen(s=e z@vnm#&qCR`3#wla)Ht_8_1^>4?*Nn@V^H7U1J$nvHP1X8hlPLt8q{}R_Vqu7TF+lW z&GQ}q{v6ai--npSTy$0W{%R<_tgml?ns*!A0Cz&odlYKDC!oH+A8OvyQ2n2Rn(sL% zeb0J+8p@AfhT8YvhSG1@zkeNS+&_mu0{;$bKmWn=9K4qLe?Zy)8k}GAZiQOUPS1X* zdEX7So-yA(1?8tlApcCxkMw^8YMf`F^nV^|zt2LZGGBli@2gPj`ZFl~zX|pI_n`La z2cDNRiTYm$wZ0pn^xN#)cR-EP2Q^OxYTkQ1YfyS0hl8-`+du31DwJK8q2~DtlpcQq zyWrnI&A0aIlI!4Gsowx)*IS|Hza47cL8$o-L9OdB)c41r>=OC+_dNawgexzDJ<+coORSPe85b*FAsBzyBSm?_YzMzIoNs%6&+}iQ#=QV%mwnel zeRnN%dP0t{*#Y(a0jU1t{{7wleFJJg-UD$_^M0s)AM@=mK&}5p&(FbQ)L(;I&xW^^ z?{`4e2jC5G0&3nSlpag|{bRoUS;!L13-D(6D%5(v0j1A5sPFzAYTV1Yq;!t1htjVL zs$VaZJ$L&0UMM^4_w9$F#<|PaPe8?y97?~3p~iU}YMmc}5j^AHujNu!zjdBBK#jKn zs{c;kz8`8o$Nc+g&v_{QPC@Da5Y%^1z|HXEzWzs0`}TFn5jFn^wGJ6mV7k&FF&xfJx|3P>od>LxJe+=bUU-$KILFw^N zzJAWXe*;S2%lKIH+z91Iw?XND2x`1(h|8FxkRxT5p!9wcs^2qE>-!{>eP4lE&oY!> zd=1Ku-+?-(|Losi%B8FJw?f&~Le1X;HSa$E{t(oBlkgzC3u--|^n4Mj|I5Dqc_=&n z9^3?9huXh4pzONFmh-HG8m9|N|1D7S?}3V66Hx1zhFaGw)I2AlzIz{(oj(9I{!^Z3 zpzQLSP!IfDff~0LUI6z%?f+iS0nh!OL+}FL{~V%c z12T(z6bTUR*-7Ma){m46!MdT?&&(nz3{!2(5nLwm# z`+3M0?r%51pGDr^QCD#fvKP_$m_|N~oJB518i<}>LAE3BK}L}-M9;Ix6w*?~Kc9j} zeEDj46p>HtKoaCQqUT9Odqv1_IXp+gOd@*bke$eSq#w~^k&h$4farNQ(v94K96$(p z?ksME+YtHB?T8-vc!cajo&tI}4sI zCbLo2u=Oww3(Zwl^I*YMlCV~&Q7oru7j2kjDT=PVvnWg7EDSV`gH6IkZ>zGQZiB)M z21Ve0Y_Ze~U1>&!QM(GZ>zzIInO*wX&TsC@`Sud`#!(Tf)fKjHy#IgScV&XymCDJ( z*`O7pQ%SpO>%r0>C2SVf(yUH5S;?YjjJ{DYmjw-&*Genc*(`0?Jj@ot%nYV>DQ(%s zASt|cZ80DEk@7pwdykiB24`EnSxn4es~O`(%;x8=uw&8>4=}Dg9mK8BRAT2_xv3;` z5k3RrD4#bKw>=%+u|Gm*{Ukxd6^by+&3^Cd90w{{nAGOY!7y%`gY2f=WSePLn4wwA zSFE|dxm>#qQ3hwvLL1=zQCPPTu2qlFm&33a74sZETY6?PXUj8^X4ZTBP@^1I$0*Fr zP=d{v#-Y95b}d~rL!9y`NX#&2tQE|K6j09FOKv$FCUeF7mdUU%!x$kq!zl-_RNfUs zGZHq^Y{|Cru-<3y6CSW#_cg<;#))9ezMkIo4{S6eX+4_NdT`W{bRo2LHlvIVgJnaj zSyrdU&B)SlFv9^JSsDqGmd8*LHjIlGqcZepxG3q=R@HjMwial_%aer}P4N-uigv+m z>(~i1me|3{aHU#NX=;41UnL??$56HrB=Ab1<8UTxw2u`A*fwN^Q<-pqHnqUz1|QdD6lGw0Eh3R#mJ z&{|;wJ1cL;ABrfMWBlk8wSp*`6Pl^FYBhr9Y%A_CCe2H@!by;853BB!yI*^3eOKLw zr=yx}V}7iMHm75|q0gRoAz58ZTo(9lIaFVX+*CukKX(u%LS_{Q=nUgkqQt6i>$*|g zXirZ-$z7=JG1V-ZWAk$}mE>GkVO@p)eq~clGu32>(y^!T30!k3*IAk(;>wUkX@9Dv zJ*OF2XKOkN*`etuD_WewV#(YcW~n8R#IA&)V}oDBK{JmO7V80#gyWB1(X3P5d9;mk zB4eTDY$b}YBJLedaj<}w$8TUA8Rl?n>;rTA9hbw z_jm7H?I%?^<=vH}Mg&ae`s~h`s919#Ou|fV)4gx0&+b#Sv%Njrb~42eOTflVe~suG z5ZFxrTo%re)_B_Gm&iYAEA50W4lmb*GP5XH=~jlDpd&de$MO9MBg7>Ath{4J7G)zhm4Jt;6&ULHA*B^%`BY> zYWRe1o~Y4`)Jz5^kS2j42FDgLy_P@IPUM2sOio1~13EjjbGe|M%mwXiE-2GE-CY?g zQ67dx{kJZM3o;wV5l6`B*lyzf%<5$e$H5m)y6;@Zz-}q{gk=+>mL0M@%R=AL^*jR1#lte+<6&h8erY=MGLPO|)^FCK11T&4NS z&M1|3euP%pJT@Y-$IOMnfR7YXHG&<9yjHQ{NbX(`j=YxL_1X^l_*l~ zS|VJre8$cPGZ7aCCf;o0fHliObw)Ht9F$hWIHZuubQ8{9nL?Il(dJR&GDyB*k?o_K z`WzD1Ewe}p%a6No(oQ7x%B7Ngwz63(EnHYZJELUt>k)sindN6=uEe0j^@mgP>7rHN z!WlT;N?6!1(IgDWq})2$InHyLXhx^}xpqlr6xde5%1;IL;D@K0HGgQ7Y-W~IaXjrz zH)+bUXvW7t<(yHai0*BL{0%h{m1$?x-fLGaQ%|>hEC{dO+RwXS5Kk+y6Ozf}C2 z#OBU_&OZjJAs?-* z)W=QCOj8~%zoK~FPEkvj;$6;PX+$4iSV>iVbeiR_ZK9L@!c~H*Xeu=_pSW_RD2>xq z)Y;Zwb1BU!{H1-kR|@SzwVJ!OyS(}G;gsMM)#cwMdsQN3DkK_Z_UfwcbZF)LF}1Rs z_EKGs)sxrC3C~gLS`YY0?NVA932$yCD~A(P%(87+``)PrjbQl|f>O?jTYgD-E^5&W z6S+eN3Bzw)RNemvuxsuT>CvCs&A@pXo+V#Z-kWx=(t84-$sHSV5WmOe)l`j? zd&>_3{zGE{pCM_^Q>DV0EmrsNzif#v{M^OMX{?47+X=BomQ(p_cmS%)wyz(9djB5y4)_4%&-?*D%iyz6 z&;L0*3%&sL{FibadzdhCIE|5m8y>QLiM!!cO+`(J>1?*(7~N2vL{ z1U1gT`}?m!jq`noY0Q}`%lj8V>1BQW3aD|{!zsB!m0&G#_W`^TZiy%*~Fk3x<2 zaVULHdj1}iAO9iLx<3P@U)SG%5vt#>z%$^hQ0w_k&)4AFsQ&=U{uko>8h0Jkd^ULw zLXCSf)O_~%_DLu|y$$kba(<-$hoJiT7?l2xL9O>m$WZ1vsQ&%|YF_^YrT@!N?|&O= zoqp(fE`#X#i=gKBPAL7Z_U+d~^)mo9P6cY*TRrPgdLM#Au-j8{UAj=?{4JCo zUxKUP*P+H+aY4!R;m=XO7|O2KL5+Vs)VM=XuRaImH!ncN;a5E0h;j73g<9{mQ15SpYhV@1zDJ?- zybEd`_d)glFx2yp`}U`O`)8r%^PJ~j`uop&eg$Fz^RG~L{GO+jRQ(*NaaKb4*Tqod zTm^a5TmzYc8S(9*X9{seGY2*A`=Ipr1jJi-Q15oY}D z<^2ZKI(MM@xf80NhoR1wPeIx5^S=E>$e($MAKCGL;5*@^JgjvegnF;SkMs?o-tR!o z`z|QGKLTavM}7TM{{Fmge+Fv(p7r(Tq5AnUlzy*3t=oS=+3~wDg=gRdntul6ug84- zPAI+ahU(`DsPX?4N}oT2()Wu{_Ib&-|0i_*4P88d{oFtI;?i!LAx&l{lpPvS@!^9| z{XYP;4v#?T`!tlDKJD*+21=jLL5=@+zWwWvr83`y+TY9QTzZ`Cxe~sc`o)l`n{80z z-3Fy+6Uq)b)VRle{Z6R$z8lIO4?&IdN01O>o`)LeWhguU2jtIu*YkW1OWA89)N=z+ z`=|mn&q>cF)IOMn8t*@u{L~}Zd+=u)IqU${+%wwKf|M&XxQfU2kY29`pS0S^= zM-g2QBAV;NNCO#1&OmzC2YlgpuL1rN@@q@#D&C0PgvhS@kPjm#k+YB%qU#fg*7H}8 zQDhaO>rrG9>8RqbC*f_rthL^c$S1Bv666q~>j6YQq5Y#h%vLiKh^{GQ6LL8+i0HD& zZzI2s=(-u{LvBEJBHB-E6SEpwk7#|bM|8;tBV-%$am1}Pyc=1AR1jUek+rTw?1hRA zbI4)j5u|rrM_~-P3)zGGDk4Aq5YoHur*OhoJ_x6M`3RgrZbb%=Er_n|$N}UsMAwBS z%r&0r0T2AP138BD%IB1@dN9xGqI*LC!?l$Oc5$ z2(kltV4#8dywAsIEC|(k0E=J&B&dIu4Bl%T&c{xe9_!Xq1vl>{tjH> z%WsA6Lw*-|KXNwmi-@i(kyM4t%;l?!v1Nyb~=^&X3a~pJuv=tOlJ&1LW8bjB!uoWhS*%suXaU-E% zW!!8Jl6n}M?bBiXaQT~??Qs}n{H6(y#;ION7=7`3ZlS6$WLIJj9q8~M#$Ip0{~NE{WR zT3un|>f=B8vkL>{u2c>lHiJ%#P9^Q4t%pj3l(1P?PqPM($x0SIWAu%JsVr#0yk1(t zHnX&4^DvtUGc%OhxwK;Gn<&9PCLen7|oAeVa22$ z9$;K~Ul4afQ;D5#<))HMMfePeqkP&_-102(jvWy?>n90Xu26(oZgzNAXFE{I!lXWJ zc7<`<>|!K5G_&60hg)U8+D2h+h7)YYFb=)>_G;;@8D^J9L1L=xu}&}*Qb0LxFS%tkOs0zI zwG&}ssu&?R)szibD({M+83|iyHfK9|*chVcdRxeX(MWC zJ~-+~IuqIki%~|0p|YXVE~}GcW@N4!9ApEJ%#DOe$78q%TgJtUQ5kwPoRuuqR@Fwt zvKDB>&7*}GP4N-uigv~=>z>1APhy8E)k>|R(&X6Cph`rbC0*H8kf3|!+9xJ<**a^1 zza?p5n`tL$^m9iWH#cKJp3lmM$ME26VsF)qX=(R%LUeGy_OcCTyyu0yW19iq(iotz z8Ok$4M(zcm#kO)YP$vvc^K1*NdHCnO`6|_3^V0Y_PVhETz1G3~V^_o}>YcJ*-I-qE zimJDJQ&EML%$!G0C}d5rLF&UE8V2ks2a?sX#9@K&mR${$$W1Mj`*Q|S zB4pNZfTdx)MwD3eZ5=m?8@=HPD7g!@{ic>hQ!IXNCX<}wDr~55`k76(&19P{O2?kW zCveTlTzhGfh$}-DrTwXv_S{F$+FSdgkQLe&WkrWwSj?IChFNL}B(W=D=-A*FanQ~q zg~didBw_obSJZT>JCC+ePGl@JopnSJX2iL}E)Hh!GFjT~RzgP{+k>Ior=}Gpor2xG z#FDujw2_+}Un|lyHk~9YmTfDs({s1yh-Yn^8Ozc`ct+oFqt8!l!1i{6UDv<9f8Fx& zP(i70B%g{JecL)yd0#CZu$7U);p$C;Lqp@0iHYT7d-}%1nMj7|Z@fzl5=#Y*?^^`j9mj968m?IFVy>LVbJo8_J(Tpra0

o+mPkIlcj?^*J6|w?sqRyHlawoqaAKdc`DeS2+xW`Gv@JS9+;gd$E7>^AZOeszj$Fmdg+z`A zUGC;mxrkJ*JAc|-ZZJtYF@5gq&SyG7TxwKg-Dl|9RGQ78t|p~*nb{F$EmtFumA!MW zYmPp(^Tnk>!9U=VJKc8KoeD0!Qz5qt37$-Nnj{aK#DUE_?IjtbOZWVZpfeLr1zBSu z^OKJ1`l#Xn2}Rh+IHtVooAJmJJBsq5)1KA z_1Wk(C`Ue}fo=BFO$#+*e#($PqSRQx#Jlqx&}6uAZ`C-67MmwzR-$Ug;o?i0f0$(9 z6sL~=0$9AqO1RUbmqrqgCzyD5p7^G0n>A^4%3QL8SEV@D?R_(8hbX$xzr6O;0dl+b zlxN)w3#lc2Zq-LfnOtfMoJK{T0XbhGu1lLo?ye4UlSu~p_V~1&mh9t zouimjQqTIdcEl*zF>Bq7$|`3FcEM5UDYr{Du`D9|~hkoSd zG@ieh_NGbqL?JsBeCmy)$%xB-)#`o1HIzAUN;0e%5Ohk$4CP$h1rUGB5-*4#g(k!P ziDvj{B9hl-zS7@L*=r=n6Reicj#ccX$DNrh!b?NTx47bXqEINGTNqOA=5l1E(#m_H zq{vWEDac~Bj7#e+Wt!pZGUo9RXL_26_X^6JyC>>=#OoED&+j))(8c6lXxD8f+LZf{ zO_%B5>l+8_F3;isTMQ(NCFf9nS1zX2i&16aBwezbdR|l}*vj?To5Yzi!*=y@f5{qt zk1d_Qd%|tqD@#8kA;9FWpM~g#w%vK<-ZDB~Md<5obAo5k>OSSRdW~dHiMdb3aUpk3 zl}Ai2>vZ^E290GZ&XeTg-KPkSE)Vx6`$@8KcEYLYxZ8$0*O%txtdHw>At{#wDl5nQ z%FDy0XK*!GbR(;TBG9He<+S3<1IsIuapz&bVJp@7gBm5^+@!Y7d_>Xtl_sR=5wcIltiH>LcXu`o+FS$piJ`{ sQQl4yeJ|*U3l;*{LdyU8)hhFTjYQ&4IvK_M7jS24cBcd&Q%lYN18BuLlmGw# literal 0 HcmV?d00001 diff --git a/locale/it_IT/LC_MESSAGES/petersql.mo b/locale/it_IT/LC_MESSAGES/petersql.mo new file mode 100644 index 0000000000000000000000000000000000000000..2efb70e144005609bd2fe441a7a98d6edb802bd5 GIT binary patch literal 8491 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 C*Pn#| literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml index d1d0475..e252424 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "petersql" +name = "PeterSQL" version = "0.1.0" description = "graphical client for database management" readme = "README.md" @@ -24,6 +24,7 @@ dev = [ "pytest-mock>=3.15.1", "pytest-xdist>=3.8.0", "testcontainers>=4.14.1", + "toml>=0.10.2", "types-pymysql>=1.1.0.20251220", "types-pyyaml>=6.0.12.20250915", "types-wxpython>=0.9.7", diff --git a/uv.lock b/uv.lock index d077a7b..6ed37a4 100644 --- a/uv.lock +++ b/uv.lock @@ -388,6 +388,7 @@ dev = [ { name = "pytest-mock" }, { name = "pytest-xdist" }, { name = "testcontainers" }, + { name = "toml" }, { name = "types-pymysql" }, { name = "types-pyyaml" }, { name = "types-wxpython" }, @@ -409,6 +410,7 @@ requires-dist = [ { name = "pyyaml", specifier = ">=6.0.3" }, { name = "sqlglot", specifier = ">=29.0.1" }, { name = "testcontainers", marker = "extra == 'dev'", specifier = ">=4.14.1" }, + { name = "toml", marker = "extra == 'dev'", specifier = ">=0.10.2" }, { name = "types-pymysql", marker = "extra == 'dev'", specifier = ">=1.1.0.20251220" }, { name = "types-pyyaml", marker = "extra == 'dev'", specifier = ">=6.0.12.20250915" }, { name = "types-wxpython", marker = "extra == 'dev'", specifier = ">=0.9.7" }, @@ -671,6 +673,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c8/31/5e7b23f9e43ff7fd46d243808d70c5e8daf3bc08ecf5a7fb84d5e38f7603/testcontainers-4.14.1-py3-none-any.whl", hash = "sha256:03dfef4797b31c82e7b762a454b6afec61a2a512ad54af47ab41e4fa5415f891", size = 125640, upload-time = "2026-01-31T23:13:45.464Z" }, ] +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, +] + [[package]] name = "types-pymysql" version = "1.1.0.20251220"