Skip to content

Commit

Permalink
Rm global config object, refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
alukach committed Feb 10, 2025
1 parent 05f4d20 commit 409b238
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 72 deletions.
12 changes: 5 additions & 7 deletions ee_plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,9 @@ def classFactory(iface): # pylint: disable=invalid-name
ee.data.setUserAgent(user_agent)

# authenticate and initialize EE
from . import ee_auth
from . import ee_auth, config, ee_plugin

ee_auth.authenticate_and_set_project()

# start
from .ee_plugin import GoogleEarthEnginePlugin

return GoogleEarthEnginePlugin(iface)
ee_config = config.EarthEngineConfig()
ee_auth.ensure_authenticated(ee_config)
ee_auth.ee_initialize_with_project(ee_config)
return ee_plugin.GoogleEarthEnginePlugin(iface, ee_config=ee_config)
7 changes: 2 additions & 5 deletions ee_plugin/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ class EarthEngineConfigDict(TypedDict):
project: str


class ConfigSignals(QObject):
class _ConfigSignals(QObject):
project_changed = pyqtSignal()


@dataclass
class EarthEngineConfig:
credentials_path: str = field(default_factory=ee.oauth.get_credentials_path)
signals: ConfigSignals = field(default_factory=ConfigSignals, init=False)
signals: _ConfigSignals = field(default_factory=_ConfigSignals, init=False)

def read(self) -> Optional[EarthEngineConfigDict]:
"""Read the credentials file."""
Expand Down Expand Up @@ -47,6 +47,3 @@ def project_id(self) -> Optional[str]:
config = self.read()
if config:
return config.get("project")


ee_config = EarthEngineConfig()
69 changes: 25 additions & 44 deletions ee_plugin/ee_auth.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import ee
from typing import Optional
from qgis.PyQt.QtWidgets import QInputDialog, QLineEdit, QMessageBox

from .config import ee_config
from .config import EarthEngineConfig


def ee_authenticate() -> bool:
def ee_authenticate(ee_config: EarthEngineConfig) -> bool:
"""show a dialog to allow users to start or cancel the authentication process"""

msg = """This plugin uses Google Earth Engine API and it looks like it is not yet authenticated on this machine.<br>
Expand Down Expand Up @@ -35,8 +36,21 @@ def ee_authenticate() -> bool:
return bool(ee_config.read())


def ee_initialize_with_project(project, force=False):
def ee_initialize_with_project(ee_config: EarthEngineConfig, force=False) -> None:
"""Initializes EE with a project or ask user to specify project if there is no project set"""
project_id = ee_config.project_id

if not project_id or force:
project_id = prompt_for_project(project_id)
if not project_id:
return

ee.Initialize(project=project_id)
ee_config.save_project(project_id)


def prompt_for_project(cur_project: Optional[str]) -> Optional[str]:
"""Prompt user to specify project"""

msg_no_project = """Google Cloud Project is empty.<br>
<br>
Expand All @@ -55,55 +69,22 @@ def ee_initialize_with_project(project, force=False):
<br>
Google Cloud Project:"""

if project is None:
msg = msg_no_project
elif force:
msg = msg_with_project
else:
# project is set and no force - initialize and return
ee.Initialize(project=project)
return

(project, ok) = QInputDialog.getText(
None, "Select Earth Engine project", msg, QLineEdit.Normal, project
msg = msg_with_project if cur_project else msg_no_project
(project, _ok) = QInputDialog.getText(
None, "Select Earth Engine project", msg, QLineEdit.Normal, cur_project
)
return project

if not ok:
return # no project is configured and cancelled, you're on your own

# sanity check
if len(project) == 0:
return

ee.Initialize(project=project)
ee_config.save_project(project)


def authenticate_and_set_project():
config = ee_config.read()

def ensure_authenticated(ee_config: EarthEngineConfig) -> None:
"""Ensure that the user is authenticated with Earth Engine"""
# Trigger authentication if there is no config (ie not authenticated)
if not config:
auth_success = ee_authenticate()
if not ee_config.read():
auth_success = ee_authenticate(ee_config)

# authentication failed
if not auth_success:
raise ee.EEException(
"Can not initialize Google Earth Engine. Please make sure you can "
"access Earth Engine, restart QGIS, and re-enable EE plugin."
)

# initialize EE with existing project or select a new one if there is no project
project = config.get("project") if config else None
ee_initialize_with_project(project)


def select_project():
# read existing project
config = ee_config.read()

project = None
if config is not None:
project = config.get("project")

ee_initialize_with_project(project, force=True)
32 changes: 19 additions & 13 deletions ee_plugin/ee_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction

from .config import ee_config
from .config import EarthEngineConfig


PLUGIN_DIR = os.path.dirname(__file__)
Expand All @@ -32,7 +32,9 @@
class GoogleEarthEnginePlugin(object):
"""QGIS Plugin Implementation."""

def __init__(self, iface):
ee_config: EarthEngineConfig

def __init__(self, iface, ee_config: EarthEngineConfig):
"""Constructor.
:param iface: An interface instance that will be passed to this class
Expand All @@ -45,7 +47,7 @@ def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface

# initialize plugin directory
self.ee_config = ee_config
self.plugin_dir = os.path.dirname(__file__)

# initialize locale
Expand All @@ -67,7 +69,9 @@ def __init__(self, iface):
provider.register_data_provider()

# Reload the plugin when the config changes
ee_config.signals.project_changed.connect(self._reload)
self.ee_config.signals.project_changed.connect(
self._refresh_project_button_text
)

# noinspection PyMethodMayBeStatic
def tr(self, message):
Expand Down Expand Up @@ -102,7 +106,7 @@ def initGui(self):
gcp_project_icon_path = ":/plugins/ee_plugin/icons/google-cloud-project.svg"
self.cmd_set_cloud_project = QAction(
QIcon(gcp_project_icon_path),
f"Set Project: {ee_config.project_id or '...'}",
self._project_button_text,
self.iface.mainWindow(),
)
self.cmd_set_cloud_project.triggered.connect(self.run_cmd_set_cloud_project)
Expand All @@ -115,10 +119,14 @@ def initGui(self):
# Register signal to initialize EE layers on project load
self.iface.projectRead.connect(self.updateLayers)

def _reload(self):
"""Reload the plugin if config changes."""
self.unload()
self.initGui()
@property
def _project_button_text(self):
"""Get the text for the project button."""
return f"Set Project: {self.ee_config.project_id or '...'}"

def _refresh_project_button_text(self):
"""Refresh the text for the project button."""
self.cmd_set_cloud_project.setText(self._project_button_text)

def run_cmd_ee_user_guide(self):
# open user guide in external web browser
Expand All @@ -127,18 +135,16 @@ def run_cmd_ee_user_guide(self):
def run_cmd_sign_in(self):
import ee

from ee_plugin import ee_auth # type: ignore

# reset authentication by forcing sign in
ee.Authenticate(auth_mode="localhost", force=True)

# after resetting authentication, select Google Cloud project again
ee_auth.select_project()
self.run_cmd_set_cloud_project()

def run_cmd_set_cloud_project(self):
from ee_plugin import ee_auth # type: ignore

ee_auth.select_project()
ee_auth.ee_initialize_with_project(self.ee_config, force=True)

def check_version(self):
global version_checked
Expand Down
11 changes: 8 additions & 3 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from qgis.utils import plugins
from PyQt5.QtCore import QSettings, QCoreApplication

from ee_plugin.ee_plugin import GoogleEarthEnginePlugin
from ee_plugin import ee_plugin, config


@fixture(scope="session", autouse=True)
Expand All @@ -15,7 +15,12 @@ def setup_ee():


@fixture(scope="session", autouse=True)
def load_ee_plugin(qgis_app, setup_ee):
def ee_config():
return config.EarthEngineConfig()


@fixture(scope="session", autouse=True)
def load_ee_plugin(qgis_app, setup_ee, ee_config):
"""Load Earth Engine plugin and configure QSettings."""

# Set QSettings values required by the plugin
Expand All @@ -24,7 +29,7 @@ def load_ee_plugin(qgis_app, setup_ee):
QSettings().setValue("locale/userLocale", "en")

# Initialize and register the plugin
plugin = GoogleEarthEnginePlugin(qgis_app)
plugin = ee_plugin.GoogleEarthEnginePlugin(qgis_app, ee_config=ee_config)
plugins["ee_plugin"] = plugin
plugin.check_version()
yield qgis_app

0 comments on commit 409b238

Please sign in to comment.