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

27 Tallenna kaava jsonina #76

Merged
merged 3 commits into from
Dec 4, 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
94 changes: 94 additions & 0 deletions arho_feature_template/core/lambda_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from __future__ import annotations

import json

from qgis.PyQt.QtCore import QByteArray, QObject, QUrl, pyqtSignal
from qgis.PyQt.QtNetwork import QNetworkAccessManager, QNetworkProxy, QNetworkReply, QNetworkRequest
from qgis.PyQt.QtWidgets import QMessageBox

from arho_feature_template.utils.misc_utils import get_active_plan_id, get_plan_name, get_settings


class LambdaService(QObject):
jsons_received = pyqtSignal(dict, dict)

def __init__(self):
super().__init__() # Ensure QObject initialization
# Init network manager
self.network_manager = QNetworkAccessManager()

def send_request(self, action: str, plan_id: str):
"""Sends a request to the lambda function."""

proxy_host, proxy_port, self.lambda_url = get_settings()

# Initialize or reset proxy each time a request is sent. Incase settings have changed.
if proxy_host and proxy_port:
# Set up SOCKS5 Proxy if values are provided
proxy = QNetworkProxy()
proxy.setType(QNetworkProxy.Socks5Proxy)
proxy.setHostName(proxy_host)
proxy.setPort(int(proxy_port))
self.network_manager.setProxy(proxy)
else:
self.network_manager.setProxy(QNetworkProxy())

payload = {"action": action, "plan_uuid": plan_id}
payload_bytes = QByteArray(json.dumps(payload).encode("utf-8"))

request = QNetworkRequest(QUrl(self.lambda_url))
request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json")

reply = self.network_manager.post(request, payload_bytes)

# Connect reply signal to handle the response
reply.finished.connect(lambda: self._process_reply(reply))

def _process_reply(self, reply: QNetworkReply):
"""Processes the reply from the lambda and emits signal."""
plan_id = get_active_plan_id()

if reply.error() != QNetworkReply.NoError:
error_string = reply.errorString()
QMessageBox.critical(None, "API Virhe", f"Lambdan kutsu epäonnistui: {error_string}")
reply.deleteLater()
return

try:
response_data = reply.readAll().data().decode("utf-8")
response_json = json.loads(response_data)

# Determine if the proxy is set up.
if hasattr(self, "network_manager") and self.network_manager.proxy().type() == QNetworkProxy.Socks5Proxy:
# If proxy has been set up, retrieve 'details' directly
details = response_json.get("details", {})
else:
# If proxy has not been set up (using local docker lambda), the response includes 'body'.
# In this case we need to retrieve 'details' from 'body' first.
body = response_json.get("body", {})
details = body.get("details", {})

# Extract the plan JSON for the given plan_id
plan_json = details.get(plan_id, {})
if not isinstance(plan_json, dict):
plan_json = {}

outline_json = {}
if plan_json:
geographical_area = plan_json.get("geographicalArea")
if geographical_area:
outline_name = get_plan_name(plan_id, language="fin")
outline_json = {
"type": "Feature",
"properties": {"name": outline_name},
"srid": geographical_area.get("srid"),
"geometry": geographical_area.get("geometry"),
}

# Emit the signal with the two JSONs
self.jsons_received.emit(plan_json, outline_json)

except json.JSONDecodeError as e:
QMessageBox.critical(None, "JSON Virhe", f"Failed to parse response JSON: {e}")
finally:
reply.deleteLater()
46 changes: 45 additions & 1 deletion arho_feature_template/core/plan_manager.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lambdoille kommunikoinnin voisi siirtää kokonaan omaan moduuliin. Plan managerissa voisi edelleen olla get_plan_json(), joka sitten kutsuisi tätä toisessa moduulissa olevaa koodia.

Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
from __future__ import annotations

import json

from qgis.core import QgsExpressionContextUtils, QgsProject, QgsVectorLayer
from qgis.PyQt.QtWidgets import QDialog, QMessageBox
from qgis.utils import iface

