-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
697 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
import os | ||
|
||
from qgis.PyQt import uic | ||
from qgis.PyQt.QtCore import QSettings | ||
from qgis.PyQt.QtWidgets import QWizard, QFileDialog | ||
|
||
from qgis.gui import QgsFileWidget | ||
from qgis.core import QgsProject, QgsProviderRegistry, QgsApplication, QgsAuthMethodConfig | ||
|
||
from .utils import get_mergin_auth | ||
|
||
base_dir = os.path.dirname(__file__) | ||
ui_direction_page, base_direction_page = uic.loadUiType(os.path.join(base_dir, "ui", "ui_sync_direction_page.ui")) | ||
ui_gpkg_select_page, base_gpkg_select_page = uic.loadUiType(os.path.join(base_dir, "ui", "ui_gpkg_selection_page.ui")) | ||
ui_db_select_page, base_db_select_page = uic.loadUiType(os.path.join(base_dir, "ui", "ui_db_selection_page.ui")) | ||
ui_config_page, base_config_page = uic.loadUiType(os.path.join(base_dir, "ui", "ui_config_file_page.ui")) | ||
|
||
SYNC_DIRECTION_PAGE = 0 | ||
GPKG_SELECT_PAGE = 1 | ||
DB_SELECT_PAGE = 2 | ||
CONFIG_PAGE = 3 | ||
|
||
|
||
class SyncDirectionPage(ui_direction_page, base_direction_page): | ||
"""Initial wizard page with sync direction selector.""" | ||
|
||
def __init__(self, parent=None): | ||
super().__init__(parent) | ||
self.setupUi(self) | ||
self.parent = parent | ||
|
||
self.ledit_sync_direction.hide() | ||
self.registerField("init_from*", self.ledit_sync_direction) | ||
|
||
self.radio_from_project.toggled.connect(self.update_direction) | ||
self.radio_from_db.toggled.connect(self.update_direction) | ||
|
||
def update_direction(self, checked): | ||
if self.radio_from_project.isChecked(): | ||
self.ledit_sync_direction.setText("gpkg") | ||
else: | ||
self.ledit_sync_direction.setText("db") | ||
|
||
def nextId(self): | ||
"""Decide about the next page based on selected sync direction.""" | ||
if self.radio_from_project.isChecked(): | ||
return GPKG_SELECT_PAGE | ||
return DB_SELECT_PAGE | ||
|
||
|
||
class GpkgSelectionPage(ui_gpkg_select_page, base_gpkg_select_page): | ||
"""Wizard page for selecting GPKG file.""" | ||
|
||
def __init__(self, parent=None): | ||
super().__init__(parent) | ||
self.setupUi(self) | ||
self.parent = parent | ||
|
||
self.ledit_gpkg_file.hide() | ||
self.registerField("sync_file", self.ledit_gpkg_file) | ||
|
||
def initializePage(self): | ||
direction = self.field("init_from") | ||
if direction == "gpkg": | ||
self.label.setText("Pick a GeoPackage file that contains data to be synchronized") | ||
self.file_edit_gpkg.setStorageMode(QgsFileWidget.GetFile) | ||
else: | ||
self.label.setText("Pick a GeoPackage file that will contain synchronized data") | ||
self.file_edit_gpkg.setStorageMode(QgsFileWidget.SaveFile) | ||
|
||
self.file_edit_gpkg.setDialogTitle(self.tr('Select file')) | ||
settings = QSettings() | ||
self.file_edit_gpkg.setDefaultRoot(settings.value('Mergin/lastUsedDirectory', QgsProject.instance().homePath(), str)) | ||
self.file_edit_gpkg.setFilter('GeoPackage files (*.gpkg *.GPKG)') | ||
self.file_edit_gpkg.fileChanged.connect(self.ledit_gpkg_file.setText) | ||
|
||
def nextId(self): | ||
"""Decide about the next page based on selected sync direction.""" | ||
direction = self.field("init_from") | ||
if direction == "gpkg": | ||
return DB_SELECT_PAGE | ||
return CONFIG_PAGE | ||
|
||
|
||
class DatabaseSelectionPage(ui_db_select_page, base_db_select_page): | ||
"""Wizard page for selecting database and schema.""" | ||
|
||
def __init__(self, parent=None): | ||
super().__init__(parent) | ||
self.setupUi(self) | ||
self.parent = parent | ||
|
||
self.ledit_sync_schema.hide() | ||
self.populate_connections() | ||
|
||
self.registerField("connection*", self.cmb_db_conn, "currentText", self.cmb_db_conn.currentTextChanged) | ||
self.registerField("sync_schema*", self.ledit_sync_schema) | ||
self.registerField("internal_schema*", self.line_edit_internal_schema) | ||
self.cmb_db_conn.currentTextChanged.connect(self.populate_schemas) | ||
|
||
def initializePage(self): | ||
self.direction = self.field("init_from") | ||
if self.direction == "gpkg": | ||
self.label_sync_schema.setText("Schema name for sync (will be created)") | ||
# use line edit for schema name | ||
self.stackedWidget.setCurrentIndex(0) | ||
self.line_edit_sync_schema.textChanged.connect(self.schema_changed) | ||
else: | ||
self.label_sync_schema.setText("Existing schema name for sync") | ||
# use combobox to select existing schema | ||
self.stackedWidget.setCurrentIndex(1) | ||
self.cmb_sync_schema.currentTextChanged.connect(self.schema_changed) | ||
|
||
# pre-fill internal schema name | ||
self.line_edit_internal_schema.setText("merginmaps_db_sync") | ||
|
||
def cleanupPage(self): | ||
if self.direction == "gpkg": | ||
self.line_edit_sync_schema.textChanged.disconnect() | ||
else: | ||
self.cmb_sync_schema.currentTextChanged.disconnect() | ||
|
||
def schema_changed(self, schema_name): | ||
self.line_edit_internal_schema.setText(f"{schema_name}_db_sync") | ||
self.ledit_sync_schema.setText(schema_name) | ||
|
||
def populate_connections(self): | ||
metadata = QgsProviderRegistry.instance().providerMetadata("postgres") | ||
connections = metadata.dbConnections() | ||
for k, v in connections.items(): | ||
self.cmb_db_conn.addItem(k, v) | ||
|
||
self.cmb_db_conn.setCurrentIndex(-1) | ||
|
||
def populate_schemas(self): | ||
connection = self.cmb_db_conn.currentData() | ||
if connection: | ||
self.cmb_sync_schema.clear() | ||
self.cmb_sync_schema.addItems(connection.schemas()) | ||
|
||
def nextId(self): | ||
"""Decide about the next page based on selected sync direction.""" | ||
if self.direction == "gpkg": | ||
return CONFIG_PAGE | ||
return GPKG_SELECT_PAGE | ||
|
||
|
||
class ConfigFilePage(ui_config_page, base_config_page): | ||
"""Wizard page with generated config file.""" | ||
|
||
def __init__(self, project_name, parent=None): | ||
super().__init__(parent) | ||
self.setupUi(self) | ||
self.parent = parent | ||
|
||
self.project_name = project_name | ||
|
||
self.btn_save_config.clicked.connect(self.save_config) | ||
|
||
def initializePage(self): | ||
self.text_config_file.setPlainText(self.generate_config()) | ||
|
||
def save_config(self): | ||
file_path, _ = QFileDialog.getSaveFileName(self, "Save file", os.path.expanduser('~'), "YAML files (*.yml *.YML)") | ||
if file_path: | ||
if not file_path.lower().endswith(".yml"): | ||
file_path += ".yml" | ||
|
||
with open(file_path, "w", encoding="utf-8") as f: | ||
f.write(self.text_config_file.toPlainText()) | ||
|
||
def generate_config(self): | ||
url, user, password = get_mergin_auth() | ||
conn_name = self.field("connection") | ||
metadata = QgsProviderRegistry.instance().providerMetadata("postgres") | ||
conn = metadata.dbConnections()[conn_name] | ||
decoded_uri = metadata.decodeUri(conn.uri()) | ||
conn_string = [] | ||
if "host" in decoded_uri: | ||
conn_string.append(f"host={decoded_uri['host']}") | ||
if "dbname" in decoded_uri: | ||
conn_string.append(f"dbname={decoded_uri['dbname']}") | ||
|
||
if "authcfg" in decoded_uri: | ||
auth_id = decoded_uri["authcfg"] | ||
auth_manager = QgsApplication.authManager() | ||
auth_config = QgsAuthMethodConfig() | ||
auth_manager.loadAuthenticationConfig(auth_id, auth_config, True) | ||
conn_string.append(f"user={auth_config.config('username')}") | ||
conn_string.append(f"password={auth_config.config('password')}") | ||
else: | ||
if "username" in decoded_uri: | ||
user_name = decoded_uri['username'].strip("'") | ||
conn_string.append(f"user={user_name}") | ||
if "password" in decoded_uri: | ||
password = decoded_uri['password'].strip("'") | ||
conn_string.append(f"password={password}") | ||
|
||
cfg = ("mergin:\n" | ||
f" url: {url}\n" | ||
f" username: {user}\n" | ||
f" password: {password}\n" | ||
f"init_from: {self.field('init_from')}\n" | ||
"connections:\n" | ||
f" - driver: postgres\n" | ||
f" conn_info: \"{' '.join(conn_string)}\"\n" | ||
f" modified: {self.field('sync_schema')}\n" | ||
f" base: {self.field('internal_schema')}\n" | ||
f" mergin_project: {self.project_name}\n" | ||
f" sync_file: {self.field('sync_file')}\n" | ||
f"daemon:\n" | ||
f" sleep_time: 10\n" | ||
) | ||
|
||
return cfg | ||
|
||
|
||
class DbSyncConfigWizard(QWizard): | ||
"""Wizard for configuring db-sync.""" | ||
|
||
def __init__(self, project_name, parent=None): | ||
"""Create a wizard""" | ||
super().__init__(parent) | ||
|
||
self.setWindowTitle("Create db-sync configuration") | ||
|
||
self.project_name = project_name | ||
|
||
self.start_page = SyncDirectionPage(self) | ||
self.setPage(SYNC_DIRECTION_PAGE, self.start_page) | ||
|
||
self.gpkg_page = GpkgSelectionPage(parent=self) | ||
self.setPage(GPKG_SELECT_PAGE, self.gpkg_page) | ||
|
||
self.db_page = DatabaseSelectionPage(parent=self) | ||
self.setPage(DB_SELECT_PAGE, self.db_page) | ||
|
||
self.config_page = ConfigFilePage(self.project_name, parent=self) | ||
self.setPage(CONFIG_PAGE, self.config_page) |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.