Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avaa kaava -ominaisuus #37

Merged
merged 2 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions arho_feature_template/core/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class UnexpectedNoneError(Exception):
"""Internal QGIS errors that should not be happened"""
145 changes: 145 additions & 0 deletions arho_feature_template/gui/load_plan_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
from importlib import resources

from qgis.core import QgsProviderRegistry
from qgis.PyQt import uic
from qgis.PyQt.QtCore import QRegularExpression, QSortFilterProxyModel, Qt
from qgis.PyQt.QtGui import QStandardItem, QStandardItemModel
from qgis.PyQt.QtWidgets import QComboBox, QDialog, QDialogButtonBox, QLineEdit, QMessageBox, QPushButton, QTableView

from arho_feature_template.core.exceptions import UnexpectedNoneError

ui_path = resources.files(__package__) / "load_plan_dialog.ui"

LoadPlanDialogBase, _ = uic.loadUiType(ui_path)


class PlanFilterProxyModel(QSortFilterProxyModel):
def filterAcceptsRow(self, source_row, source_parent): # noqa: N802
model = self.sourceModel()
if not model:
return False

filter_text = self.filterRegularExpression().pattern()
if not filter_text:
return True

for column in range(5):
index = model.index(source_row, column, source_parent)
data = model.data(index)
if data and filter_text.lower() in data.lower():
return True

return False


class LoadPlanDialog(QDialog, LoadPlanDialogBase): # type: ignore
connectionComboBox: QComboBox # noqa: N815
push_button_load: QPushButton
planTableView: QTableView # noqa: N815
searchLineEdit: QLineEdit # noqa: N815
buttonBox: QDialogButtonBox # noqa: N815

def __init__(self, parent, connections):
super().__init__(parent)
self.setupUi(self)

self._selected_plan_id = None

self.buttonBox.rejected.connect(self.reject)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)

self.push_button_load.clicked.connect(self.load_plans)
self.searchLineEdit.textChanged.connect(self.filter_plans)

self.connectionComboBox.addItems(connections)

self.planTableView.setSelectionMode(QTableView.SingleSelection)
self.planTableView.setSelectionBehavior(QTableView.SelectRows)
self.planTableView.selectionModel().selectionChanged.connect(self.on_selection_changed)

self.model = QStandardItemModel()
self.model.setColumnCount(5)
self.model.setHorizontalHeaderLabels(
[
"ID",
"Tuottajan kaavatunnus",
"Nimi",
"Kaavan elinkaaren tila",
"Kaavalaji",
]
)

self.filterProxyModel = PlanFilterProxyModel()
self.filterProxyModel.setSourceModel(self.model)
self.filterProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive)

self.planTableView.setModel(self.filterProxyModel)

def load_plans(self):
self.model.removeRows(0, self.model.rowCount())

selected_connection = self.connectionComboBox.currentText()
if not selected_connection:
self.planTableView.setModel(QStandardItemModel())
return

provider_registry = QgsProviderRegistry.instance()
if provider_registry is None:
raise UnexpectedNoneError
postgres_provider_metadata = provider_registry.providerMetadata("postgres")
if postgres_provider_metadata is None:
raise UnexpectedNoneError

try:
connection = postgres_provider_metadata.createConnection(selected_connection)
plans = connection.executeSql("""
SELECT
p.id,
p.producers_plan_identifier,
p.name ->> 'fin' AS name_fin,
l.name ->> 'fin' AS lifecycle_status_fin,
pt.name ->> 'fin' AS plan_type_fin
FROM
hame.plan p
LEFT JOIN
codes.lifecycle_status l
ON
p.lifecycle_status_id = l.id
LEFT JOIN
codes.plan_type pt
ON
p.plan_type_id = pt.id;
""")
for plan in plans:
self.model.appendRow([QStandardItem(column) for column in plan])

except Exception as e: # noqa: BLE001
QMessageBox.critical(self, "Error", f"Failed to load plans: {e}")
self.model.removeRows(0, self.model.rowCount())

def filter_plans(self):
search_text = self.searchLineEdit.text()
if search_text:
search_regex = QRegularExpression(search_text)
self.filterProxyModel.setFilterRegularExpression(search_regex)
else:
self.filterProxyModel.setFilterRegularExpression("")

def on_selection_changed(self):
# Enable the OK button only if a row is selected
selection = self.planTableView.selectionModel().selectedRows()
ok_button = self.buttonBox.button(QDialogButtonBox.Ok)
if selection:
selected_row = selection[0].row()
self._selected_plan_id = self.planTableView.model().index(selected_row, 0).data()
ok_button.setEnabled(True)
else:
self._selected_plan_id = None
ok_button.setEnabled(False)