from arho_feature_template.core.lambda_service import LambdaService
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.gui.serialize_plan import SerializePlan
from arho_feature_template.utils.db_utils import get_existing_database_connection_names
from arho_feature_template.utils.misc_utils import get_layer_by_name, handle_unsaved_changes
from arho_feature_template.utils.misc_utils import get_active_plan_id, get_layer_by_name, handle_unsaved_changes


class PlanManager:
def __init__(self):
self.json_plan_path = None
self.json_plan_outline_path = None
self.kaava_layer = get_layer_by_name("Kaava")

def add_new_plan(self):
Expand Down Expand Up @@ -98,3 +106,39 @@ def commit_all_editable_layers(self):
for layer in QgsProject.instance().mapLayers().values():
if isinstance(layer, QgsVectorLayer) and layer.isEditable():
layer.commitChanges()

def get_plan_json(self):
"""Serializes plan and plan outline to JSON"""
dialog = SerializePlan()
if dialog.exec_() == QDialog.Accepted:
self.json_plan_path = dialog.plan_path_edit.text()
self.json_plan_outline_path = dialog.plan_outline_path_edit.text()

plan_id = get_active_plan_id()
if not plan_id:
QMessageBox.critical(None, "Virhe", "Ei aktiivista kaavaa.")
return

self.lambda_service = LambdaService()
self.lambda_service.jsons_received.connect(self.save_plan_jsons)
self.lambda_service.send_request("get_plans", plan_id)

def save_plan_jsons(self, plan_json, outline_json):
"""This slot saves the plan and outline JSONs to files."""
if plan_json is None or outline_json is None:
QMessageBox.critical(None, "Virhe", "Kaava tai sen ulkoraja ei löytynyt.")
return

# Retrieve paths
if self.json_plan_path is None or self.json_plan_outline_path is None:
QMessageBox.critical(None, "Virhe", "Tiedostopolut eivät ole saatavilla.")
return

# Save the JSONs
with open(self.json_plan_path, "w", encoding="utf-8") as full_file:
json.dump(plan_json, full_file, ensure_ascii=False, indent=2)

with open(self.json_plan_outline_path, "w", encoding="utf-8") as outline_file:
json.dump(outline_json, outline_file, ensure_ascii=False, indent=2)

QMessageBox.information(None, "Tallennus onnistui", "Kaava ja sen ulkoraja tallennettu onnistuneesti.")
15 changes: 9 additions & 6 deletions arho_feature_template/gui/plugin_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,19 @@ def load_settings(self):
"""Load settings from QSettings with default values."""
settings = QSettings("ArhoFeatureTemplate")

lambda_host = settings.value("lambda_host", "localhost")
lambda_port = settings.value("lambda_port", "5435")
proxy_host = settings.value("proxy_host", "localhost")
proxy_port = settings.value("proxy_port", "5443")
lambda_url = settings.value("lambda_url", "https://t5w26iqnsf.execute-api.eu-central-1.amazonaws.com/v0/ryhti")

self.hostInput.setText(lambda_host)
self.portInput.setText(lambda_port)
self.hostInput.setText(proxy_host)
self.portInput.setText(proxy_port)
self.lambdaInput.setText(lambda_url)

def save_settings(self):
"""Save settings to QSettings."""
settings = QSettings("ArhoFeatureTemplate")
settings.setValue("lambda_host", self.hostInput.text())
settings.setValue("lambda_port", self.portInput.text())
settings.setValue("proxy_host", self.hostInput.text())
settings.setValue("proxy_port", self.portInput.text())
settings.setValue("lambda_url", self.lambdaInput.text())

self.accept()
14 changes: 12 additions & 2 deletions arho_feature_template/gui/plugin_settings.ui
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<item>
<widget class="QLabel" name="labelHost">
<property name="text">
<string>Lambdan isäntä:</string>
<string>Proxy isäntä:</string>
</property>
</widget>
</item>
Expand All @@ -27,13 +27,23 @@
<item>
<widget class="QLabel" name="labelPort">
<property name="text">
<string>Lambdan portti:</string>
<string>Proxy portti:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="portInput"/>
</item>
<item>
<widget class="QLabel" name="labelLambda">
<property name="text">
<string>Lambdan osoite:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lambdaInput"/>
</item>
<item>
<widget class="QPushButton" name="saveButton">
<property name="text">
Expand Down
50 changes: 50 additions & 0 deletions arho_feature_template/gui/serialize_plan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from importlib import resources

from qgis.PyQt import uic
from qgis.PyQt.QtWidgets import QDialog, QDialogButtonBox, QFileDialog, QLineEdit, QPushButton

ui_path = resources.files(__package__) / "serialize_plan.ui"
FormClass, _ = uic.loadUiType(ui_path)


class SerializePlan(QDialog, FormClass): # type: ignore
plan_outline_path_edit: QLineEdit
plan_path_edit: QLineEdit
plan_outline_select_button: QPushButton
plan_select_button: QPushButton

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

self.plan_outline_select_button.clicked.connect(self.select_plan_outline_file)
self.plan_select_button.clicked.connect(self.select_plan_file)

# Disable Save button initially
self.buttonBox.button(QDialogButtonBox.Save).setEnabled(False)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)

def select_plan_outline_file(self):
file_path, _ = QFileDialog.getSaveFileName(
self, "Valitse kaavan ulkorajan tallennuspolku", "", "JSON Files (*.json)"
)
if file_path:
self.plan_outline_path_edit.setText(file_path)
self.check_inputs()

def select_plan_file(self):
file_path, _ = QFileDialog.getSaveFileName(self, "Valitse kaavan tallennuspolku", "", "JSON Files (*.json)")
if file_path:
self.plan_path_edit.setText(file_path)
self.check_inputs()

def check_inputs(self):
# Enable Save button if both text fields have paths
if self.plan_outline_path_edit.text() and self.plan_path_edit.text():
self.buttonBox.button(QDialogButtonBox.Save).setEnabled(True)
else:
self.buttonBox.button(QDialogButtonBox.Save).setEnabled(False)

def get_paths(self):
return self.plan_outline_path_edit.text(), self.plan_path_edit.text()
89 changes: 89 additions & 0 deletions arho_feature_template/gui/serialize_plan.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>558</width>
<height>193</height>
</rect>
</property>
<property name="windowTitle">
<string>Tallenna kaava JSON</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>20</x>
<y>150</y>
<width>521</width>
<height>32</height>
</rect>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>521</width>
<height>120</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="plan_outline_label">
<property name="text">
<string>Kaavan ulkorajan tallennuspolku:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="plan_outline_path_edit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="plan_outline_select_button">
<property name="text">
<string>Valitse...</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="plan_label">
<property name="text">
<string>Kaavan tallennuspolku:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="plan_path_edit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="plan_select_button">
<property name="text">
<string>Valitse...</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
13 changes: 13 additions & 0 deletions arho_feature_template/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,15 @@ def initGui(self) -> None: # noqa N802
add_to_toolbar=True,
)

self.serialize_plan_action = self.add_action(
"",
text="Tallenna kaava JSON",
triggered_callback=self.serialize_plan,
add_to_menu=True,
add_to_toolbar=True,
status_tip="Tallenna aktiivinen kaava JSON muodossa",
)

self.plugin_settings_action = self.add_action(
"",
text="Asetukset",
Expand All @@ -191,6 +200,10 @@ def add_new_plan(self):
def load_existing_land_use_plan(self):
self.plan_manager.load_land_use_plan()

def serialize_plan(self):
"""Serializes currently active plan."""
self.plan_manager.get_plan_json()

def open_settings(self):
"""Open the plugin settings dialog."""
settings = PluginSettings()
Expand Down
Loading
Loading