def get_selected_connection(self):
return self.connectionComboBox.currentText()

def get_selected_plan_id(self):
return self._selected_plan_id
147 changes: 147 additions & 0 deletions arho_feature_template/gui/load_plan_dialog.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoadPlanDialog</class>
<widget class="QDialog" name="LoadPlanDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>800</width>
<height>600</height>
</size>
</property>
<property name="windowTitle">
<string>Avaa kaava</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Valitse tietokantayhteys:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="connectionComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="push_button_load">
<property name="text">
<string>Lataa kaavat</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="spacer1">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_search">
<property name="text">
<string>Etsi kaavoja:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="searchLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="placeholderText">
<string>Etsi kaavoja...</string>
</property>
</widget>
</item>
<item>
<spacer name="spacer2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_plan_table">
<property name="text">
<string>Kaavat:</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="planTableView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<spacer name="spacer3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
33 changes: 28 additions & 5 deletions arho_feature_template/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@

from qgis.PyQt.QtCore import QCoreApplication, Qt, QTranslator
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction, QWidget
from qgis.PyQt.QtWidgets import QAction, QDialog, QMessageBox, QWidget
from qgis.utils import iface

from arho_feature_template.core.feature_template_library import FeatureTemplater, TemplateGeometryDigitizeMapTool
from arho_feature_template.core.new_plan import NewPlan
from arho_feature_template.core.update_plan import LandUsePlan, update_selected_plan
from arho_feature_template.gui.load_plan_dialog import LoadPlanDialog
from arho_feature_template.qgis_plugin_tools.tools.custom_logging import setup_logger, teardown_logger
from arho_feature_template.qgis_plugin_tools.tools.i18n import setup_translation
from arho_feature_template.qgis_plugin_tools.tools.resources import plugin_name
from arho_feature_template.utils.db_utils import get_existing_database_connection_names
from arho_feature_template.utils.misc_utils import PLUGIN_PATH

if TYPE_CHECKING:
Expand Down Expand Up @@ -134,6 +137,7 @@ def initGui(self) -> None: # noqa N802

iface.mapCanvas().mapToolSet.connect(self.templater.digitize_map_tool.deactivate)

# Add main plugin action to the toolbar
self.template_dock_action = self.add_action(
"",
"Feature Templates",
Expand All @@ -146,11 +150,11 @@ def initGui(self) -> None: # noqa N802

self.new_land_use_plan_action = self.add_action(
plan_icon_path,
"Create New Plan",
self.digitize_new_plan,
"Create New Land Use Plan",
self.add_new_plan,
add_to_menu=True,
add_to_toolbar=True,
status_tip="Create a new plan",
status_tip="Create a new land use plan",
)

self.load_land_use_plan_action = self.add_action(
Expand All @@ -165,12 +169,31 @@ def on_map_tool_changed(self, new_tool: QgsMapTool, old_tool: QgsMapTool) -> Non
if not isinstance(new_tool, TemplateGeometryDigitizeMapTool):
self.template_dock_action.setChecked(False)

def digitize_new_plan(self):
def add_new_plan(self):
self.new_plan.add_new_plan()

def load_existing_land_use_plan(self) -> None:
"""Open existing land use plan."""

connections = get_existing_database_connection_names()

if not connections:
QMessageBox.critical(None, "Error", "No database connections found.")
return

dialog = LoadPlanDialog(None, connections)

if dialog.exec_() == QDialog.Accepted:
selected_plan_id = dialog.get_selected_plan_id()

if not selected_plan_id:
QMessageBox.critical(None, "Error", "No plan was selected.")
return

plan = LandUsePlan(selected_plan_id)

update_selected_plan(plan)

def unload(self) -> None:
"""Removes the plugin menu item and icon from QGIS GUI."""
for action in self.actions:
Expand Down
26 changes: 26 additions & 0 deletions arho_feature_template/utils/db_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from __future__ import annotations

import logging

from qgis.core import QgsProviderRegistry

from arho_feature_template.core.exceptions import UnexpectedNoneError

LOGGER = logging.getLogger("LandUsePlugin")


def get_existing_database_connection_names() -> list[str]:
"""
Retrieve the list of existing database connections from QGIS settings.

:return: A set of available PostgreSQL connection names.
"""

provider_registry = QgsProviderRegistry.instance()
if provider_registry is None:
raise UnexpectedNoneError
postgres_provider_metadata = provider_registry.providerMetadata("postgres")
if postgres_provider_metadata is None:
raise UnexpectedNoneError

return list(postgres_provider_metadata.dbConnections(False))
Loading