From a123acefa40fe3ff79fbb6da05e3a71972ea7c69 Mon Sep 17 00:00:00 2001 From: pschmidtke Date: Fri, 6 Feb 2026 08:30:43 +0100 Subject: [PATCH 1/4] initial commit --- README.md | 81 +- threedecision_maestro.py | 62 + threedecision_maestro_dir/__init__.py | 19 + threedecision_maestro_dir/api_client.py | 1273 +++++++++ threedecision_maestro_dir/gui.py | 2519 +++++++++++++++++ threedecision_maestro_dir/images/.DS_Store | Bin 0 -> 6148 bytes .../images/3decision_RVB_Dark_transparent.png | Bin 0 -> 121269 bytes .../3decision_RVB_White_transparent.png | Bin 0 -> 136660 bytes .../images/LogoTitle2.0.png | Bin 0 -> 13871 bytes .../images/LogoTitle2.0.svg | 45 + .../images/cog-16-dark.png | Bin 0 -> 824 bytes .../images/cog-16-white.png | Bin 0 -> 281 bytes threedecision_maestro_dir/images/cog-16.png | Bin 0 -> 281 bytes 13 files changed, 3998 insertions(+), 1 deletion(-) create mode 100644 threedecision_maestro.py create mode 100644 threedecision_maestro_dir/__init__.py create mode 100644 threedecision_maestro_dir/api_client.py create mode 100644 threedecision_maestro_dir/gui.py create mode 100644 threedecision_maestro_dir/images/.DS_Store create mode 100644 threedecision_maestro_dir/images/3decision_RVB_Dark_transparent.png create mode 100644 threedecision_maestro_dir/images/3decision_RVB_White_transparent.png create mode 100644 threedecision_maestro_dir/images/LogoTitle2.0.png create mode 100644 threedecision_maestro_dir/images/LogoTitle2.0.svg create mode 100644 threedecision_maestro_dir/images/cog-16-dark.png create mode 100644 threedecision_maestro_dir/images/cog-16-white.png create mode 100644 threedecision_maestro_dir/images/cog-16.png diff --git a/README.md b/README.md index 5bedd94..f7956d9 100644 --- a/README.md +++ b/README.md @@ -1 +1,80 @@ -# 3decision_plugin_maestro \ No newline at end of file +# 3decision Maestro Plugin + +A Maestro plugin to search and load structures from the 3decision database. + +## Features + +- **Search**: Search for structures by keyword, PDB code, target name, or other criteria +- **Column Filters**: Filter results by any column (supports numeric filters like `<2.0` for resolution) +- **Projects**: Browse and load structures from your 3decision projects +- **Transformation Matrices**: Structures from projects are loaded with their alignment transformations +- **Associated Files**: View and open associated files (CCP4, MTZ, PDB, CIF, etc.) +- **Metadata**: Structure properties are preserved in the Maestro Project Table + +## Installation + +1. In Maestro, go to **Scripts > Install Custom Scripts...** + +2. Navigate to the `3decision_plugin_maestro` folder and select `threedecision_maestro.py` + +3. Click **Install** + +4. Restart Maestro + +5. The plugin appears in **Scripts > 3decision** + +**Note:** The installer automatically copies both `threedecision_maestro.py` and the `threedecision_maestro_dir/` folder containing the plugin files. + +## Configuration + +1. Open the plugin from **Scripts > 3decision** +2. Click **Settings** +3. Enter your 3decision API URL (e.g., `https://your-instance.discngine.cloud`) +4. Enter your API Key +5. Click **Test Connection** to verify +6. Click **Save** + +Configuration is saved to `~/.3decision_maestro_config`. + +## Usage + +### Search Tab +- Enter a search term and click **Submit** +- Use the filter fields below the search to narrow results +- Resolution filter supports: `<2.0`, `>1.5`, `1.5-3.0`, `<=2.0` +- Select structures and click **Load Selected in Maestro** + +### Projects Tab +- Click **Refresh Projects** to load your projects +- Click a project to see its structures +- Use filters to narrow down structures +- Click **View** to see associated files for a structure +- Select structures and click **Load Selected Structures** + +### Associated Files Tab +- Shows files for the selected structure (CCP4 maps, MTZ, etc.) +- **Open in Maestro**: Import structure/map files directly +- **Download & Open**: Open with system default application + +## Loaded Structure Properties + +Structures imported into Maestro include these properties: +- `s_user_3decision_structure_id` +- `s_user_3decision_external_code` +- `s_user_3decision_label` +- `s_user_3decision_source` + +## Requirements + +- Maestro 2020-1 or later +- Network access to your 3decision server + +## Troubleshooting + +**Plugin doesn't appear in Scripts menu:** +- Restart Maestro after installation + +**Connection failed:** +- Verify your API URL includes `https://` +- Check that your API key is valid +- Check network/proxy settings diff --git a/threedecision_maestro.py b/threedecision_maestro.py new file mode 100644 index 0000000..47228e0 --- /dev/null +++ b/threedecision_maestro.py @@ -0,0 +1,62 @@ +#Name: Discngine 3decision +#Command: pythonrun threedecision_maestro.run_plugin +""" +3decision Maestro Plugin + +Search and load structures from 3decision database into Maestro. +""" + +__version__ = "1.0" +_panel = None + + +def run_plugin(): + """Launch the 3decision plugin panel.""" + global _panel + import sys + import os + + # The plugin files are in threedecision_maestro_dir/ next to this script + script_dir = os.path.dirname(os.path.abspath(__file__)) + plugin_dir = os.path.join(script_dir, "threedecision_maestro_dir") + + # Fallback: check if files are in the same directory (development mode) + if not os.path.exists(os.path.join(plugin_dir, "gui.py")): + plugin_dir = script_dir + + if plugin_dir not in sys.path: + sys.path.insert(0, plugin_dir) + + try: + from gui import ThreeDecisionPanel + _panel = ThreeDecisionPanel.panel(run=True) + return _panel + except ImportError as e: + msg = ( + f"Failed to load 3decision plugin: {e}\n\n" + "The plugin folder must contain: gui.py, api_client.py\n\n" + f"Current location: {plugin_dir}" + ) + print(f"ERROR: {msg}") + try: + from schrodinger.Qt.QtWidgets import QMessageBox + QMessageBox.critical(None, "3decision Plugin Error", msg) + except: + pass + return None + + +def open_panel(): + """ + Alternative entry point for opening the panel. + + This can be used from the Maestro Python console: + import threedecision_maestro + threedecision_maestro.open_panel() + """ + return run_plugin() + + +if __name__ == "__main__": + # Allow running the script directly for testing + run_plugin() diff --git a/threedecision_maestro_dir/__init__.py b/threedecision_maestro_dir/__init__.py new file mode 100644 index 0000000..8d9ce90 --- /dev/null +++ b/threedecision_maestro_dir/__init__.py @@ -0,0 +1,19 @@ +""" +3decision Maestro Plugin v1.0 + +A plugin to search and load structures from 3decision database into Maestro. +""" +#Name: 3decision +#Command: pythonrun threedecision_maestro.run_plugin + +# Plugin version +__version__ = "1.0" + +def run_plugin(): + """ + Launch the 3decision plugin panel. + This is the entry point called by Maestro's pythonrun command. + """ + from .gui import ThreeDecisionPanel + panel = ThreeDecisionPanel.panel(run=True) + return panel diff --git a/threedecision_maestro_dir/api_client.py b/threedecision_maestro_dir/api_client.py new file mode 100644 index 0000000..657275c --- /dev/null +++ b/threedecision_maestro_dir/api_client.py @@ -0,0 +1,1273 @@ +""" +3decision API Client v1.0 + +Handles all communication with the 3decision API endpoints. + +Version: 1.0 +""" + +import json +import requests +from typing import Optional, Dict, List, Any +import os +import configparser +import urllib3 + +# Disable SSL warnings when ignoring certificate verification +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + +# Simple logging state - stored as module-level variable +_logging_enabled = False + +# Private structure naming attribute - stored as module-level variable +# Options: 'label', 'title', 'external_code', 'internal_id' +_private_structure_naming_attribute = 'label' + +def set_logging_enabled(enabled): + """Enable or disable logging output""" + global _logging_enabled + _logging_enabled = enabled + +def is_logging_enabled(): + """Check if logging is enabled""" + global _logging_enabled + return _logging_enabled + +def set_private_structure_naming_attribute(attribute): + """Set the attribute to use for naming private structures. + + Args: + attribute: One of 'label', 'title', 'external_code', or 'internal_id' + """ + global _private_structure_naming_attribute + if attribute in ('label', 'title', 'external_code', 'internal_id'): + _private_structure_naming_attribute = attribute + else: + _private_structure_naming_attribute = 'label' # Default + +def get_private_structure_naming_attribute(): + """Get the attribute used for naming private structures. + + Returns: + One of 'label', 'title', 'external_code', or 'internal_id' + """ + global _private_structure_naming_attribute + return _private_structure_naming_attribute + +def log_debug(message): + """Log a debug message if logging is enabled""" + if _logging_enabled: + print(f"DEBUG: {message}") + +def log_error(message): + """Log an error message if logging is enabled""" + if _logging_enabled: + print(f"ERROR: {message}") + + +class ThreeDecisionAPIClient: + """ + Client for interacting with 3decision API + + Note: SSL certificate verification is disabled for all requests + to support local development servers and internal APIs. + """ + + def __init__(self): + self.base_url = None + self.api_key = None + self.token = None + self.session = requests.Session() + # Disable SSL certificate verification for all requests + self.session.verify = False + self.config_file = os.path.expanduser("~/.3decision_maestro_config") + self.load_config() + + def load_config(self): + """Load configuration from file""" + if os.path.exists(self.config_file): + try: + config = configparser.ConfigParser() + config.read(self.config_file) + + if 'API' in config: + self.base_url = config['API'].get('base_url') + self.api_key = config['API'].get('api_key') + self.token = config['API'].get('token') + + # Load logging setting + log_enabled_str = config['API'].get('logging_enabled', 'false') + log_enabled = log_enabled_str.lower() == 'true' + set_logging_enabled(log_enabled) + + # Load private structure naming attribute setting + naming_attr = config['API'].get('private_structure_naming_attribute', 'label') + set_private_structure_naming_attribute(naming_attr) + + if self.token: + self.session.headers.update({ + 'Authorization': f'Bearer {self.token}', + 'X-API-Version': '1' + }) + + except Exception as e: + log_error(f"Error loading config: {e}") + + def save_config(self): + """Save configuration to file""" + try: + config = configparser.ConfigParser() + config['API'] = { + 'base_url': self.base_url or '', + 'api_key': self.api_key or '', + 'token': self.token or '', + 'logging_enabled': str(is_logging_enabled()).lower(), + 'private_structure_naming_attribute': get_private_structure_naming_attribute() + } + + with open(self.config_file, 'w') as f: + config.write(f) + + except Exception as e: + log_error(f"Error saving config: {e}") + + def _request_with_retry(self, method: str, url: str, headers: Dict = None, + json_data: Dict = None, params: Dict = None, + description: str = "request") -> Optional[requests.Response]: + """ + Make an HTTP request with automatic retry on authentication failure. + + If the request returns 401/403, attempts to re-login and retry once. + """ + # Ensure headers exist and have Authorization + if headers is None: + headers = {} + if 'Authorization' not in headers and self.token: + headers['Authorization'] = f'Bearer {self.token}' + + # Make initial request + log_debug(f"{description} URL: {url}") + log_debug(f"Request method: {method}") + + if method.upper() == 'GET': + response = self.session.get(url, headers=headers, params=params) + elif method.upper() == 'POST': + response = self.session.post(url, headers=headers, json=json_data, params=params) + else: + log_error(f"Unsupported HTTP method: {method}") + return None + + log_debug(f"Response status: {response.status_code}") + + # If authentication failed, try to re-login and retry + if response.status_code in [401, 403]: + log_debug(f"Authentication failed for {description}, attempting re-login...") + + # Clear the old token + self.token = None + if 'Authorization' in self.session.headers: + del self.session.headers['Authorization'] + + if self.login(): + log_debug(f"Re-login successful, retrying {description}") + # Update headers with new token + headers['Authorization'] = f'Bearer {self.token}' + + if method.upper() == 'GET': + response = self.session.get(url, headers=headers, params=params) + else: + response = self.session.post(url, headers=headers, json=json_data, params=params) + + log_debug(f"Retry response status: {response.status_code}") + else: + log_error(f"Re-login failed for {description}") + return None + + return response + + def save_logging_setting(self, enabled: bool): + """Save logging setting to config file""" + set_logging_enabled(enabled) + self.save_config() + + def save_naming_attribute_setting(self, attribute: str): + """Save private structure naming attribute setting to config file""" + set_private_structure_naming_attribute(attribute) + self.save_config() + + def configure(self, base_url: str, api_key: str): + """Configure API settings""" + # Ensure the URL includes the scheme + if not base_url.startswith(('http://', 'https://')): + base_url = 'http://' + base_url + + self.base_url = base_url.rstrip('/') + self.api_key = api_key + self.token = None + + # Update session headers + self.session.headers.update({ + 'Dng-Api-Key': self.api_key, + 'X-API-Version': '1', + 'User-Agent': 'Maestro-3decision-Plugin/1.0' + }) + + # Remove Authorization header if present + if 'Authorization' in self.session.headers: + del self.session.headers['Authorization'] + + def is_configured(self) -> bool: + """Check if API is configured""" + return bool(self.base_url and self.api_key) + + def is_authenticated(self) -> bool: + """Check if user is authenticated (configured and connection works)""" + return self.is_configured() and self.test_connection() + + def login(self) -> bool: + """Login and get authentication token""" + if not self.is_configured(): + return False + + try: + url = f"{self.base_url}/auth/api/login" + log_debug(f"Attempting login to: {url}") + + # Ensure we have the API key header for login + headers = { + 'Dng-Api-Key': self.api_key, + 'X-API-Version': '1', + 'User-Agent': 'Maestro-3decision-Plugin/1.0' + } + + response = self.session.get(url, headers=headers) + log_debug(f"Response status: {response.status_code}") + + if response.status_code == 200 or response.status_code == 201: + data = response.json() + self.token = data.get('access_token') + + if self.token: + # Update session with token + self.session.headers.update({ + 'Authorization': f'Bearer {self.token}', + 'X-API-Version': '1', + 'User-Agent': 'Maestro-3decision-Plugin/1.0' + }) + # Remove API key header as we now have token + if 'Dng-Api-Key' in self.session.headers: + del self.session.headers['Dng-Api-Key'] + + self.save_config() + log_debug("Login successful") + return True + + log_error(f"Login failed: {response.status_code} - {response.text}") + return False + + except Exception as e: + log_error(f"Login error: {e}") + return False + + def test_connection(self) -> bool: + """Test API connection""" + if not self.is_configured(): + return False + + # If we have a token, assume it's valid and don't test + if self.token and 'Authorization' in self.session.headers: + log_debug("Using existing token for connection") + return True + + # Try to login if no token + log_debug("No token found, attempting login") + return self.login() + + def submit_search(self, query: str) -> Optional[Dict[str, Any]]: + """Submit a search query and return job info""" + if not self.test_connection(): + return None + + try: + # Step 1: Submit search to get job ID + url = f"{self.base_url}/search/{query}" + + response = self._request_with_retry( + method='GET', + url=url, + description="Search submission" + ) + + if response is None: + return None + + if response.status_code in [200, 201]: + try: + data = response.json() + log_debug(f"Search response data: {data}") + + # Extract job ID from response + job_id = data.get('id') + if job_id: + log_debug(f"Extracted job ID: {job_id}") + + # Step 2: Poll the queue endpoint to get results + queue_url = f"{self.base_url}/queues/basicSearch/jobs/{job_id}" + + queue_response = self._request_with_retry( + method='GET', + url=queue_url, + description="Queue polling" + ) + + if queue_response is None: + return None + + if queue_response.status_code in [200, 201]: + queue_data = queue_response.json() + progress = queue_data.get("progress") + if progress is None: + progress = 0 + log_debug(f"queue data: {queue_data}") + if progress == 100: + log_debug("Job completed (progress = 100%), extracting results") + + # Extract structure IDs from returnvalue.STRUCTURE_ID + structure_ids = [] + if 'returnvalue' in queue_data and 'STRUCTURE_ID' in queue_data['returnvalue']: + structure_ids = queue_data['returnvalue']['STRUCTURE_ID'] + log_debug(f"Extracted structure IDs: {structure_ids}") + + # If we have structure IDs, fetch detailed info via GraphQL + if structure_ids: + structures_info = self.get_structures_info(structure_ids) + log_debug(f"Retrieved {len(structures_info)} structure details") + + # Return the complete job data with structure details + queue_data['structures_info'] = structures_info + return queue_data + else: + log_debug("No structure IDs found in queue response - job completed with no results") + queue_data['structures_info'] = [] + return queue_data + else: + log_debug(f"Job still in progress ({progress}%), need to poll again") + return { + 'id': job_id, + 'queue': 'basicSearch', + 'status': 'running', + 'progress': progress, + 'polling_needed': True + } + else: + log_error(f"Queue polling failed: {queue_response.status_code} - {queue_response.text}") + return None + else: + log_debug("No job ID found in search response") + return { + 'id': 'direct', + 'queue': 'direct', + 'status': 'completed', + 'result': data + } + + except json.JSONDecodeError: + log_error(f"Non-JSON response from search: {response.text}") + return None + + else: + log_error(f"Search submission failed: {response.status_code}") + log_error(f"Response text: {response.text}") + return None + + except Exception as e: + log_error(f"Search submission error: {e}") + import traceback + log_error(f"Full traceback: {traceback.format_exc()}") + return None + + def get_job_status(self, queue_name: str, job_id: int) -> Optional[Dict[str, Any]]: + """Get status of a job""" + if not self.test_connection(): + return None + + try: + url = f"{self.base_url}/queues/{queue_name}/jobs/{job_id}" + + response = self._request_with_retry( + method='GET', + url=url, + description="Job status check" + ) + + if response is None: + return None + + if response.status_code in [200, 201]: + data = response.json() + log_debug(f"Job status response data: {data}") + return data + else: + log_error(f"Job status check failed: {response.status_code}") + return None + + except Exception as e: + log_error(f"Job status error: {e}") + return None + + def get_structures_info(self, structure_ids: List[int]) -> List[Dict[str, Any]]: + """Get detailed information for structures using GraphQL""" + if not self.test_connection(): + return [] + + try: + # If we have more than 500 structures, split into batches + batch_size = 500 + all_structures = [] + + if len(structure_ids) > batch_size: + log_debug(f"Large dataset detected ({len(structure_ids)} structures). Splitting into batches of {batch_size}...") + + for i in range(0, len(structure_ids), batch_size): + batch_ids = structure_ids[i:i + batch_size] + batch_num = (i // batch_size) + 1 + total_batches = (len(structure_ids) + batch_size - 1) // batch_size + + log_debug(f"Processing batch {batch_num}/{total_batches} ({len(batch_ids)} structures)...") + + batch_structures = self._fetch_structures_batch(batch_ids) + if batch_structures: + all_structures.extend(batch_structures) + log_debug(f"Batch {batch_num} completed: {len(batch_structures)} structures retrieved") + + log_debug(f"All batches completed. Total structures retrieved: {len(all_structures)}") + return all_structures + else: + log_debug(f"Small dataset ({len(structure_ids)} structures). Using single request...") + return self._fetch_structures_batch(structure_ids) + + except Exception as e: + log_error(f"Structures info error: {e}") + return [] + + def _fetch_structures_batch(self, structure_ids: List[int]) -> List[Dict[str, Any]]: + """Fetch a batch of structures using GraphQL""" + try: + query = """ + query GetStructuresInfo($ids: [Int!]!) { + getStructuresInfo(ids: $ids) { + structure_id + general { + structure_id + external_code + title + method + resolution + created_date + imported_date + created_by + imported_by + source + } + } + } + """ + + variables = {"ids": structure_ids} + + url = f"{self.base_url}/graphql" + payload = { + "query": query, + "variables": variables + } + + log_debug(f"GraphQL batch request (batch size: {len(structure_ids)})") + + response = self._request_with_retry( + method='POST', + url=url, + json_data=payload, + description="GraphQL structures batch" + ) + + if response is None: + return [] + + if response.status_code in [200, 201]: + data = response.json() + + if 'data' in data and 'getStructuresInfo' in data['data']: + batch_results = data['data']['getStructuresInfo'] + + # Remove duplicates based on structure_id (NMR structures with multiple states) + seen_ids = set() + unique_structures = [] + for structure in batch_results: + structure_id = structure.get('structure_id') + if structure_id and structure_id not in seen_ids: + seen_ids.add(structure_id) + unique_structures.append(structure) + + return unique_structures + else: + log_error(f"Unexpected GraphQL response structure") + return [] + else: + log_error(f"GraphQL batch query failed: {response.status_code}") + return [] + + except Exception as e: + log_error(f"Batch fetch error: {e}") + return [] + + def get_structure_internal_id(self, structure_id: int) -> Optional[str]: + """ + Fetch the internal_id annotation for a structure. + """ + if not self.test_connection(): + return None + + try: + url = f"{self.base_url}/structures/info/annotation" + params = { + "structure_id": [int(structure_id)] + } + headers = { + 'X-API-VERSION': '1' + } + + log_debug(f"Fetching internal_id for structure_id: {structure_id}") + + response = self._request_with_retry( + method='GET', + url=url, + headers=headers, + params=params, + description=f"Structure annotations for structure_id {structure_id}" + ) + + if response is None: + return None + + if response.status_code in [200, 201]: + data = response.json() + + if isinstance(data, list) and len(data) > 0: + structure_data = data[0] + annotation_info = structure_data.get('ANNOTATION_INFO', {}) + structure_annots = annotation_info.get('StructureAnnot', []) + + for annot in structure_annots: + if annot.get('ANNOT_TYPE_LABEL', '').lower() == 'internal id': + internal_id = annot.get('ANNOT_VALUE') + if internal_id: + log_debug(f"Found internal_id for structure_id {structure_id}: {internal_id}") + return internal_id + + log_debug(f"No internal_id annotation found for structure_id {structure_id}") + return None + else: + log_error(f"Structure annotations request failed: {response.status_code}") + return None + + except Exception as e: + log_error(f"Get internal_id error: {e}") + return None + + def export_structure_pdb(self, structure_id: str) -> Optional[str]: + """Export structure in PDB format""" + if not self.test_connection(): + return None + + try: + url = f"{self.base_url}/exports/structure" + params = {"output_format": "structures-pdb-txt"} + + payload = { + "structures_id": [int(structure_id)] + } + + log_debug(f"Export request params: {params}") + + response = self._request_with_retry( + method='POST', + url=url, + json_data=payload, + params=params, + description="Structure export" + ) + + if response is None: + return None + + if response.status_code in [200, 201]: + domain_event_id = response.text.strip() + log_debug(f"Received domain event ID: {domain_event_id}") + + if domain_event_id: + domain_events_url = f"{self.base_url}/domain-events/{domain_event_id}" + log_debug(f"Polling domain events endpoint: {domain_events_url}") + + max_attempts = 30 + attempt = 0 + + while attempt < max_attempts: + log_debug(f"Domain events poll attempt {attempt + 1}/{max_attempts}") + + domain_response = self._request_with_retry( + method='GET', + url=domain_events_url, + description="Export domain events polling" + ) + + if domain_response is None: + return None + + if domain_response.status_code in [200, 201]: + try: + domain_data = domain_response.json() + state = domain_data.get('state') + log_debug(f"Export state: {state}") + + if state == 'success': + content = domain_data.get('content', {}) + errors = content.get('errors', {}) + not_exported = errors.get('not_exported', []) + + if not_exported: + log_error(f"Export completed with errors: {not_exported}") + return None + else: + log_debug("Export completed successfully") + + file_names = content.get('file_names', []) + if file_names and len(file_names) > 0: + first_file = file_names[0] + if isinstance(first_file, dict): + filename = first_file.get("file_name") + else: + log_error(f"Unexpected file_names structure") + return None + + if not filename: + log_error("No filename found in file_names entry") + return None + + filename_without_ext = filename + if '.' in filename: + filename_without_ext = filename.rsplit('.', 1)[0] + + download_url = f"{self.base_url}/exports/structure/{domain_event_id}?filename={filename_without_ext}&download=true" + + download_response = self._request_with_retry( + method='GET', + url=download_url, + description="PDB file download" + ) + + if download_response is None: + return None + + if download_response.status_code in [200, 201]: + pdb_content = download_response.text + log_debug(f"Download successful, PDB content length: {len(pdb_content)} characters") + return pdb_content + else: + log_error(f"Download failed: {download_response.status_code}") + return None + else: + log_error("No filename found in export response") + return None + + elif state == 'failed': + log_error("Export failed") + return None + else: + log_debug(f"Export still processing (state: {state}), waiting...") + import time + time.sleep(2) + attempt += 1 + continue + + except json.JSONDecodeError as e: + log_error(f"Failed to parse domain events response: {e}") + return None + else: + log_error(f"Domain events request failed: {domain_response.status_code}") + return None + + log_error("Export polling timed out") + return None + else: + log_error("No domain event ID received") + return None + else: + log_error(f"Export request failed: {response.status_code}") + return None + + except Exception as e: + log_error(f"Structure export error: {e}") + import traceback + log_error(f"Full traceback: {traceback.format_exc()}") + return None + + def get_projects(self) -> List[Dict[str, Any]]: + """Get list of available projects""" + if not self.is_configured(): + raise Exception("API not configured") + + if not self.test_connection(): + raise Exception("Not authenticated") + + try: + url = f"{self.base_url}/projects" + headers = { + "Accept": "application/json" + } + + response = self._request_with_retry( + method='GET', + url=url, + headers=headers, + description="Projects request" + ) + + if response is None: + raise Exception("Authentication failed - please check your API key in Settings") + + if response.status_code == 200: + projects_data = response.json() + log_debug(f"Projects response: {projects_data}") + + if isinstance(projects_data, list): + return projects_data + elif isinstance(projects_data, dict): + if 'projects' in projects_data: + return projects_data['projects'] + elif 'results' in projects_data: + return projects_data['results'] + else: + log_error(f"Unexpected projects response format: {projects_data}") + return [] + else: + log_error(f"Unexpected projects response format: {projects_data}") + return [] + + else: + log_error(f"Projects request failed: {response.status_code}") + raise Exception(f"Projects request failed: {response.status_code}") + + except Exception as e: + log_error(f"Projects request error: {e}") + raise + + def get_project_structures(self, project_id: str) -> List[Dict[str, Any]]: + """Get structures from a specific project with transformation matrices""" + if not self.is_configured(): + raise Exception("API not configured") + + if not self.test_connection(): + raise Exception("Not authenticated") + + try: + url = f"{self.base_url}/projects/{project_id}/structures/matrix" + headers = { + "Accept": "application/json" + } + + response = self._request_with_retry( + method='GET', + url=url, + headers=headers, + description="Project structures request" + ) + + if response is None: + raise Exception("Authentication failed - please check your API key in Settings") + + if response.status_code == 200: + structures_data = response.json() + log_debug(f"Project structures response: {structures_data}") + + if isinstance(structures_data, list): + return structures_data + elif isinstance(structures_data, dict) and 'results' in structures_data: + return structures_data['results'] + else: + log_error(f"Unexpected project structures response format: {structures_data}") + return [] + + else: + log_error(f"Project structures request failed: {response.status_code}") + raise Exception(f"Project structures request failed: {response.status_code}") + + except Exception as e: + log_error(f"Project structures request error: {e}") + raise + + def export_structures_with_transforms(self, structures_with_transforms: List[Dict]) -> Optional[Dict[str, str]]: + """Export multiple structures with transformation matrices using batch export""" + if not self.test_connection(): + return None + + try: + if len(structures_with_transforms) > 1: + log_debug(f"Exporting {len(structures_with_transforms)} structures with transformations as ZIP") + + matrix_payload = [ + { + "external_code": item["external_code"], + "structure_id": item["structure_id"], + "transform": item["transform"] + } + for item in structures_with_transforms + ] + + structure_ids = [item["structure_id"] for item in structures_with_transforms] + + zip_content = self.download_structures_zip(structure_ids, matrix_payload) + + if not zip_content: + log_error("Failed to download structures as ZIP") + return None + + import zipfile + import io + + pdb_files_dict = {} + + try: + with zipfile.ZipFile(io.BytesIO(zip_content), 'r') as zip_ref: + file_list = zip_ref.namelist() + log_debug(f"ZIP contains {len(file_list)} files: {file_list}") + + pdb_files = [f for f in file_list if f.endswith('.pdb')] + log_debug(f"Found {len(pdb_files)} PDB files in ZIP") + + for pdb_filename in pdb_files: + log_debug(f"Extracting {pdb_filename} from ZIP") + pdb_content = zip_ref.read(pdb_filename).decode('utf-8') + base_filename = pdb_filename.split('/')[-1] + pdb_files_dict[base_filename] = pdb_content + + if pdb_files_dict: + log_debug(f"Successfully extracted {len(pdb_files_dict)} structures from ZIP") + return pdb_files_dict + else: + log_error("No PDB files were extracted from ZIP") + return None + + except zipfile.BadZipFile as e: + log_error(f"Invalid ZIP file: {e}") + return None + except Exception as e: + log_error(f"Error extracting PDB files from ZIP: {e}") + return None + + else: + # Single structure + url = f"{self.base_url}/exports/structure" + + matrix_payload = { + "external_codes": [item["external_code"] for item in structures_with_transforms], + "matrix": [ + { + "external_code": item["external_code"], + "structure_id": item["structure_id"], + "transform": item["transform"] + } + for item in structures_with_transforms + ] + } + + params = { + "output_format": "structures-pdb-txt" + } + + log_debug(f"Batch export payload: {json.dumps(matrix_payload, indent=2)}") + + response = self._request_with_retry( + method='POST', + url=url, + json_data=matrix_payload, + params=params, + description="Batch export (single structure)" + ) + + if response is None: + return None + + if response.status_code in [200, 201]: + domain_event_id = response.text.strip() + log_debug(f"Received domain event ID: {domain_event_id}") + + if domain_event_id: + domain_events_url = f"{self.base_url}/domain-events/{domain_event_id}" + + max_attempts = 30 + attempt = 0 + + while attempt < max_attempts: + domain_response = self._request_with_retry( + method='GET', + url=domain_events_url, + description="Batch export domain events polling" + ) + + if domain_response is None: + return None + + if domain_response.status_code in [200, 201]: + try: + domain_data = domain_response.json() + state = domain_data.get('state') + + if state == 'success': + content = domain_data.get('content', {}) + errors = content.get('errors', {}) + not_exported = errors.get('not_exported', []) + + if not_exported: + log_error(f"Batch export completed with errors: {not_exported}") + return None + + log_debug("Batch export completed successfully") + + file_names = content.get('file_names', []) + if file_names and len(file_names) > 0: + first_file = file_names[0] + if isinstance(first_file, dict): + filename = first_file.get("file_name") + else: + log_error(f"Unexpected file_names structure: {first_file}") + return None + + if not filename: + log_error("No filename found in file_names entry") + return None + + filename_without_ext = filename + if '.' in filename: + filename_without_ext = filename.rsplit('.', 1)[0] + + download_url = f"{self.base_url}/exports/structure/{domain_event_id}?filename={filename_without_ext}&download=true" + + download_response = self._request_with_retry( + method='GET', + url=download_url, + description="Batch export PDB download" + ) + + if download_response is None: + return None + + if download_response.status_code == 200: + pdb_content = download_response.text + log_debug(f"Successfully downloaded PDB content: {len(pdb_content)} bytes") + external_code = structures_with_transforms[0]["external_code"] + return {f"{external_code}.pdb": pdb_content} + else: + log_error(f"Failed to download PDB: {download_response.status_code}") + return None + else: + log_error("No file_names found in export response") + return None + + elif state == 'failed': + log_error("Batch export failed") + return None + + except Exception as e: + log_error(f"Error parsing domain event response: {e}") + + attempt += 1 + if attempt < max_attempts: + import time + time.sleep(2) + + log_error("Batch export polling timed out") + return None + else: + log_error("No domain event ID received") + return None + else: + log_error(f"Batch export request failed: {response.status_code}") + return None + + except Exception as e: + log_error(f"Batch export error: {e}") + import traceback + log_error(f"Full traceback: {traceback.format_exc()}") + return None + + def download_structures_zip(self, structure_ids: List[int], matrices: List[Dict] = None) -> Optional[bytes]: + """Download structures as a ZIP file""" + if not self.is_authenticated(): + raise Exception("Not authenticated") + + try: + url = f"{self.base_url}/exports/structure" + headers = { + "Authorization": f"Bearer {self.token}", + "Content-Type": "application/json" + } + + payload = { + "structures_id": structure_ids + } + + if matrices: + payload["matrix"] = matrices + + params = { + "output_format": "structures-pdb-zip" + } + + log_debug(f"Structure IDs: {structure_ids}") + + response = self._request_with_retry( + method='POST', + url=url, + headers=headers, + json_data=payload, + params=params, + description="ZIP export request" + ) + + if response is None: + return None + + if response.status_code in [200, 201]: + domain_event_id = response.text.strip() + log_debug(f"Received domain event ID: {domain_event_id}") + + if domain_event_id: + domain_events_url = f"{self.base_url}/domain-events/{domain_event_id}" + + max_attempts = 30 + attempt = 0 + + while attempt < max_attempts: + domain_response = self._request_with_retry( + method='GET', + url=domain_events_url, + description="ZIP export domain events polling" + ) + + if domain_response is None: + return None + + if domain_response.status_code in [200, 201]: + try: + domain_data = domain_response.json() + state = domain_data.get('state') + + if state == 'success': + content = domain_data.get('content', {}) + errors = content.get('errors', {}) + not_exported = errors.get('not_exported', []) + + if not_exported: + log_error(f"Export completed with errors: {not_exported}") + return None + else: + log_debug("Export completed successfully") + + file_names = content.get('file_names', []) + + if file_names and len(file_names) > 0: + first_file = file_names[0] + + if isinstance(first_file, dict): + filename = first_file.get("file_name") + else: + log_error(f"Unexpected file_names structure") + return None + + if not filename: + log_error("No filename found in file_names entry") + return None + + filename_without_ext = filename + if '.' in filename: + filename_without_ext = filename.rsplit('.', 1)[0] + + download_url = f"{self.base_url}/exports/structure/{domain_event_id}?filename={filename_without_ext}&download=true" + + download_response = self._request_with_retry( + method='GET', + url=download_url, + description="ZIP file download" + ) + + if download_response is None: + return None + + if download_response.status_code in [200, 201]: + zip_content = download_response.content + log_debug(f"Download successful, ZIP content length: {len(zip_content)} bytes") + + if len(zip_content) > 4 and zip_content[:2] == b'PK': + log_debug("Confirmed ZIP file format (starts with PK signature)") + return zip_content + else: + log_debug(f"Warning: Downloaded content may not be a ZIP file") + return zip_content + else: + log_error(f"Download failed: {download_response.status_code}") + return None + else: + log_error("No filename found in export response") + return None + + elif state == 'failed': + log_error("Export failed") + return None + else: + log_debug(f"Export still processing (state: {state}), waiting...") + import time + time.sleep(2) + attempt += 1 + continue + + except json.JSONDecodeError as e: + log_error(f"Failed to parse domain events response: {e}") + return None + else: + log_error(f"Domain events request failed: {domain_response.status_code}") + return None + + log_error("Export polling timed out") + return None + else: + log_error("No domain event ID received") + return None + else: + log_error(f"Export request failed: {response.status_code}") + return None + + except Exception as e: + log_error(f"ZIP download error: {e}") + return None + + def get_associated_files(self, external_code: str) -> List[Dict[str, Any]]: + """Get associated files for a structure by external code""" + try: + if not self.test_connection(): + log_error("Not authenticated for associated files request") + return [] + + url = f"{self.base_url}/structures/{external_code}/associated-files" + headers = { + "Accept": "application/json" + } + + response = self._request_with_retry( + method='GET', + url=url, + headers=headers, + description="Associated files request" + ) + + if response is None: + return [] + + if response.status_code == 200: + files_data = response.json() + log_debug(f"Associated files response: {json.dumps(files_data, indent=2)}") + + if isinstance(files_data, list): + return files_data + elif isinstance(files_data, dict): + if 'files' in files_data: + return files_data['files'] + elif 'results' in files_data: + return files_data['results'] + else: + log_error(f"Unexpected associated files response format: {files_data}") + return [] + else: + log_error(f"Unexpected associated files response format: {files_data}") + return [] + + elif response.status_code == 404: + log_debug("No associated files found (404)") + return [] + else: + log_error(f"Associated files request failed: {response.status_code} - {response.text}") + return [] + + except Exception as e: + log_error(f"Associated files request error: {e}") + return [] + + def download_file_by_id(self, file_id: str) -> Optional[bytes]: + """Download file content by ID""" + try: + if not self.test_connection(): + log_error("Not authenticated for file download") + return None + + url = f"{self.base_url}/structures/file/{file_id}/download" + + response = self._request_with_retry( + method='GET', + url=url, + description="File download" + ) + + if response is None: + return None + + if response.status_code == 200: + log_debug(f"File downloaded successfully: {len(response.content)} bytes") + return response.content + else: + log_error(f"File download failed: {response.status_code} - {response.text}") + return None + + except Exception as e: + log_error(f"File download error: {e}") + return None + + def download_file(self, file_info: Dict[str, Any]) -> Optional[bytes]: + """Download file using file info dictionary""" + file_id = file_info.get('id') or file_info.get('file_id') or file_info.get('FILE_ID') + + if not file_id: + download_url = file_info.get('download_url') or file_info.get('url') + if download_url: + return self._download_from_url(download_url) + else: + log_error("No file ID or download URL found in file info") + return None + + return self.download_file_by_id(str(file_id)) + + def _download_from_url(self, url: str) -> Optional[bytes]: + """Download file from direct URL""" + try: + if not self.test_connection(): + log_error("Not authenticated for URL download") + return None + + response = self._request_with_retry( + method='GET', + url=url, + description="Direct URL download" + ) + + if response is None: + return None + + if response.status_code == 200: + content = response.content + log_debug(f"Direct URL download successful: {len(content)} bytes") + return content + else: + log_error(f"Direct URL download failed: {response.status_code}") + return None + + except Exception as e: + log_error(f"Direct URL download error: {e}") + return None diff --git a/threedecision_maestro_dir/gui.py b/threedecision_maestro_dir/gui.py new file mode 100644 index 0000000..9ed7522 --- /dev/null +++ b/threedecision_maestro_dir/gui.py @@ -0,0 +1,2519 @@ +""" +3decision Plugin GUI v1.0 + +Main panel and interface components for the 3decision Maestro plugin. + +Version: 1.0 +""" + +import os +import sys +import json +import tempfile +from typing import Optional, Dict, List, Any + +# Import Schrodinger modules +try: + from schrodinger.ui.qt.appframework2 import af2 + from schrodinger.ui.qt import swidgets + from schrodinger.Qt import QtWidgets, QtCore, QtGui + from schrodinger.Qt.QtWidgets import ( + QDialog, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, + QTableWidget, QTableWidgetItem, QLabel, QMessageBox, QHeaderView, + QProgressBar, QCheckBox, QAbstractItemView, QTabWidget, QWidget, + QFormLayout, QComboBox, QFrame, QDialogButtonBox + ) + from schrodinger.Qt.QtCore import Qt, QThread, pyqtSignal + from schrodinger.Qt.QtGui import QPixmap, QIcon + from schrodinger import maestro, structure + MAESTRO_AVAILABLE = True + SWIDGETS_AVAILABLE = True +except ImportError: + # Fallback for testing outside Maestro + from PyQt5 import QtWidgets, QtCore, QtGui + from PyQt5.QtWidgets import ( + QDialog, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, + QTableWidget, QTableWidgetItem, QLabel, QMessageBox, QHeaderView, + QProgressBar, QCheckBox, QAbstractItemView, QTabWidget, QWidget, + QFormLayout, QComboBox, QFrame, QDialogButtonBox + ) + from PyQt5.QtCore import Qt, QThread, pyqtSignal + from PyQt5.QtGui import QPixmap, QIcon + MAESTRO_AVAILABLE = False + SWIDGETS_AVAILABLE = False + af2 = None + swidgets = None + +# Import api_client - try relative import first, then absolute +try: + from .api_client import ThreeDecisionAPIClient, is_logging_enabled, get_private_structure_naming_attribute +except ImportError: + from api_client import ThreeDecisionAPIClient, is_logging_enabled, get_private_structure_naming_attribute + + +# Dark theme stylesheet matching Maestro's dark UI +DARK_STYLESHEET = """ + * { + background-color: #444444; + color: #ECECEC; + } + QWidget, QFrame, QDialog { + background-color: #444444; + color: #ECECEC; + } + QLabel { + background-color: transparent; + color: #ECECEC; + } + QStatusBar { + background-color: #3a3a3a; + color: #ECECEC; + border-top: 1px solid #555555; + } + QLineEdit { + background-color: #3a3a3a; + color: #ECECEC; + border: 1px solid #555555; + border-radius: 3px; + padding: 4px 6px; + selection-background-color: #60B0DC; + } + QLineEdit:focus { + border: 1px solid #60B0DC; + } + QLineEdit:disabled { + background-color: #383838; + color: #888888; + } + QLineEdit::placeholder { + color: #888888; + } + QComboBox { + background-color: #3a3a3a; + color: #ECECEC; + border: 1px solid #555555; + border-radius: 3px; + padding: 4px 6px; + } + QComboBox:hover, QComboBox:focus { + border: 1px solid #60B0DC; + } + QComboBox::drop-down { + border: none; + width: 20px; + } + QComboBox::down-arrow { + image: none; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 6px solid #ECECEC; + margin-right: 6px; + } + QComboBox QAbstractItemView { + background-color: #3a3a3a; + color: #ECECEC; + selection-background-color: #60B0DC; + border: 1px solid #555555; + } + QPushButton { + background-color: #505050; + color: #ECECEC; + border: 1px solid #606060; + border-radius: 3px; + padding: 5px 12px; + min-height: 20px; + } + QPushButton:hover { + background-color: #585858; + border: 1px solid #60B0DC; + } + QPushButton:pressed { + background-color: #404040; + } + QPushButton:disabled { + background-color: #3a3a3a; + color: #666666; + border: 1px solid #4a4a4a; + } + QCheckBox { + background-color: transparent; + color: #ECECEC; + spacing: 6px; + } + QCheckBox::indicator { + width: 16px; + height: 16px; + border: 1px solid #555555; + border-radius: 2px; + background-color: #3a3a3a; + } + QCheckBox::indicator:checked { + background-color: #60B0DC; + border-color: #60B0DC; + } + QCheckBox::indicator:hover { + border-color: #60B0DC; + } + QTabWidget::pane { + border: 1px solid #555555; + background-color: #444444; + border-radius: 3px; + } + QTabBar::tab { + background-color: #3a3a3a; + color: #ABABAB; + border: 1px solid #555555; + border-bottom: none; + padding: 6px 16px; + margin-right: 2px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + } + QTabBar::tab:selected { + background-color: #444444; + color: #ECECEC; + border-bottom: 1px solid #444444; + } + QTabBar::tab:hover:!selected { + background-color: #404040; + color: #ECECEC; + } + QTableWidget { + background-color: #3a3a3a; + color: #ECECEC; + gridline-color: #555555; + border: 1px solid #555555; + selection-background-color: #60B0DC; + selection-color: #FFFFFF; + } + QTableWidget::item { + padding: 4px; + } + QTableWidget::item:selected { + background-color: #60B0DC; + color: #FFFFFF; + } + QHeaderView::section { + background-color: #4a4a4a; + color: #ECECEC; + padding: 5px; + border: none; + border-right: 1px solid #555555; + border-bottom: 1px solid #555555; + } + QHeaderView::section:hover { + background-color: #555555; + } + QProgressBar { + background-color: #3a3a3a; + border: 1px solid #555555; + border-radius: 3px; + text-align: center; + color: #ECECEC; + } + QProgressBar::chunk { + background-color: #60B0DC; + border-radius: 2px; + } + QScrollBar:vertical { + background-color: #3a3a3a; + width: 12px; + border: none; + } + QScrollBar::handle:vertical { + background-color: #606060; + min-height: 20px; + border-radius: 3px; + margin: 2px; + } + QScrollBar::handle:vertical:hover { + background-color: #707070; + } + QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { + height: 0px; + } + QScrollBar:horizontal { + background-color: #3a3a3a; + height: 12px; + border: none; + } + QScrollBar::handle:horizontal { + background-color: #606060; + min-width: 20px; + border-radius: 3px; + margin: 2px; + } + QScrollBar::handle:horizontal:hover { + background-color: #707070; + } + QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { + width: 0px; + } + QDialogButtonBox QPushButton { + min-width: 70px; + } + QFrame[frameShape="4"], QFrame[frameShape="5"] { + background-color: #555555; + } +""" + + +class NumericTableWidgetItem(QTableWidgetItem): + """Custom QTableWidgetItem that sorts numerically instead of alphabetically""" + def __init__(self, text, numeric_value): + super().__init__(text) + self.numeric_value = numeric_value + + def __lt__(self, other): + """Override less-than comparison for sorting""" + if isinstance(other, NumericTableWidgetItem): + return self.numeric_value < other.numeric_value + return super().__lt__(other) + + +def log_debug(message): + """Log a debug message if logging is enabled""" + if is_logging_enabled(): + print(f"DEBUG: {message}") + +def log_error(message): + """Log an error message if logging is enabled""" + if is_logging_enabled(): + print(f"ERROR: {message}") + +def log_info(message): + """Log an info message if logging is enabled""" + if is_logging_enabled(): + print(f"INFO: {message}") + + +class SearchThread(QThread): + """Thread for handling search operations""" + results_ready = pyqtSignal(list) + error_occurred = pyqtSignal(str) + status_update = pyqtSignal(str) + + def __init__(self, api_client: ThreeDecisionAPIClient, search_query: str): + super().__init__() + self.api_client = api_client + self.search_query = search_query + + def run(self): + try: + self.status_update.emit("Submitting search...") + job_response = self.api_client.submit_search(self.search_query) + + if not job_response: + self.error_occurred.emit("Failed to submit search") + return + + if 'structures_info' in job_response: + structures = job_response['structures_info'] + self.status_update.emit(f"Search completed. Found {len(structures)} structures.") + self.results_ready.emit(structures) + return + + if job_response.get('polling_needed'): + job_id = job_response.get('id') + queue_name = job_response.get('queue', 'basicSearch') + + if not job_id: + self.error_occurred.emit("No job ID received from search") + return + + self.status_update.emit(f"Job submitted (ID: {job_id}). Waiting for completion...") + + max_attempts = 60 + attempt = 0 + + while attempt < max_attempts: + try: + result = self.api_client.get_job_status(queue_name, job_id) + + if result: + progress = result.get('progress', 0) + self.status_update.emit(f"Search progress: {progress}%") + + structure_ids = [] + if 'returnvalue' in result and 'STRUCTURE_ID' in result['returnvalue']: + structure_ids = result['returnvalue']['STRUCTURE_ID'] + + if progress == 100 or (isinstance(structure_ids, list) and len(structure_ids) == 0 and progress > 0): + if structure_ids: + self.status_update.emit("Fetching structure details...") + structures = self.api_client.get_structures_info(structure_ids) + self.results_ready.emit(structures) + else: + self.status_update.emit("Search completed with no results.") + self.results_ready.emit([]) + return + elif result.get('status') == 'failed': + self.error_occurred.emit("Search job failed") + return + + self.msleep(2000) + attempt += 1 + + except Exception as e: + log_error(f"Polling error: {e}") + attempt += 1 + self.msleep(2000) + + self.error_occurred.emit("Search timed out waiting for completion") + return + + job_id = job_response.get('id') + queue_name = job_response.get('queue', 'basicSearch') + + if not job_id: + self.error_occurred.emit("No job ID received from search") + return + + if job_response.get('status') == 'completed': + structure_ids = job_response.get('result', []) + if structure_ids: + self.status_update.emit("Fetching structure details...") + structures = self.api_client.get_structures_info(structure_ids) + self.results_ready.emit(structures) + else: + self.results_ready.emit([]) + return + + self.status_update.emit(f"Polling for results (Job ID: {job_id})...") + + if job_id == 'direct': + return + + max_attempts = 30 + attempt = 0 + + while attempt < max_attempts: + try: + result = self.api_client.get_job_status(queue_name, job_id) + + if result and result.get('status') == 'completed': + structure_ids = result.get('result', []) + if structure_ids: + self.status_update.emit("Fetching structure details...") + structures = self.api_client.get_structures_info(structure_ids) + self.results_ready.emit(structures) + else: + self.results_ready.emit([]) + return + elif result and result.get('status') == 'failed': + self.error_occurred.emit("Search job failed") + return + + except Exception as e: + log_error(f"Polling attempt {attempt + 1} failed: {e}") + + attempt += 1 + self.msleep(2000) + + self.error_occurred.emit("Search timed out") + + except Exception as e: + self.error_occurred.emit(f"Search error: {str(e)}") + + +def get_object_name(external_code: str, label: str = None, source: str = None, title: str = None, internal_id: str = None) -> str: + """ + Determine the best object name for a structure. + + Naming logic: + - For public domain structures (RCSB PDB, PDB, AlphaFold, etc.): use external_code + - For private/internal structures: use the attribute configured in settings + """ + public_sources = [ + 'rcsb', 'pdb', 'alphafold', 'uniprot', 'chembl', 'drugbank', + 'pubchem', 'zinc', 'emdb', 'wwpdb' + ] + + is_public = False + if source: + source_lower = source.lower() + is_public = any(ps in source_lower for ps in public_sources) + + if is_public: + name = external_code + else: + naming_attr = get_private_structure_naming_attribute() + + if naming_attr == 'label' and label and label.strip() and label.lower() not in ['n/a', 'null', 'none', '']: + name = label.strip() + elif naming_attr == 'title' and title and title.strip() and title.lower() not in ['n/a', 'null', 'none', '']: + name = title.strip() + elif naming_attr == 'internal_id' and internal_id and internal_id.strip() and internal_id.lower() not in ['n/a', 'null', 'none', '']: + name = internal_id.strip() + else: + name = external_code + + if name.lower().startswith('3dec_'): + name = name[5:] + + # Sanitize name (alphanumeric with underscores) + sanitized = ''.join(c if c.isalnum() or c == '_' else '_' for c in name) + + if not sanitized: + sanitized = external_code if external_code else 'structure' + + return sanitized + + +class LoadStructureThread(QThread): + """Thread for loading structures into Maestro""" + structure_loaded = pyqtSignal(str, str) # structure_id, object_name + error_occurred = pyqtSignal(str) + status_update = pyqtSignal(str) + all_structures_loaded = pyqtSignal() + pdb_content_ready = pyqtSignal(str, str, dict) # pdb_content, object_name, metadata + + def __init__(self, api_client: ThreeDecisionAPIClient, structure_data: List[Dict[str, str]]): + super().__init__() + self.api_client = api_client + self.structure_data = structure_data + + def run(self): + try: + has_matrices = any(s.get('matrix') is not None for s in self.structure_data) + + if has_matrices: + log_debug("Loading structures with transformation matrices using batch export") + self.status_update.emit("Loading structures with transformations...") + + structures_with_transforms = [] + for structure_info in self.structure_data: + structure_id = int(structure_info['structure_id']) + external_code = structure_info['external_code'] + matrix = structure_info.get('matrix') + + identity_matrix = [ + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ] + + if matrix: + if isinstance(matrix, list) and len(matrix) == 4: + flat_matrix = [] + for row in matrix: + if isinstance(row, list) and len(row) == 4: + flat_matrix.extend(row) + else: + log_error(f"Invalid matrix row format for structure {structure_id}") + flat_matrix = None + break + + if flat_matrix and len(flat_matrix) == 16: + structures_with_transforms.append({ + "structure_id": structure_id, + "external_code": external_code, + "transform": flat_matrix + }) + else: + structures_with_transforms.append({ + "structure_id": structure_id, + "external_code": external_code, + "transform": identity_matrix + }) + else: + structures_with_transforms.append({ + "structure_id": structure_id, + "external_code": external_code, + "transform": identity_matrix + }) + else: + structures_with_transforms.append({ + "structure_id": structure_id, + "external_code": external_code, + "transform": identity_matrix + }) + + pdb_files_dict = self.api_client.export_structures_with_transforms(structures_with_transforms) + + if pdb_files_dict: + loaded_count = 0 + + for structure_info in self.structure_data: + structure_id = structure_info['structure_id'] + external_code = structure_info['external_code'] + label = structure_info.get('label') + source = structure_info.get('source') + title = structure_info.get('title') + + internal_id = None + if get_private_structure_naming_attribute() == 'internal_id': + internal_id = self.api_client.get_structure_internal_id(structure_id) + + object_name = get_object_name(external_code, label, source, title, internal_id) + + pdb_content = None + for filename, content in pdb_files_dict.items(): + filename_lower = filename.lower() + code_lower = external_code.lower() + if code_lower in filename_lower or filename_lower.replace('3dec_', '').startswith(code_lower): + pdb_content = content + log_debug(f"Matched {external_code} to file {filename}") + break + + if not pdb_content and len(pdb_files_dict) == 1: + pdb_content = list(pdb_files_dict.values())[0] + log_debug(f"Using single PDB file for {external_code}") + + if pdb_content: + metadata = { + '3decision_structure_id': structure_id, + '3decision_external_code': external_code, + '3decision_label': label or '', + '3decision_source': source or 'unknown' + } + self.pdb_content_ready.emit(pdb_content, object_name, metadata) + self.structure_loaded.emit(structure_id, object_name) + loaded_count += 1 + else: + log_error(f"Could not find PDB content for {external_code}") + self.error_occurred.emit(f"Could not find PDB content for {external_code}") + + if loaded_count == 0: + self.error_occurred.emit("Failed to load any structures") + else: + self.error_occurred.emit("Failed to load structures with transformations") + else: + log_debug("Loading structures individually without transformations") + for structure_info in self.structure_data: + structure_id = structure_info['structure_id'] + external_code = structure_info['external_code'] + label = structure_info.get('label') + source = structure_info.get('source') + title = structure_info.get('title') + + self.status_update.emit(f"Loading structure {external_code} ({structure_id})...") + + pdb_content = self.api_client.export_structure_pdb(structure_id) + + if pdb_content: + internal_id = None + if get_private_structure_naming_attribute() == 'internal_id': + internal_id = self.api_client.get_structure_internal_id(structure_id) + + object_name = get_object_name(external_code, label, source, title, internal_id) + + metadata = { + '3decision_structure_id': structure_id, + '3decision_external_code': external_code, + '3decision_label': label or '', + '3decision_source': source or 'unknown' + } + self.pdb_content_ready.emit(pdb_content, object_name, metadata) + log_info(f"3decision Plugin: Loaded {object_name} with structure_id: {structure_id}") + self.structure_loaded.emit(structure_id, object_name) + else: + self.error_occurred.emit(f"Failed to load structure {external_code} ({structure_id})") + + self.all_structures_loaded.emit() + + except Exception as e: + self.error_occurred.emit(f"Loading error: {str(e)}") + + +class SettingsDialog(QDialog): + """Settings dialog for configuring 3decision API""" + + def __init__(self, api_client: ThreeDecisionAPIClient, parent=None): + super().__init__(parent) + self.api_client = api_client + self.test_thread = None + self.init_ui() + self.load_current_settings() + + def init_ui(self): + """Initialize the user interface""" + self.setWindowTitle("3decision API Settings") + self.setModal(True) + self.setMinimumSize(500, 250) + + # Apply shared dark theme stylesheet + self.setStyleSheet(DARK_STYLESHEET) + + main_layout = QVBoxLayout() + main_layout.setContentsMargins(20, 20, 20, 20) + + # Form layout + form_layout = QFormLayout() + form_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow) + + # URL input + self.url_input = QLineEdit() + self.url_input.setPlaceholderText("https://your-3decision-api-url.discngine.cloud") + self.url_input.setMinimumWidth(300) + form_layout.addRow("3decision URL:", self.url_input) + + # API Key input + self.api_key_input = QLineEdit() + self.api_key_input.setEchoMode(QLineEdit.Password) + self.api_key_input.setPlaceholderText("Your API key") + self.api_key_input.setMinimumWidth(300) + form_layout.addRow("API Key:", self.api_key_input) + + # Log events checkbox + self.log_events_checkbox = QCheckBox("Log events") + self.log_events_checkbox.setToolTip("Enable logging of plugin events to console") + form_layout.addRow("", self.log_events_checkbox) + + # Private structure naming attribute dropdown + self.naming_attribute_combo = QComboBox() + self.naming_attribute_combo.addItems(["label", "title", "external_code", "internal_id"]) + self.naming_attribute_combo.setToolTip( + "Choose which attribute to use for naming private structures.\n" + "Public structures (PDB, AlphaFold, etc.) always use external_code." + ) + form_layout.addRow("Private structure name:", self.naming_attribute_combo) + + main_layout.addLayout(form_layout) + + # Test connection button + test_layout = QHBoxLayout() + test_layout.addStretch() + + self.test_button = QPushButton("Test Connection") + self.test_button.clicked.connect(self.test_connection) + test_layout.addWidget(self.test_button) + + main_layout.addLayout(test_layout) + + # Status label + self.status_label = QLabel("") + self.status_label.setWordWrap(True) + main_layout.addWidget(self.status_label) + + # Dialog buttons + button_box = QDialogButtonBox( + QDialogButtonBox.Save | QDialogButtonBox.Cancel, + Qt.Horizontal + ) + button_box.accepted.connect(self.save_settings) + button_box.rejected.connect(self.reject) + main_layout.addWidget(button_box) + + self.setLayout(main_layout) + + def load_current_settings(self): + """Load current settings into the form""" + if self.api_client.base_url: + self.url_input.setText(self.api_client.base_url) + if self.api_client.api_key: + self.api_key_input.setText(self.api_client.api_key) + + try: + from .api_client import is_logging_enabled, get_private_structure_naming_attribute + except ImportError: + from api_client import is_logging_enabled, get_private_structure_naming_attribute + self.log_events_checkbox.setChecked(is_logging_enabled()) + + naming_attr = get_private_structure_naming_attribute() + index = self.naming_attribute_combo.findText(naming_attr) + if index >= 0: + self.naming_attribute_combo.setCurrentIndex(index) + + def test_connection(self): + """Test the API connection""" + url = self.url_input.text().strip() + api_key = self.api_key_input.text().strip() + + if not url: + QMessageBox.warning(self, "Warning", "Please enter a 3decision URL") + return + + if not api_key: + QMessageBox.warning(self, "Warning", "Please enter an API key") + return + + self.test_button.setEnabled(False) + self.status_label.setText("Testing connection...") + self.status_label.setStyleSheet("color: #60B0DC;") + + # Configure and test + old_url = self.api_client.base_url + old_key = self.api_client.api_key + old_token = self.api_client.token + + self.api_client.configure(url, api_key) + + if self.api_client.test_connection(): + self.status_label.setText("Connection successful!") + self.status_label.setStyleSheet("color: #7FBA7A; font-weight: bold;") + else: + self.status_label.setText("Connection failed. Please check your URL and API key.") + self.status_label.setStyleSheet("color: #FF6B6B; font-weight: bold;") + # Restore old settings + self.api_client.base_url = old_url + self.api_client.api_key = old_key + self.api_client.token = old_token + + self.test_button.setEnabled(True) + + def save_settings(self): + """Save the settings""" + url = self.url_input.text().strip() + api_key = self.api_key_input.text().strip() + + if not url: + QMessageBox.warning(self, "Warning", "Please enter a 3decision URL") + return + + if not api_key: + QMessageBox.warning(self, "Warning", "Please enter an API key") + return + + # Configure and save API settings + self.api_client.configure(url, api_key) + self.api_client.save_config() + + # Save logging setting + log_enabled = self.log_events_checkbox.isChecked() + try: + from .api_client import set_logging_enabled, set_private_structure_naming_attribute + except ImportError: + from api_client import set_logging_enabled, set_private_structure_naming_attribute + set_logging_enabled(log_enabled) + self.api_client.save_logging_setting(log_enabled) + + # Save private structure naming attribute setting + naming_attr = self.naming_attribute_combo.currentText() + set_private_structure_naming_attribute(naming_attr) + self.api_client.save_naming_attribute_setting(naming_attr) + + # Try to login + if self.api_client.login(): + QMessageBox.information(self, "Success", "Settings saved and login successful!") + self.accept() + else: + QMessageBox.warning( + self, + "Warning", + "Settings saved but login failed. Please check your credentials." + ) + + +# Main Panel Class +if MAESTRO_AVAILABLE and af2: + class ThreeDecisionPanel(af2.App): + """Main 3decision plugin panel for Maestro""" + + def setPanelOptions(self): + """Set panel options""" + super().setPanelOptions() + self.title = "3decision Structure Search v1.0" + self.maestro_dockable = True + self.help_topic = "" + self.ui = None + + def setup(self): + """Set up the panel""" + super().setup() + self.api_client = ThreeDecisionAPIClient() + self.search_thread = None + self.load_thread = None + self.all_results = [] + self.projects_data = [] + self.current_project_structures = [] + self.projects_loaded = False + self.current_structure = None + self.current_structure_id = None + self.current_external_code = None + self.current_transform_matrix = None + + def layOut(self): + """Lay out the panel widgets""" + super().layOut() + main_layout = self.main_layout + + # Apply dark theme stylesheet to main panel + self.setStyleSheet(DARK_STYLESHEET) + + # Force dark background using palette + dark_palette = QtGui.QPalette() + dark_color = QtGui.QColor(68, 68, 68) # #444444 + dark_palette.setColor(QtGui.QPalette.Window, dark_color) + dark_palette.setColor(QtGui.QPalette.Base, QtGui.QColor(58, 58, 58)) + dark_palette.setColor(QtGui.QPalette.AlternateBase, dark_color) + dark_palette.setColor(QtGui.QPalette.Button, QtGui.QColor(80, 80, 80)) + dark_palette.setColor(QtGui.QPalette.WindowText, QtGui.QColor(236, 236, 236)) + dark_palette.setColor(QtGui.QPalette.Text, QtGui.QColor(236, 236, 236)) + dark_palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor(236, 236, 236)) + dark_palette.setColor(QtGui.QPalette.Highlight, QtGui.QColor(96, 176, 220)) + dark_palette.setColor(QtGui.QPalette.HighlightedText, QtGui.QColor(255, 255, 255)) + dark_palette.setColor(QtGui.QPalette.PlaceholderText, QtGui.QColor(136, 136, 136)) # #888888 + self.setPalette(dark_palette) + self.setAutoFillBackground(True) + + # Also style the af2 status bar if it exists + if hasattr(self, 'status_bar') and self.status_bar: + self.status_bar.setStyleSheet(""" + QStatusBar { + background-color: #3a3a3a; + color: #ECECEC; + border-top: 1px solid #555555; + } + QLabel { + color: #ECECEC; + background-color: transparent; + } + QPushButton, QToolButton { + background-color: #505050; + color: #ECECEC; + border: 1px solid #606060; + border-radius: 3px; + padding: 3px 8px; + } + QPushButton:hover, QToolButton:hover { + background-color: #585858; + border: 1px solid #60B0DC; + } + QProgressBar { + background-color: #3a3a3a; + border: 1px solid #555555; + border-radius: 3px; + text-align: center; + color: #ECECEC; + } + QProgressBar::chunk { + background-color: #60B0DC; + } + """) + + # Also style the bottom bar if it exists + if hasattr(self, 'bottom_bar') and self.bottom_bar: + self.bottom_bar.setStyleSheet(""" + QWidget { + background-color: #3a3a3a; + color: #ECECEC; + } + QPushButton, QToolButton { + background-color: #505050; + color: #ECECEC; + border: 1px solid #606060; + border-radius: 3px; + padding: 4px 10px; + } + QPushButton:hover, QToolButton:hover { + background-color: #585858; + border: 1px solid #60B0DC; + } + QLabel { + color: #ECECEC; + background-color: transparent; + } + """) + + # Style the bottom_line separator if it exists + if hasattr(self, 'bottom_line') and self.bottom_line: + self.bottom_line.setStyleSheet("background-color: #555555;") + + # Settings button + settings_layout = QHBoxLayout() + settings_layout.addStretch() + + self.settings_button = QPushButton("Settings") + self.settings_button.clicked.connect(self.open_settings) + settings_layout.addWidget(self.settings_button) + + main_layout.addLayout(settings_layout) + + # Tab widget + self.tab_widget = QTabWidget() + + # Search tab + self.search_widget = self._create_search_tab() + self.tab_widget.addTab(self.search_widget, "Search") + + # Projects tab + self.projects_widget = self._create_projects_tab() + self.tab_widget.addTab(self.projects_widget, "Projects") + + # Associated Files tab + self.files_widget = self._create_files_tab() + self.tab_widget.addTab(self.files_widget, "Associated Files") + + main_layout.addWidget(self.tab_widget) + + # Status label + self.status_label = QLabel("Not logged in") + self.status_label.setStyleSheet("color: #FF6B6B; padding: 5px; background-color: #444444;") + self.status_label.setAutoFillBackground(True) + main_layout.addWidget(self.status_label) + + # Apply dark palette to all child widgets recursively + self._apply_dark_palette_recursive(self) + + # Check login status + self.check_login_status() + + def _apply_dark_palette_recursive(self, widget): + """Apply dark palette to widget and all its children""" + dark_palette = QtGui.QPalette() + dark_color = QtGui.QColor(68, 68, 68) + dark_palette.setColor(QtGui.QPalette.Window, dark_color) + dark_palette.setColor(QtGui.QPalette.Base, QtGui.QColor(58, 58, 58)) + dark_palette.setColor(QtGui.QPalette.AlternateBase, dark_color) + dark_palette.setColor(QtGui.QPalette.Button, QtGui.QColor(80, 80, 80)) + dark_palette.setColor(QtGui.QPalette.WindowText, QtGui.QColor(236, 236, 236)) + dark_palette.setColor(QtGui.QPalette.Text, QtGui.QColor(236, 236, 236)) + dark_palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor(236, 236, 236)) + dark_palette.setColor(QtGui.QPalette.Highlight, QtGui.QColor(96, 176, 220)) + dark_palette.setColor(QtGui.QPalette.HighlightedText, QtGui.QColor(255, 255, 255)) + dark_palette.setColor(QtGui.QPalette.PlaceholderText, QtGui.QColor(136, 136, 136)) # #888888 + + widget.setPalette(dark_palette) + widget.setAutoFillBackground(True) + + for child in widget.findChildren(QWidget): + child.setPalette(dark_palette) + child.setAutoFillBackground(True) + + def _create_search_tab(self): + """Create the search tab content""" + widget = QWidget() + layout = QVBoxLayout() + + # Search section + search_layout = QHBoxLayout() + + search_layout.addWidget(QLabel("Search:")) + self.search_input = QLineEdit() + self.search_input.setPlaceholderText("Enter search term (e.g., ABL1)") + self.search_input.returnPressed.connect(self.submit_search) + search_layout.addWidget(self.search_input) + + self.submit_button = QPushButton("Submit") + self.submit_button.clicked.connect(self.submit_search) + self.submit_button.setEnabled(False) + search_layout.addWidget(self.submit_button) + + layout.addLayout(search_layout) + + # Progress bar + self.progress_bar = QProgressBar() + self.progress_bar.setVisible(False) + layout.addWidget(self.progress_bar) + + # Column filters + filters_label = QLabel("Filter Results:") + filters_label.setStyleSheet("font-weight: bold; margin-top: 10px; color: #ECECEC;") + layout.addWidget(filters_label) + + filters_layout = QHBoxLayout() + + self.filter_external_code = QLineEdit() + self.filter_external_code.setPlaceholderText("External Code") + self.filter_external_code.textChanged.connect(self.apply_filters) + filters_layout.addWidget(self.filter_external_code) + + self.filter_label = QLineEdit() + self.filter_label.setPlaceholderText("Label") + self.filter_label.textChanged.connect(self.apply_filters) + filters_layout.addWidget(self.filter_label) + + self.filter_title = QLineEdit() + self.filter_title.setPlaceholderText("Title") + self.filter_title.textChanged.connect(self.apply_filters) + filters_layout.addWidget(self.filter_title) + + self.filter_method = QLineEdit() + self.filter_method.setPlaceholderText("Method") + self.filter_method.textChanged.connect(self.apply_filters) + filters_layout.addWidget(self.filter_method) + + self.filter_resolution = QLineEdit() + self.filter_resolution.setPlaceholderText("Resolution (e.g., <2.0)") + self.filter_resolution.textChanged.connect(self.apply_filters) + filters_layout.addWidget(self.filter_resolution) + + self.filter_source = QLineEdit() + self.filter_source.setPlaceholderText("Source") + self.filter_source.textChanged.connect(self.apply_filters) + filters_layout.addWidget(self.filter_source) + + clear_filters_btn = QPushButton("Clear") + clear_filters_btn.clicked.connect(self.clear_filters) + filters_layout.addWidget(clear_filters_btn) + + layout.addLayout(filters_layout) + + # Results table + self.results_table = QTableWidget() + self.results_table.setColumnCount(6) + self.results_table.setHorizontalHeaderLabels([ + "External Code", "Label", "Title", "Method", "Resolution", "Source" + ]) + self.results_table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.results_table.setSelectionMode(QAbstractItemView.ExtendedSelection) + self.results_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.results_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.results_table.setAlternatingRowColors(True) + self.results_table.setSortingEnabled(True) + + layout.addWidget(self.results_table) + + # Load button + button_layout = QHBoxLayout() + button_layout.addStretch() + + self.load_button = QPushButton("Load Selected in Maestro") + self.load_button.clicked.connect(self.load_selected_structures) + self.load_button.setEnabled(False) + button_layout.addWidget(self.load_button) + + layout.addLayout(button_layout) + + widget.setLayout(layout) + return widget + + def _create_projects_tab(self): + """Create the projects tab content""" + widget = QWidget() + layout = QVBoxLayout() + + layout.addWidget(QLabel("Browse and load structures from your 3decision projects")) + + # Projects table + layout.addWidget(QLabel("Projects:")) + + # Projects filters + projects_filters_layout = QHBoxLayout() + + self.projects_filter_name = QLineEdit() + self.projects_filter_name.setPlaceholderText("Project Name") + self.projects_filter_name.textChanged.connect(self.apply_projects_filters) + projects_filters_layout.addWidget(self.projects_filter_name) + + self.projects_filter_owner = QLineEdit() + self.projects_filter_owner.setPlaceholderText("Owner") + self.projects_filter_owner.textChanged.connect(self.apply_projects_filters) + projects_filters_layout.addWidget(self.projects_filter_owner) + + self.projects_filter_structures = QLineEdit() + self.projects_filter_structures.setPlaceholderText("Structures (e.g., >10)") + self.projects_filter_structures.textChanged.connect(self.apply_projects_filters) + projects_filters_layout.addWidget(self.projects_filter_structures) + + self.projects_filter_id = QLineEdit() + self.projects_filter_id.setPlaceholderText("Project ID") + self.projects_filter_id.textChanged.connect(self.apply_projects_filters) + projects_filters_layout.addWidget(self.projects_filter_id) + + clear_projects_filters_btn = QPushButton("Clear") + clear_projects_filters_btn.clicked.connect(self.clear_projects_filters) + projects_filters_layout.addWidget(clear_projects_filters_btn) + + layout.addLayout(projects_filters_layout) + + self.projects_table = QTableWidget() + self.projects_table.setColumnCount(4) + self.projects_table.setHorizontalHeaderLabels([ + "Project Name", "Owner", "Structures", "Project ID" + ]) + + header = self.projects_table.horizontalHeader() + header.setStretchLastSection(True) + header.resizeSection(0, 200) + header.resizeSection(1, 120) + header.resizeSection(2, 80) + header.resizeSection(3, 150) + + self.projects_table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.projects_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.projects_table.setAlternatingRowColors(True) + self.projects_table.setSortingEnabled(True) + self.projects_table.itemSelectionChanged.connect(self.on_project_selection_changed) + + layout.addWidget(self.projects_table) + + # Projects buttons + projects_buttons = QHBoxLayout() + refresh_button = QPushButton("Refresh Projects") + refresh_button.clicked.connect(self.load_projects_for_tab) + projects_buttons.addWidget(refresh_button) + projects_buttons.addStretch() + layout.addLayout(projects_buttons) + + # Project structures list + layout.addWidget(QLabel("Project Structures:")) + + # Project structures filters + project_filters_layout = QHBoxLayout() + + self.project_filter_external_code = QLineEdit() + self.project_filter_external_code.setPlaceholderText("External Code") + self.project_filter_external_code.textChanged.connect(self.apply_project_filters) + project_filters_layout.addWidget(self.project_filter_external_code) + + self.project_filter_label = QLineEdit() + self.project_filter_label.setPlaceholderText("Label") + self.project_filter_label.textChanged.connect(self.apply_project_filters) + project_filters_layout.addWidget(self.project_filter_label) + + self.project_filter_title = QLineEdit() + self.project_filter_title.setPlaceholderText("Title") + self.project_filter_title.textChanged.connect(self.apply_project_filters) + project_filters_layout.addWidget(self.project_filter_title) + + self.project_filter_method = QLineEdit() + self.project_filter_method.setPlaceholderText("Method") + self.project_filter_method.textChanged.connect(self.apply_project_filters) + project_filters_layout.addWidget(self.project_filter_method) + + clear_project_filters_btn = QPushButton("Clear") + clear_project_filters_btn.clicked.connect(self.clear_project_filters) + project_filters_layout.addWidget(clear_project_filters_btn) + + layout.addLayout(project_filters_layout) + + self.project_structures_table = QTableWidget() + self.project_structures_table.setColumnCount(5) + self.project_structures_table.setHorizontalHeaderLabels([ + "External Code", "Label", "Title", "Method", "Files" + ]) + self.project_structures_table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.project_structures_table.setSelectionMode(QAbstractItemView.ExtendedSelection) + self.project_structures_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + header = self.project_structures_table.horizontalHeader() + header.setSectionResizeMode(QHeaderView.Stretch) + header.setSectionResizeMode(4, QHeaderView.Fixed) # Files column fixed width + self.project_structures_table.setColumnWidth(4, 80) + self.project_structures_table.setAlternatingRowColors(True) + self.project_structures_table.setSortingEnabled(True) + + layout.addWidget(self.project_structures_table) + + # Structure selection buttons + structure_buttons = QHBoxLayout() + select_all_btn = QPushButton("Select All") + select_all_btn.clicked.connect(self.select_all_project_structures) + structure_buttons.addWidget(select_all_btn) + + select_none_btn = QPushButton("Select None") + select_none_btn.clicked.connect(self.select_none_project_structures) + structure_buttons.addWidget(select_none_btn) + + structure_buttons.addStretch() + + self.load_project_structures_button = QPushButton("Load Selected Structures") + self.load_project_structures_button.clicked.connect(self.load_selected_project_structures) + self.load_project_structures_button.setEnabled(False) + structure_buttons.addWidget(self.load_project_structures_button) + + layout.addLayout(structure_buttons) + + widget.setLayout(layout) + return widget + + def _create_files_tab(self): + """Create the associated files tab""" + widget = QWidget() + layout = QVBoxLayout() + + # Header with current structure info + self.files_header_label = QLabel("Associated Files - Select a structure from the Projects tab to view its files") + self.files_header_label.setStyleSheet("font-weight: bold; padding: 5px; color: #ECECEC;") + layout.addWidget(self.files_header_label) + + # Checkbox to apply transformation matrix + transform_layout = QHBoxLayout() + self.apply_transform_checkbox = QCheckBox("Apply transformation matrix to associated files (if available)") + self.apply_transform_checkbox.setToolTip("When enabled, associated files will be transformed using the structure's reference transformation matrix") + self.apply_transform_checkbox.setChecked(True) + transform_layout.addWidget(self.apply_transform_checkbox) + transform_layout.addStretch() + layout.addLayout(transform_layout) + + # Files table + self.files_table = QTableWidget() + self.files_table.setColumnCount(5) + self.files_table.setHorizontalHeaderLabels(["File Name", "Type", "Size", "Format", "Description"]) + self.files_table.setSelectionBehavior(QAbstractItemView.SelectRows) + self.files_table.setAlternatingRowColors(True) + self.files_table.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.files_table.setSortingEnabled(True) + + header = self.files_table.horizontalHeader() + header.setStretchLastSection(True) + header.setSectionResizeMode(QHeaderView.Interactive) + self.files_table.setColumnWidth(0, 200) + self.files_table.setColumnWidth(1, 100) + self.files_table.setColumnWidth(2, 80) + self.files_table.setColumnWidth(3, 100) + + self.files_table.itemSelectionChanged.connect(self._on_file_selection_changed) + layout.addWidget(self.files_table) + + # Buttons for file operations + files_buttons = QHBoxLayout() + + self.refresh_files_button = QPushButton("Refresh Files") + self.refresh_files_button.clicked.connect(self.refresh_associated_files) + self.refresh_files_button.setEnabled(False) + files_buttons.addWidget(self.refresh_files_button) + + files_buttons.addStretch() + + self.open_file_button = QPushButton("Open in Maestro") + self.open_file_button.clicked.connect(self.open_selected_file) + self.open_file_button.setEnabled(False) + files_buttons.addWidget(self.open_file_button) + + self.open_system_button = QPushButton("Download && Open") + self.open_system_button.setToolTip("Download the file and open it with the system's default application") + self.open_system_button.clicked.connect(self.download_and_open_with_system) + self.open_system_button.setEnabled(False) + files_buttons.addWidget(self.open_system_button) + + layout.addLayout(files_buttons) + + # Status label for file operations + self.files_status_label = QLabel("") + self.files_status_label.setStyleSheet("color: #ABABAB; padding: 5px;") + layout.addWidget(self.files_status_label) + + widget.setLayout(layout) + return widget + + def check_login_status(self): + """Check if user is logged in and update UI accordingly""" + if self.api_client.is_configured() and self.api_client.test_connection(): + self.status_label.setText("Logged in successfully") + self.status_label.setStyleSheet("color: #7FBA7A;") + self.submit_button.setEnabled(True) + + if hasattr(self, 'projects_table') and not self.projects_loaded: + try: + self.load_projects_for_tab() + self.projects_loaded = True + except Exception as e: + log_error(f"Failed to auto-load projects: {e}") + else: + self.status_label.setText("Not logged in - click Settings to configure") + self.status_label.setStyleSheet("color: #FF6B6B;") + self.submit_button.setEnabled(False) + + def open_settings(self): + """Open the settings dialog""" + dialog = SettingsDialog(self.api_client, self) + if dialog.exec() == QDialog.Accepted: + self.check_login_status() + + def submit_search(self): + """Submit search query""" + query = self.search_input.text().strip() + if not query: + QMessageBox.warning(self, "Warning", "Please enter a search term") + return + + if not self.api_client.is_configured(): + QMessageBox.warning(self, "Warning", "Please configure API settings first") + return + + self.search_thread = SearchThread(self.api_client, query) + self.search_thread.results_ready.connect(self.display_results) + self.search_thread.error_occurred.connect(self.handle_search_error) + self.search_thread.status_update.connect(self.update_status) + + self.submit_button.setEnabled(False) + self.progress_bar.setVisible(True) + self.progress_bar.setRange(0, 0) + + self.search_thread.start() + + def update_status(self, message: str): + """Update status message""" + self.status_label.setText(message) + + def display_results(self, structures: List[Dict[str, Any]]): + """Display search results in the table""" + self.progress_bar.setVisible(False) + self.submit_button.setEnabled(True) + self.check_login_status() + + self.all_results = structures + self.populate_results_table(structures) + + if len(structures) == 0: + QMessageBox.information(self, "Search Results", "No structures found for your search query.") + else: + QMessageBox.information(self, "Search Results", f"Found {len(structures)} structures.") + + def populate_results_table(self, structures: List[Dict[str, Any]]): + """Populate the results table with given structures""" + self.results_table.setSortingEnabled(False) + self.results_table.setRowCount(len(structures)) + + for row, structure in enumerate(structures): + general = structure.get('general', {}) + + external_code = general.get('external_code', 'N/A') + item = QTableWidgetItem(external_code) + item.setData(Qt.UserRole, general.get('structure_id')) + self.results_table.setItem(row, 0, item) + + label = general.get('label', 'N/A') + self.results_table.setItem(row, 1, QTableWidgetItem(label)) + + title = general.get('title', 'N/A') + self.results_table.setItem(row, 2, QTableWidgetItem(title)) + + method = general.get('method', 'N/A') + self.results_table.setItem(row, 3, QTableWidgetItem(method)) + + resolution = general.get('resolution') + resolution_text = f"{resolution:.2f} A" if resolution else "N/A" + numeric_value = resolution if resolution is not None else float('inf') + resolution_item = NumericTableWidgetItem(resolution_text, numeric_value) + self.results_table.setItem(row, 4, resolution_item) + + source = general.get('source', 'N/A') + self.results_table.setItem(row, 5, QTableWidgetItem(source)) + + self.results_table.setSortingEnabled(True) + self.load_button.setEnabled(len(structures) > 0) + + def handle_search_error(self, error_message: str): + """Handle search errors""" + self.progress_bar.setVisible(False) + self.submit_button.setEnabled(True) + self.check_login_status() + QMessageBox.critical(self, "Search Error", error_message) + + def load_selected_structures(self): + """Load selected structures into Maestro""" + selected_structures = [] + + selected_rows = self.results_table.selectionModel().selectedRows() + log_debug(f"Found {len(selected_rows)} selected rows in search results") + + for index in selected_rows: + row = index.row() + item = self.results_table.item(row, 0) + if item: + structure_id = item.data(Qt.UserRole) + external_code = item.text() + + label_item = self.results_table.item(row, 1) + title_item = self.results_table.item(row, 2) + source_item = self.results_table.item(row, 5) + label = label_item.text() if label_item else None + title = title_item.text() if title_item else None + source = source_item.text() if source_item else None + + if structure_id: + selected_structures.append({ + 'structure_id': str(structure_id), + 'external_code': external_code, + 'label': label, + 'title': title, + 'source': source + }) + + if not selected_structures: + QMessageBox.warning(self, "Warning", "Please select at least one structure to load") + return + + self.load_thread = LoadStructureThread(self.api_client, selected_structures) + self.load_thread.pdb_content_ready.connect(self.load_pdb_into_maestro) + self.load_thread.structure_loaded.connect(self.handle_structure_loaded) + self.load_thread.error_occurred.connect(self.handle_load_error) + self.load_thread.status_update.connect(self.update_status) + self.load_thread.all_structures_loaded.connect(self.handle_all_structures_loaded) + + self.load_button.setEnabled(False) + self.progress_bar.setVisible(True) + self.progress_bar.setRange(0, 0) + + self.load_thread.start() + + def load_pdb_into_maestro(self, pdb_content: str, object_name: str, metadata: dict): + """Load PDB content into Maestro""" + try: + # Save PDB to temporary file + temp_dir = tempfile.gettempdir() + pdb_filename = f"{object_name}.pdb" + pdb_path = os.path.join(temp_dir, pdb_filename) + + with open(pdb_path, 'w') as f: + f.write(pdb_content) + + log_debug(f"Saved PDB to: {pdb_path}") + + # Read structure using Schrodinger's structure module + st = structure.StructureReader.read(pdb_path) + + # Add metadata as properties + for key, value in metadata.items(): + st.property[f's_user_{key}'] = str(value) + + # Set the structure title + st.title = object_name + + # Import into Maestro project + if MAESTRO_AVAILABLE: + pt = maestro.project_table_get() + pt.importStructure(st) + log_info(f"Imported structure {object_name} into Maestro project") + else: + log_debug(f"Would import structure {object_name} into Maestro (not in Maestro environment)") + + # Clean up temp file + try: + os.remove(pdb_path) + except: + pass + + except Exception as e: + log_error(f"Error loading PDB into Maestro: {e}") + + def handle_structure_loaded(self, structure_id: str, object_name: str): + """Handle successful structure loading""" + log_info(f"Loaded structure {structure_id} as {object_name}") + + def handle_all_structures_loaded(self): + """Handle completion of all structure loading""" + self.progress_bar.setVisible(False) + self.load_button.setEnabled(True) + if hasattr(self, 'load_project_structures_button'): + self.load_project_structures_button.setEnabled(True) + self.check_login_status() + + def handle_load_error(self, error_message: str): + """Handle structure loading errors""" + self.progress_bar.setVisible(False) + self.load_button.setEnabled(True) + if hasattr(self, 'load_project_structures_button'): + self.load_project_structures_button.setEnabled(True) + QMessageBox.critical(self, "Load Error", error_message) + + # Filter methods for search results + def clear_filters(self): + """Clear all filter inputs without triggering filtering""" + self.filter_external_code.blockSignals(True) + self.filter_label.blockSignals(True) + self.filter_title.blockSignals(True) + self.filter_method.blockSignals(True) + self.filter_resolution.blockSignals(True) + self.filter_source.blockSignals(True) + + self.filter_external_code.clear() + self.filter_label.clear() + self.filter_title.clear() + self.filter_method.clear() + self.filter_resolution.clear() + self.filter_source.clear() + + self.filter_external_code.blockSignals(False) + self.filter_label.blockSignals(False) + self.filter_title.blockSignals(False) + self.filter_method.blockSignals(False) + self.filter_resolution.blockSignals(False) + self.filter_source.blockSignals(False) + + # Show all results + if hasattr(self, 'all_results') and self.all_results: + self.populate_results_table(self.all_results) + self.status_label.setText(f"Showing all {len(self.all_results)} results") + + def apply_filters(self): + """Apply column filters to the results table""" + if not hasattr(self, 'all_results') or not self.all_results: + return + + filter_ext_code = self.filter_external_code.text().strip().lower() + filter_lbl = self.filter_label.text().strip().lower() + filter_ttl = self.filter_title.text().strip().lower() + filter_mth = self.filter_method.text().strip().lower() + filter_res = self.filter_resolution.text().strip() + filter_src = self.filter_source.text().strip().lower() + + res_min, res_max, res_operator = self.parse_resolution_filter(filter_res) + + filtered_structures = [] + for struct in self.all_results: + general = struct.get('general', {}) + + if filter_ext_code and filter_ext_code not in general.get('external_code', '').lower(): + continue + if filter_lbl and filter_lbl not in general.get('label', '').lower(): + continue + if filter_ttl and filter_ttl not in general.get('title', '').lower(): + continue + if filter_mth and filter_mth not in general.get('method', '').lower(): + continue + if filter_src and filter_src not in general.get('source', '').lower(): + continue + + if filter_res: + resolution = general.get('resolution') + if not self.check_resolution_filter(resolution, res_min, res_max, res_operator): + continue + + filtered_structures.append(struct) + + self.populate_results_table(filtered_structures) + + if len(filtered_structures) < len(self.all_results): + self.status_label.setText(f"Showing {len(filtered_structures)} of {len(self.all_results)} results") + else: + self.status_label.setText(f"Showing all {len(self.all_results)} results") + + def parse_resolution_filter(self, filter_text: str): + """ + Parse resolution filter string. + Supports formats: <2.0, >1.5, <=2.0, >=1.5, 1.5-3.0, 2.0 + Returns: (min_value, max_value, operator) + """ + if not filter_text: + return None, None, None + + filter_text = filter_text.strip() + + # Range format: 1.5-3.0 + if '-' in filter_text and not filter_text.startswith('-'): + parts = filter_text.split('-') + if len(parts) == 2: + try: + min_val = float(parts[0].strip()) + max_val = float(parts[1].strip()) + return min_val, max_val, 'range' + except ValueError: + return None, None, None + + # Operator formats: <, >, <=, >= + if filter_text.startswith('<='): + try: + val = float(filter_text[2:].strip()) + return None, val, '<=' + except ValueError: + return None, None, None + elif filter_text.startswith('>='): + try: + val = float(filter_text[2:].strip()) + return val, None, '>=' + except ValueError: + return None, None, None + elif filter_text.startswith('<'): + try: + val = float(filter_text[1:].strip()) + return None, val, '<' + except ValueError: + return None, None, None + elif filter_text.startswith('>'): + try: + val = float(filter_text[1:].strip()) + return val, None, '>' + except ValueError: + return None, None, None + + # Exact value + try: + val = float(filter_text) + return val, val, '=' + except ValueError: + return None, None, None + + def check_resolution_filter(self, resolution, min_val, max_val, operator): + """Check if resolution value passes the filter""" + if resolution is None: + return False + + if operator == 'range': + return min_val <= resolution <= max_val + elif operator == '<': + return resolution < max_val + elif operator == '<=': + return resolution <= max_val + elif operator == '>': + return resolution > min_val + elif operator == '>=': + return resolution >= min_val + elif operator == '=': + return abs(resolution - min_val) < 0.01 + + return True + + # Projects tab methods + def load_projects_for_tab(self): + """Load projects when Projects tab is accessed""" + try: + if not self.api_client.is_authenticated(): + log_debug("Not authenticated, attempting to authenticate") + if self.api_client.test_connection(): + log_debug("Successfully authenticated") + self.check_login_status() + else: + reply = QMessageBox.question( + self, + "Authentication Required", + "You need to be logged in to view projects.\n\n" + "Would you like to open settings to configure your API token?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.Yes + ) + + if reply == QMessageBox.Yes: + self.open_settings() + return + + projects = self.api_client.get_projects() + + # Filter out the "3decision" project + filtered_projects = [] + for project in projects: + project_label = project.get('project_label', '').lower() + project_name = project.get('project_name', '').lower() + + if project_label != '3decision' and project_name != '3decision': + filtered_projects.append(project) + + self.projects_data = filtered_projects + self.populate_projects_table() + + except Exception as e: + log_error(f"Failed to load projects: {e}") + QMessageBox.critical(self, "Error", f"Failed to load projects: {str(e)}") + + def populate_projects_table(self): + """Populate the projects table with data""" + self.projects_table.setSortingEnabled(False) + self.projects_table.setRowCount(len(self.projects_data)) + + for row, project in enumerate(self.projects_data): + name_item = QTableWidgetItem(project.get('project_label', '')) + name_item.setFlags(name_item.flags() & ~Qt.ItemIsEditable) + name_item.setData(Qt.UserRole, project) + self.projects_table.setItem(row, 0, name_item) + + owner = '' + for field in ['owner', 'project_owner', 'created_by', 'owner_username', 'creator', 'author', 'username']: + if project.get(field): + owner = project.get(field) + break + + owner_item = QTableWidgetItem(str(owner)) + owner_item.setFlags(owner_item.flags() & ~Qt.ItemIsEditable) + self.projects_table.setItem(row, 1, owner_item) + + count = project.get('count_structures_in_project', 0) + count_item = NumericTableWidgetItem(str(count), count) + count_item.setFlags(count_item.flags() & ~Qt.ItemIsEditable) + self.projects_table.setItem(row, 2, count_item) + + id_item = QTableWidgetItem(str(project.get('project_id', ''))) + id_item.setFlags(id_item.flags() & ~Qt.ItemIsEditable) + self.projects_table.setItem(row, 3, id_item) + + self.projects_table.setSortingEnabled(True) + + def clear_projects_filters(self): + """Clear all projects filter inputs without triggering filtering""" + self.projects_filter_name.blockSignals(True) + self.projects_filter_owner.blockSignals(True) + self.projects_filter_structures.blockSignals(True) + self.projects_filter_id.blockSignals(True) + + self.projects_filter_name.clear() + self.projects_filter_owner.clear() + self.projects_filter_structures.clear() + self.projects_filter_id.clear() + + self.projects_filter_name.blockSignals(False) + self.projects_filter_owner.blockSignals(False) + self.projects_filter_structures.blockSignals(False) + self.projects_filter_id.blockSignals(False) + + self.apply_projects_filters() + + def apply_projects_filters(self): + """Apply column filters to the projects table""" + if not hasattr(self, 'projects_data') or not self.projects_data: + return + + filter_name = self.projects_filter_name.text().strip().lower() + filter_owner = self.projects_filter_owner.text().strip().lower() + filter_structures = self.projects_filter_structures.text().strip() + filter_id = self.projects_filter_id.text().strip().lower() + + struct_min, struct_max, struct_operator = self.parse_resolution_filter(filter_structures) + + filtered_projects = [] + for project in self.projects_data: + project_name = str(project.get('project_label', '')).lower() + + owner = '' + for field in ['owner', 'project_owner', 'created_by', 'owner_username', 'creator', 'author', 'username']: + if project.get(field): + owner = str(project.get(field)).lower() + break + + structures_count = project.get('count_structures_in_project', 0) + project_id = str(project.get('project_id', '')).lower() + + if filter_name and filter_name not in project_name: + continue + if filter_owner and filter_owner not in owner: + continue + if filter_id and filter_id not in project_id: + continue + + if filter_structures: + if not self.check_resolution_filter(structures_count, struct_min, struct_max, struct_operator): + continue + + filtered_projects.append(project) + + self.projects_table.setSortingEnabled(False) + self.projects_table.setRowCount(len(filtered_projects)) + + for row, project in enumerate(filtered_projects): + name_item = QTableWidgetItem(project.get('project_label', '')) + name_item.setFlags(name_item.flags() & ~Qt.ItemIsEditable) + name_item.setData(Qt.UserRole, project) + self.projects_table.setItem(row, 0, name_item) + + owner = '' + for field in ['owner', 'project_owner', 'created_by', 'owner_username', 'creator', 'author', 'username']: + if project.get(field): + owner = project.get(field) + break + + owner_item = QTableWidgetItem(str(owner)) + owner_item.setFlags(owner_item.flags() & ~Qt.ItemIsEditable) + self.projects_table.setItem(row, 1, owner_item) + + count = project.get('count_structures_in_project', 0) + count_item = NumericTableWidgetItem(str(count), count) + count_item.setFlags(count_item.flags() & ~Qt.ItemIsEditable) + self.projects_table.setItem(row, 2, count_item) + + id_item = QTableWidgetItem(str(project.get('project_id', ''))) + id_item.setFlags(id_item.flags() & ~Qt.ItemIsEditable) + self.projects_table.setItem(row, 3, id_item) + + self.projects_table.setSortingEnabled(True) + + def on_project_selection_changed(self): + """Handle project table selection change""" + selected_rows = self.projects_table.selectionModel().selectedRows() + if selected_rows: + self.load_project_structures_button.setEnabled(False) + row = selected_rows[0].row() + project_data = self.projects_table.item(row, 0).data(Qt.UserRole) + project_id = project_data.get('project_id') + self.load_project_structures_in_tab(project_id) + + def load_project_structures_in_tab(self, project_id): + """Load structures for the selected project""" + try: + structures = self.api_client.get_project_structures(project_id) + + log_debug(f"Received {len(structures)} structures from get_project_structures") + + if not structures: + self.current_project_structures = [] + self.populate_project_structures_table([]) + return + + structure_ids = [] + for s in structures: + structure_id = s.get('STRUCTURE_ID') or s.get('structure_id') + if structure_id: + structure_ids.append(int(structure_id)) + + log_debug(f"Fetching details for {len(structure_ids)} structures...") + + detailed_structures = self.api_client.get_structures_info(structure_ids) + + details_map = {} + for detail in detailed_structures: + sid = detail.get('structure_id') or detail.get('general', {}).get('structure_id') + if sid: + details_map[int(sid)] = detail + + enriched_structures = [] + for s in structures: + structure_id = s.get('STRUCTURE_ID') or s.get('structure_id') + if structure_id and int(structure_id) in details_map: + enriched = details_map[int(structure_id)].copy() + matrix = None + ref_transforms = s.get('ReferenceTransforms') + if ref_transforms and isinstance(ref_transforms, dict): + transform = ref_transforms.get('transform') + if transform and isinstance(transform, list) and len(transform) == 16: + matrix = [ + transform[0:4], + transform[4:8], + transform[8:12], + transform[12:16] + ] + enriched['TRANSFORM_MATRIX'] = matrix + enriched_structures.append(enriched) + else: + enriched_structures.append(s) + + self.current_project_structures = enriched_structures + self.populate_project_structures_table(enriched_structures) + + except Exception as e: + log_error(f"Failed to load project structures: {e}") + QMessageBox.critical(self, "Error", f"Failed to load project structures: {str(e)}") + + def populate_project_structures_table(self, structures): + """Populate the project structures table""" + self.project_structures_table.setSortingEnabled(False) + self.project_structures_table.setRowCount(len(structures)) + + for row, structure in enumerate(structures): + external_code = (structure.get('EXTERNAL_CODE') or + structure.get('external_code') or + structure.get('general', {}).get('external_code', 'N/A')) + + item = QTableWidgetItem(external_code) + + structure_id = (structure.get('STRUCTURE_ID') or + structure.get('structure_id') or + structure.get('general', {}).get('structure_id')) + + transform_matrix = (structure.get('TRANSFORM_MATRIX') or + structure.get('matrix') or + structure.get('MATRIX')) + + source = (structure.get('SOURCE') or + structure.get('source') or + structure.get('general', {}).get('source')) + + item.setData(Qt.UserRole, { + 'structure_id': structure_id, + 'matrix': transform_matrix, + 'source': source + }) + self.project_structures_table.setItem(row, 0, item) + + label = (structure.get('LABEL') or + structure.get('label') or + structure.get('PROJECT_LABEL') or + structure.get('project_label') or + structure.get('general', {}).get('label', external_code)) + self.project_structures_table.setItem(row, 1, QTableWidgetItem(label)) + + title = (structure.get('TITLE') or + structure.get('title') or + structure.get('general', {}).get('title', external_code)) + self.project_structures_table.setItem(row, 2, QTableWidgetItem(title)) + + method = (structure.get('METHOD') or + structure.get('method') or + structure.get('general', {}).get('method', 'N/A')) + self.project_structures_table.setItem(row, 3, QTableWidgetItem(method)) + + # Files button + files_button = QPushButton("View") + files_button.setProperty('structure_data', structure) + files_button.clicked.connect(lambda checked, s=structure: self.view_structure_files(s)) + self.project_structures_table.setCellWidget(row, 4, files_button) + + self.project_structures_table.setSortingEnabled(True) + self.load_project_structures_button.setEnabled(len(structures) > 0) + + def clear_project_filters(self): + """Clear all project structure filter inputs without triggering filtering""" + self.project_filter_external_code.blockSignals(True) + self.project_filter_label.blockSignals(True) + self.project_filter_title.blockSignals(True) + self.project_filter_method.blockSignals(True) + + self.project_filter_external_code.clear() + self.project_filter_label.clear() + self.project_filter_title.clear() + self.project_filter_method.clear() + + self.project_filter_external_code.blockSignals(False) + self.project_filter_label.blockSignals(False) + self.project_filter_title.blockSignals(False) + self.project_filter_method.blockSignals(False) + + self.apply_project_filters() + + def apply_project_filters(self): + """Apply column filters to the project structures table""" + if not hasattr(self, 'current_project_structures') or not self.current_project_structures: + return + + filter_ext_code = self.project_filter_external_code.text().strip().lower() + filter_lbl = self.project_filter_label.text().strip().lower() + filter_ttl = self.project_filter_title.text().strip().lower() + filter_mth = self.project_filter_method.text().strip().lower() + + filtered_structures = [] + for struct in self.current_project_structures: + external_code = (struct.get('EXTERNAL_CODE') or + struct.get('external_code') or + struct.get('general', {}).get('external_code', 'N/A')) + + label = (struct.get('LABEL') or + struct.get('label') or + struct.get('PROJECT_LABEL') or + struct.get('project_label') or + struct.get('general', {}).get('label', external_code)) + + title = (struct.get('TITLE') or + struct.get('title') or + struct.get('general', {}).get('title', external_code)) + + method = (struct.get('METHOD') or + struct.get('method') or + struct.get('general', {}).get('method', 'N/A')) + + external_code_lower = str(external_code).lower() if external_code else '' + label_lower = str(label).lower() if label else '' + title_lower = str(title).lower() if title else '' + method_lower = str(method).lower() if method else '' + + if filter_ext_code and filter_ext_code not in external_code_lower: + continue + if filter_lbl and filter_lbl not in label_lower: + continue + if filter_ttl and filter_ttl not in title_lower: + continue + if filter_mth and filter_mth not in method_lower: + continue + + filtered_structures.append(struct) + + self.populate_project_structures_table(filtered_structures) + + def select_all_project_structures(self): + """Select all structures in the project structures table""" + self.project_structures_table.selectAll() + + def select_none_project_structures(self): + """Deselect all structures in the project structures table""" + self.project_structures_table.clearSelection() + + def load_selected_project_structures(self): + """Load selected structures from the project""" + selected_structures = [] + + selected_rows = self.project_structures_table.selectionModel().selectedRows() + log_debug(f"Found {len(selected_rows)} selected rows") + + for index in selected_rows: + row = index.row() + item = self.project_structures_table.item(row, 0) + if item: + data = item.data(Qt.UserRole) + external_code = item.text() + + label_item = self.project_structures_table.item(row, 1) + label = label_item.text() if label_item else None + + title_item = self.project_structures_table.item(row, 2) + title = title_item.text() if title_item else None + + if isinstance(data, dict): + structure_id = data.get('structure_id') + matrix = data.get('matrix') + source = data.get('source') + else: + structure_id = data + matrix = None + source = None + + if structure_id: + selected_structures.append({ + 'structure_id': str(structure_id), + 'external_code': external_code, + 'label': label, + 'title': title, + 'source': source, + 'matrix': matrix + }) + + if not selected_structures: + QMessageBox.warning(self, "Warning", "Please select at least one structure to load") + return + + self.load_thread = LoadStructureThread(self.api_client, selected_structures) + self.load_thread.pdb_content_ready.connect(self.load_pdb_into_maestro) + self.load_thread.structure_loaded.connect(self.handle_structure_loaded) + self.load_thread.error_occurred.connect(self.handle_load_error) + self.load_thread.status_update.connect(self.update_status) + self.load_thread.all_structures_loaded.connect(self.handle_all_structures_loaded) + + self.load_project_structures_button.setEnabled(False) + self.status_label.setText("Loading structures...") + + self.load_thread.start() + + # ============================================= + # Associated Files functionality + # ============================================= + + def view_structure_files(self, structure): + """View associated files for a structure""" + if not structure: + return + + structure_id = str(structure.get('STRUCTURE_ID') or structure.get('structure_id') or + structure.get('general', {}).get('structure_id', '')) + external_code = (structure.get('EXTERNAL_CODE') or structure.get('external_code') or + structure.get('general', {}).get('external_code', structure_id)) + + # Store current structure for file operations + self.current_structure = structure + self.current_structure_id = structure_id + self.current_external_code = external_code + + # Extract and store transformation matrix if available + self.current_transform_matrix = None + matrix = structure.get('TRANSFORM_MATRIX') + if not matrix: + ref_transforms = structure.get('ReferenceTransforms') + if ref_transforms and isinstance(ref_transforms, dict): + transform = ref_transforms.get('transform') + if transform and isinstance(transform, list) and len(transform) == 16: + matrix = [ + transform[0:4], + transform[4:8], + transform[8:12], + transform[12:16] + ] + self.current_transform_matrix = matrix + + # Update header and switch to files tab + matrix_status = " (with transformation matrix)" if matrix else "" + self.files_header_label.setText(f"Associated Files for {external_code} (ID: {structure_id}){matrix_status}") + self.tab_widget.setCurrentIndex(2) # Switch to Associated Files tab + + # Enable refresh button + self.refresh_files_button.setEnabled(True) + + # Load files using external_code + self.load_associated_files(external_code) + + def load_associated_files(self, external_code): + """Load associated files for a structure using external code""" + try: + self.files_status_label.setText("Loading associated files...") + + files = self.api_client.get_associated_files(external_code) + + if files: + self.populate_files_table(files) + self.files_status_label.setText(f"Loaded {len(files)} associated files") + else: + self.files_table.setRowCount(0) + self.files_status_label.setText("No associated files found") + + except Exception as e: + log_error(f"Failed to load associated files: {e}") + self.files_status_label.setText(f"Error loading files: {str(e)}") + self.files_table.setRowCount(0) + + def populate_files_table(self, files): + """Populate the files table with file data""" + self.files_table.setRowCount(len(files)) + + for row, file_info in enumerate(files): + filename = file_info.get('file_name', 'Unknown') + file_type = file_info.get('file_type_label', 'Unknown') + file_size = "N/A" + file_extension = file_info.get('file_type_extension', '') + file_format = self._get_file_format_from_extension(file_extension, filename) + description = file_info.get('file_desc', '') or '' + + name_item = QTableWidgetItem(filename) + name_item.setData(Qt.UserRole, file_info) + self.files_table.setItem(row, 0, name_item) + self.files_table.setItem(row, 1, QTableWidgetItem(str(file_type))) + self.files_table.setItem(row, 2, QTableWidgetItem(str(file_size))) + self.files_table.setItem(row, 3, QTableWidgetItem(file_format)) + self.files_table.setItem(row, 4, QTableWidgetItem(description)) + + def _get_file_format_from_extension(self, extension, filename=""): + """Determine file format from extension and filename""" + if not extension: + if filename and '.' in filename: + extension = filename.lower().split('.')[-1] + else: + return "Unknown" + + extension = extension.lower() + + # Maestro-supported formats and other common formats + format_map = { + 'pdb': 'PDB Structure', + 'cif': 'mmCIF Structure', + 'mmcif': 'mmCIF Structure', + 'mae': 'Maestro File', + 'maegz': 'Maestro File (compressed)', + 'sdf': 'SDF Molecule', + 'mol2': 'MOL2 Molecule', + 'mol': 'MOL Molecule', + 'xyz': 'XYZ Coordinates', + 'mtz': 'MTZ Reflection', + 'mrc': 'MRC Map', + 'map': 'CCP4 Map', + 'ccp4': 'CCP4 Map', + 'dsn6': 'DSN6 Map', + 'xplor': 'XPLOR Map', + 'dx': 'DX Map', + 'smi': 'SMILES File', + 'csv': 'CSV File', + 'txt': 'Text File', + 'pdf': 'PDF File', + 'png': 'PNG Image', + 'jpg': 'JPG Image', + 'jpeg': 'JPEG Image', + 'tif': 'TIF Image', + 'tiff': 'TIFF Image', + 'doc': 'DOC File', + 'docx': 'DOCX File', + 'xls': 'XLS File', + 'xlsx': 'XLSX File', + } + + return format_map.get(extension, f"{extension.upper()} File" if extension else "Unknown") + + def _on_file_selection_changed(self): + """Handle file selection change""" + selected_items = self.files_table.selectedItems() + has_selection = len(selected_items) > 0 + + self.open_file_button.setEnabled(has_selection) + self.open_system_button.setEnabled(has_selection) + + def refresh_associated_files(self): + """Refresh the associated files list""" + if hasattr(self, 'current_external_code') and self.current_external_code: + self.load_associated_files(self.current_external_code) + + def open_selected_file(self): + """Open the selected file in Maestro""" + selected_row = self.files_table.currentRow() + if selected_row < 0: + QMessageBox.warning(self, "Warning", "Please select a file to open") + return + + file_item = self.files_table.item(selected_row, 0) + if not file_item: + return + + file_info = file_item.data(Qt.UserRole) + filename = file_item.text() + file_format = self.files_table.item(selected_row, 3).text() + + # Maestro-supported structure formats + structure_formats = ['PDB Structure', 'mmCIF Structure', 'Maestro File', 'Maestro File (compressed)', + 'SDF Molecule', 'MOL2 Molecule', 'MOL Molecule', 'XYZ Coordinates'] + # Map/density formats that can be directly imported via visimport + supported_map_formats = ['CCP4 Map', 'MRC Map'] + # Map formats that are NOT supported for direct import + unsupported_map_formats = { + 'DSN6 Map': 'DSN6 format is not supported by Maestro.', + 'MTZ Reflection': 'MTZ files contain reflection data, not real-space maps.\nThey must be converted to CCP4 format first using tools like sf2map.', + 'XPLOR Map': 'XPLOR map format is not supported for direct import.', + 'DX Map': 'DX map format is not supported for direct import.' + } + + supported_formats = structure_formats + supported_map_formats + + # Check for unsupported map formats first + if file_format in unsupported_map_formats: + QMessageBox.warning(self, "Unsupported Map Format", + f"{unsupported_map_formats[file_format]}\n\n" + f"Use the 'Download & Open' button to save the file locally.") + return + + if file_format not in supported_formats: + QMessageBox.information(self, "Use System Application", + f"'{file_format}' files cannot be opened directly in Maestro.\n\n" + f"Use the 'Download & Open' button to open this file " + f"with your system's default application.") + return + + try: + self.files_status_label.setText(f"Opening {filename} in Maestro...") + + file_data = self.api_client.download_file(file_info) + + if file_data: + temp_dir = tempfile.gettempdir() + file_path = os.path.join(temp_dir, filename) + + with open(file_path, 'wb') as f: + f.write(file_data) + + log_debug(f"Saved file to: {file_path}") + + if MAESTRO_AVAILABLE: + if file_format in structure_formats: + # Load structure into Maestro + st = structure.StructureReader.read(file_path) + obj_name = os.path.splitext(filename)[0] + st.title = obj_name + + # Apply transformation matrix if enabled + if self.apply_transform_checkbox.isChecked() and self.current_transform_matrix: + try: + self._apply_transform_to_structure(st, self.current_transform_matrix) + self.files_status_label.setText(f"Opened {filename} with transformation applied") + except Exception as matrix_error: + log_error(f"Could not apply transformation matrix: {matrix_error}") + self.files_status_label.setText(f"Opened {filename} (could not apply transformation)") + else: + self.files_status_label.setText(f"Opened {filename} in Maestro") + + pt = maestro.project_table_get() + pt.importStructure(st) + log_info(f"Imported file {filename} into Maestro project") + else: + # Map files - try to import onto currently selected workspace entry + pt = maestro.project_table_get() + included_rows = list(pt.included_rows) + + if included_rows: + # Get the first included entry's ID + entry_row = included_rows[0] + entry_id = entry_row.entry_id + + # Determine map type - CCP4 and MRC are both read as ccp4 format + # file_format here is the display name like 'CCP4 Map' or 'MRC Map' + map_type = 'ccp4' # Both CCP4 and MRC use ccp4 map type + + # Use visimport command to load the map onto the entry + surface_name = os.path.splitext(filename)[0] + cmd = f'visimport entry={entry_id} map_type={map_type} isovalue=1.0 "{file_path}":::{surface_name}' + log_debug(f"Importing map with command: {cmd}") + + try: + maestro.command(cmd) + self.files_status_label.setText(f"Imported {filename} onto entry {entry_id}") + log_info(f"Imported map file {filename} onto entry {entry_id}") + except Exception as cmd_error: + log_error(f"visimport command failed: {cmd_error}") + self.files_status_label.setText(f"Map file saved to temp folder") + QMessageBox.warning( + self, "Map Import", + f"Could not automatically import map: {cmd_error}\n\n" + f"Map file saved to:\n{file_path}\n\n" + "Try using File > Import Structures/Volumes manually." + ) + else: + # No entry selected in workspace + self.files_status_label.setText(f"No entry selected - map saved to temp folder") + QMessageBox.information( + self, "Map File", + f"Map file '{filename}' has been downloaded to:\n{file_path}\n\n" + "To automatically import map files:\n" + "1. First include a structure in the Workspace\n" + "2. Then click 'Open in Maestro' for the map\n\n" + "Or use File > Import Structures/Volumes manually." + ) + else: + self.files_status_label.setText("Maestro not available") + QMessageBox.warning(self, "Warning", "Maestro is not available. Cannot open file.") + + # Clean up temp files for structure formats (map files may be needed if not auto-imported) + if file_format in structure_formats: + try: + os.remove(file_path) + except: + pass + + else: + self.files_status_label.setText("Failed to download file") + QMessageBox.warning(self, "Error", "Failed to download file for opening") + + except Exception as e: + log_error(f"Error opening file: {e}") + self.files_status_label.setText(f"Error opening file: {str(e)}") + QMessageBox.critical(self, "Open Error", str(e)) + + def _apply_transform_to_structure(self, st, matrix): + """Apply a 4x4 transformation matrix to a Schrodinger structure""" + import numpy as np + + # Convert matrix to flat list if needed + if isinstance(matrix, list) and len(matrix) == 4 and isinstance(matrix[0], list): + flat_matrix = [] + for row in matrix: + flat_matrix.extend(row) + matrix = flat_matrix + + if len(matrix) != 16: + raise ValueError(f"Invalid matrix length: {len(matrix)}, expected 16") + + # Convert to 4x4 numpy array + transform = np.array(matrix).reshape(4, 4) + + # Apply transformation to all atoms + for atom in st.atom: + coords = np.array([atom.x, atom.y, atom.z, 1.0]) + new_coords = transform @ coords + atom.x, atom.y, atom.z = new_coords[:3] + + log_debug(f"Applied transformation matrix to structure") + + def download_and_open_with_system(self): + """Download the selected file and open it with the system's default application.""" + selected_row = self.files_table.currentRow() + if selected_row < 0: + QMessageBox.warning(self, "Warning", "Please select a file to download") + return + + file_item = self.files_table.item(selected_row, 0) + if not file_item: + return + + file_info = file_item.data(Qt.UserRole) + filename = file_item.text() + + try: + self.files_status_label.setText(f"Downloading {filename}...") + + file_data = self.api_client.download_file(file_info) + + if file_data: + import subprocess + + temp_dir = os.path.join(tempfile.gettempdir(), '3decision_downloads') + os.makedirs(temp_dir, exist_ok=True) + file_path = os.path.join(temp_dir, filename) + + with open(file_path, 'wb') as f: + f.write(file_data) + + self.files_status_label.setText(f"Opening {filename} with system application...") + + self._open_file_with_system(file_path) + + self.files_status_label.setText(f"Downloaded and opened {filename}") + + else: + self.files_status_label.setText("Failed to download file") + QMessageBox.warning(self, "Error", "Failed to download file") + + except Exception as e: + log_error(f"Error downloading file: {e}") + self.files_status_label.setText(f"Error: {str(e)}") + QMessageBox.critical(self, "Error", f"Failed to download and open file: {str(e)}") + + def _open_file_with_system(self, file_path: str): + """Open a file with the system's default application.""" + import subprocess + + if sys.platform == 'darwin': # macOS + subprocess.run(['open', file_path], check=True) + elif sys.platform == 'win32': # Windows + os.startfile(file_path) + else: # Linux + subprocess.run(['xdg-open', file_path], check=True) + +else: + # Fallback for when Maestro is not available (testing outside Maestro) + class ThreeDecisionPanel(QDialog): + """Standalone dialog for testing outside Maestro""" + + _instance = None + + @classmethod + def panel(cls, run=True): + """Return or create a singleton instance""" + if cls._instance is None: + cls._instance = cls() + if run: + cls._instance.show() + return cls._instance + + def __init__(self, parent=None): + super().__init__(parent) + self.api_client = ThreeDecisionAPIClient() + self.search_thread = None + self.load_thread = None + self.all_results = [] + self.projects_data = [] + self.current_project_structures = [] + self.projects_loaded = False + + self.setWindowTitle("3decision Structure Search v1.0 (Standalone)") + self.setMinimumSize(800, 600) + self.init_ui() + self.check_login_status() + + def init_ui(self): + """Initialize the user interface""" + main_layout = QVBoxLayout() + + # Settings button + settings_layout = QHBoxLayout() + settings_layout.addStretch() + + self.settings_button = QPushButton("Settings") + self.settings_button.clicked.connect(self.open_settings) + settings_layout.addWidget(self.settings_button) + + main_layout.addLayout(settings_layout) + + # Tab widget + self.tab_widget = QTabWidget() + + # Search tab (simplified) + search_widget = QWidget() + search_layout = QVBoxLayout() + + search_row = QHBoxLayout() + search_row.addWidget(QLabel("Search:")) + self.search_input = QLineEdit() + self.search_input.setPlaceholderText("Enter search term") + search_row.addWidget(self.search_input) + + self.submit_button = QPushButton("Submit") + self.submit_button.setEnabled(False) + search_row.addWidget(self.submit_button) + + search_layout.addLayout(search_row) + + self.results_table = QTableWidget() + self.results_table.setColumnCount(6) + self.results_table.setHorizontalHeaderLabels([ + "External Code", "Label", "Title", "Method", "Resolution", "Source" + ]) + search_layout.addWidget(self.results_table) + + self.load_button = QPushButton("Load Selected") + self.load_button.setEnabled(False) + search_layout.addWidget(self.load_button) + + search_widget.setLayout(search_layout) + self.tab_widget.addTab(search_widget, "Search") + + main_layout.addWidget(self.tab_widget) + + # Status + self.status_label = QLabel("Not logged in") + self.status_label.setStyleSheet("color: #FF6B6B;") + main_layout.addWidget(self.status_label) + + self.setLayout(main_layout) + + def check_login_status(self): + if self.api_client.is_configured() and self.api_client.test_connection(): + self.status_label.setText("Logged in successfully") + self.status_label.setStyleSheet("color: #7FBA7A;") + self.submit_button.setEnabled(True) + else: + self.status_label.setText("Not logged in - click Settings") + self.status_label.setStyleSheet("color: #FF6B6B;") + self.submit_button.setEnabled(False) + + def open_settings(self): + dialog = SettingsDialog(self.api_client, self) + if dialog.exec() == QDialog.Accepted: + self.check_login_status() diff --git a/threedecision_maestro_dir/images/.DS_Store b/threedecision_maestro_dir/images/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T03(kp%!C z;GQKdfq&D!7QMo~x5U#~;fjflKDR2;9nLK&A}MnHiXtPoyt|#f ztl{lH4>kjz6t3L&^mLOI74`P^7V(x4LApDLipj{xh+Y>L6&Du+b00>4x%?l9RffD|Mrk@BrpuTQ!QA4-~66J0S zSMvg-cXdy2G`Yy?JNm#~Ol~^@LiGT&p>Rc9LQ3NQH*NZ_X)!Tr@!!*`NEf6#1TaLn z!WFT-uW8bg)j-%dz>OX4Jn#Pt-tQT0xT^zg>KClNDH???D4I#YTpaMR9pZR~q6=2`~cR;)X0-xxw z$OnA*6+7XsK)`edBIB82BoI^&g*XBh@jom91QK|OdWjdhZNSo|B%yj$DMKK-HOuDl zyF44I4s`(y+gR)5o5&#a#&g`FwB`SDd2`9cp54rV# z`SlTxnstlfh-jydyd`CDDj{tA(BRhFjLwbFR4=ph{}kngONmQJsHst_TI^est*}Ou z9=m3iJWhFcv1cwBNh%1*UzW!u5w4A}xf-0+y!``Obti#|an#JO>OG#V?dcZ0H2-fN<5shU&$qf`@7^z)o)dtR?d@4Kh%Qa~bn=WSWIwtv9aK0ZxQ zG&Q(93VEN-ceNVRb-~xu=t&H>*7H0W^rg6SXBEI#HAZH3M;k7b972}(1%fc?+RFt@ zDdvmyj!EeeIU9jTqwZ9xCoD|v5YTYJe7h}CWqyG)l_kRLbet2 zQ3wgAyWbE_%D#sWT9;{v_>dGpV4a&+sZ2py>>Qc|-RBA)uJQo1!QiHD3j8f{mG{W>$e=bzvk6@TqYzH7{Ptc$<#kyP^Rn{dtroX<_G9WlNOVoqrN5-~9;qp-h7 z9`V6-_z^tGd0{$mG*M={ZaT1MI)*c?e$dL9g2OARx;JcW|3OQ@{~iC8z<(w1UkUtI z0{@l3eKdIz8ZbRUX|4fda08UsE~AHaj3a|drJO=(LDfp@-hu1OUu-E|L6dD zbmN1M4hImT!ivv=++t$&czso(cfCLI|Ht<&CzC00{hQS>weH*Z^p5O9<9u2xvIzn; z8TjdGHYlJqo&gZ{hMVefxkI3vErkAWHEH6^`(KU?m6sd=x#roFZAbAe=q85G*H`uJ zPl4KQe_{YteN0pLGRTeQSxpvMy1qZt^!>xTQ=lpvK$Nr50pc2cb%jITDs4Y$`Zy%K*%af|LGw z^Br3@?c0?*w+~R3obfsga*f5JYu8Yyfp;PrBVLE5e_;f8T+9cgQOV;QtZDuceNsNI za%2;7FyHnk4fL5H>Gl^{=ozT)PCw7;rgQju&{P_eEXO_qHy#>s4)9Md=?N2k{N6ZU zL%mQv{?@cYp4A=mAnE;8V|g?g;s+vP0&Jsw2LxppWC;^l^{Ga{34u16$~*?uxImz) zl{D?rqA$?oe;~u#`1TKE*Nyi56%R8F_tY~f=~6M8K9P<;tQ#Xs)tFh}sh%c{ZlQ~Y zqgxKhzH^5|4m3veamReU@l^l0N^TM##nU+Y=J4CHpDSpL`TPmUEL-D}E-^c0AX4rb zo<_LjLakccfki~UpovFYA#I|XKf%&rh_uaDI|kYQv>5hWVSS40RBXb;4M76`B`vrrROs}7d1ZFQ)C~`*b@dolzN?jgGjZT>&|zG{BvVR zkh+F~aMY?Te$$M;gY0(P2l&wT3g95}mi(~PoEH#rrW$#)oI&nefp^5PWM(*qhPxg}5QmCmP znujXomFQg{1UDw2=Ywa<*GiIu%nm5cTTy_6Kt?og<7teaFprs2)#61wM5E`RNk-nb z$XZfY`>w`J;NZ@H^=3hGu;0PHb9n&SDTDqJ$5Nv$vvcyx^PEFb;(-JD$9-_c}1 z^U0p2zj#EG9w%y+9+!r5I;Tt8LS}141Yj`l1A=>K1|!``h)K5;%qcl(*%5S2hC$+) zX5uJ*<#S#%Pm%i4r<3vraoX+Cscj)1kTO6<2v4xLQg3CJ^IKh9HIh6;XjmOKGq2@o zH;xIQ;u^OTb{9@=A83B;{+_6(+P5dq*-RU3JYx{U(40p@0DF<Mg_akk*i z%$8cE?-&O!&;1=^CNz(eXtAf}@Dy1Gnv^js61=W=zJ`c1OXBF>I928_%k$#(md5d8 zKlXPtojD88<7do%HG3{hYkW0U4?Mh(*OUHimZT3Aq%QWmSVkvtT?iUuu3tVMOt-(u z&GXdC>qDUV+W?|cp2n9GGG+9Tb7%(M)wtB0&XPwE)9_aVq@iaaxh0#BM8rmGR>K;p zXs2sIzD4iAWTKt`DMZ;hS@7$PS-DCo!lPG5S&&E{|IQcJL;l2DJELof%Qa&j8su5^ zFEw(cU1b2H>MBjIJQ{%Q$Y?Y~PfV842TJC|H0MP2N#_c;L&-DRRGB~^x=iM}CdWJ- z)h!r(Kw;9`uFPFm+lSCpLQ7WXwnI|RdtSiG;9x1y4eLWJW=@0Jkfi)o=p>r?PWRew zI8X4BV*vgB9=9x7;xHH#DJ-;J56LDXzD+$FL?~mtY{z?hDb^%~OBY90OKzr21KlLr zcsQQ@>>}s|U>lav!MM$0q})wh206}Y#{6UUjpm%vp{v6|&Vx@c`Di$8vulia0u&ak z5)MN7JO~V^P-AIgXDRl>M;1?#LULi-B@{2VV8QEgQq7K6x13X7Z($&m%%~4!7sxdA zpllgCN0W_rl}%awO~8SM>bWRkTBtRTKRij6x`*=OWcntZvUB2mDYXNP(uCcYu?zKU zk8^{TV}Ge6mX_6XC75J+I~jFs6>ADl5H^&*O%=?0#ETcLln1vdpjY|v)d#uW%`SYe z+TU&F)Bj-BgEOdt4S5TOIsSq!y^8+(MFm9koT4Ne_kUOh_ z96!^DI-DO=RaP(Bj%CX%duXV@a3N0YtEa&G^5nsa46URW{iLMr-Y!PvxXBUQ*1Q;B zC9%3?TTz8A(|t)diT&Yq_@Nv*rt=L6YLtW#>`vE9K*6h+Zmv2I4S+@&C4k|gWBLUw_OZz;@m3bV&-mIB3 zW|Dch3a?rRep{}Zwe_I2>A{ABvPxPJ&{I?D>|gnG>1{7bCsOz?DKLq}ZNn-4{gTuu zJl+XqOp>^9Id1}gk~v_71OGsFAOF~KAQ3cOy#L9?`qjisLWJ#nUilHqg0utgi}JiR z&p&zdb`)?06a592om#w&Lhb3Mfd@Xrc@c;QRT2u(6OW&MtUwHBJhpV4T4~O*BRYxH zDnp6Zc#P6B%2(2nm30cX7SylEv-P_L=!dY7%6yqQfIUshA^(;7RPkFNNfSHU_g@}r;a z-i$d3WnO6RY;ZHm82(2bClN=MU^WnhN9(K|SgZUE?SlMn7GFZcM@&3o&!cpv8 znO=KHyce#hssC|OQb}B)?{X{@;H|PXxo5ZeGlk2~Ws-ZJkS5;pwW1fGrZa%qCGc-( zMo(W$k3!7U7;3Hhd5AW>*vE}c}`x{ zWINgQ<{%&c>;TK?bdHg=MGt)1L2@4Iq50Aiz#cuK>`bdVZf-x4&qi z;eo$&duMzLLj7y(nuoJ#rrL*Zu}<1$lH)_e3$YrQVV|@47~3k$aAnV+jz`i$-ta>E z>U8+%dh;Elh6CHIq8)X{suX+|+w=VlA-w3|k%|KI@ za==wvGu_r%m{~J}B03tjEj6YJzMV?730SDoGu^G&t`X(;ZNUYvFzp7y4-(^;R8Hwx z5N8qAp~vK;L|hlSM^ZC6#x+b=hZ`NcvE1*9Z8+So`NFZJ&DGLUMwJq(>x@{LvWlCS z4zE;@Pmw&cFQ%$`nwZW2)m;M5#`|Ul9MhR+Dj6{mwaNyg_X=$Nxq&egeyN(M=Qgy3uB2IDrtW1Vic3F;tEZke{ z<#>RS)3^|S-<(iTUd++*r!8wXu~*`j##8-|B$Lq{*PFSVCXm5_6jJjg7--=rGkv-RM_LPm>I`Z_a$+WIpU@ za131L2BFqn%k5It+D`g@VmNI)(Fm(i5NIA~6K#EIWa-1xgZMqPMl*^Z7M5JDU{0J$ zb_1^k<*>GM+)H~%%|_sAZp^bM(!mW{mF4}x z!=O9&0LzV?Xyn=0Brw<#vi$;YemM5{6!g!eV;!1L&`3mOZ$sfp;b+brnrdjp#q#{R z8%oP_b-0b#!fQ~<=pgQUvQCl4lDWo+8zp^fDfmF!Ze&^&8P&$`A@J3+7-Kj^88(_O zeDt_uMJb}dMS{zkNjxwg!JFS}&)xylCFru)9N*Yek6g}D z-N;g-%h_?uW>PN*)G`;xB$6vTg@(N=Mz1R*A2#CRnh#!VUwwW6QY4Z_VFY$MiD0BN zLvCN)$d=mbw|}OK{Zf8Gdj_nQy8D`FYzbbzgBq&r$rZ)urQ*Ef-{fuADM=3Pj+yuP z-aXJRo3wuXAyCydAX}QpY??%q*XuTp;14<_Zl7tc@&^ad8H@>-SuqV1zrW>pOLf{&bg;5aP8t~iKbvL-MauSH zK=QibyuwM{=H+9vs^*k`b}rImWGSQH-|;9NM60p4G(VYKE8!7U{Nd>|K45f5Gd?8! z$m~?uFfu+a{oU)nj8ce~g8;R{ol=AA(8DP>SoT)<`hXZdrg7xD9 zynL3ayNexD`!a13<^en=flTn|#^{_AY!?aitjd6xYF&fNNL9ooo3&&!2tIq0U4gK& zWB}uuF??U4Nz`xo!^=BclT9~qpD|JtQ{_dj}&^#|vHg7(*q86O1_O3T1{=?L0W{oU?&~&o!NQ_AiktI;oF( zgEq~^@^KTw3)w~u3wbt(pLLRN9>m+K)3kW&tKd8JcY}gO0#)YI4nnxe)9kL}8ACHp zyGd=yS*Do=!q66ISq3thn-kifVF-~M*!m`tHut4m~6f786GjCWJKRWl>+QTrby>CU{+&W)fd@&Z(~5&SBKZJ(~OO*#KC%APrqm z-XHPmRBA5yrqFZ}%&lew*N7daZsu;W1B%;!3xML)*uEEi(~|1YxI{ki2-ov8YjfXp z(LE`95mVkz_spI(w)B#=S?#=#KwNJ(h1~RPp%4;(UuIqToTKp9{z3%^*OM7n&C1=m zYuz@}pK}*93L2#W^6@f1`>78(Xr3#Q2%H#Qqucb-3G|P=>?uR;nnrm=kNUMQt*4qb z#@XwhJq{kO38FqIYRDc+Ds|6k4XoUl8;*EuXu9h^v(B-8A0gA6CvU7X#5TEj}(f#3(J?3KQ8+fg=n~5-GS+{VhfE#2lgidhI999MApd^%j zLpOb(Vtl(Y^^bQKigLT4IvsJ_qbf6*EgYlNHvEGN`>@B3od(WcozpXk680*&mdRCq zfr<_t$|%+(``NNf=bZQl)FRQocLrx%rEr#gM#(dWP0Plgad<9Rl)vFFzXeqhkPOGf0&Yv zmmZJIlzwR75IcC4^5Y3L@~Y(XC|Ln8W28&y!%r{OjvtpwK2&1E-2t{gP0TueH-4VF zkvWr6Ge6tV^KFN>(K>x*9#37J@g$)A<#MChFKiG3;dO`? z^6`CmN&qY~w#$$2N9I%|vFx}J;SO_{u7OEs8h>vOgCcT@hl8=q!7XA2ykm!lbhUdM z^%PkJHC;u1x_NNN0pMyvmC3IF?$|URFIwYc+MBK}Tkm}r+;J^2f$#o=gmNlnDDv^T zRF6^U(kyuq;uBTa`j&ZE-($4h66v9*gToG8jK!Q{fhH~?%c$3~$yzn!UdsH^Qb23O zdhyS!0<>}JGxpTelF2jthH87Z@1D;gZ;lj#!Hq7+L%{?>6SAv_K$EV<63dq|fb` z@3!f1^CyJ7E^IQqnmlH3E|hVK$o2s7q3uyrw3hD1Lqk-&1y{lFP0Z0rvsqRSB2M}&S zt*g5VM+jxNAsUy9tayLn01eVgRFFwPEflpB0OR!#&gMQ1*E*N&7X^)o4B*Zq;h!Q!vYHQcd+p&9!_mO?=Ej*DQNy{aj9UHuz4oX{Fo5jBo zM6vE6xJ%=qCQFECxXSykH6dGSjOA}+(ap;luVoDf<+)P8W>ei5ag>o2QNI=wVb=F${?n=NQA6lk!4uH^wyMK@X`!E!?^RX z-<`pfv%uUtFc<|^|2uInGvfW+2a|9FF%h5iuGzd9L+fm8VcDa&^w95K1;rTHPHb!s zF=%~O(M<0kw*rh5bI{s{#Z@Bxt}6V3Q6iPTZ!E%*ku%y2smV;|ByHc~GUHv*`AxAd zt+`PjpzaG#lS`Lf=+Jj}3)#GPe%LU{SzC29lv9(&AZJ#@shg+cf`bHyje0ra7uJi( z2jwq-r%CK8PQb(ld(hnjN_lM@TZ% zUu=gVom%u1;*#fxh%v0RRojq=VB8yCs&x_1U6QLv`CdizHMon)?b?&uDZPNkRM1+ybv0O?4Mpy8U_Ohdp$% zxFvI?d{U@qx{#H%$bLO>$b=)h6qbYutWAp{mO1j8lwT-UDZ8dIEtJhJI=cRLGOG0_ zcOcL?TF^1kt}QQ4)|glOsEA;j5d@!=FGb#lJO!oak?Vu)PkHz5`6>dO|`#O`cwD9QkVX*7Lc@w}` z%S|(qP*XsX5^6l>p0_pN_a+7kHVRF_oZoVhuUfv4)!eah0$37%?CKaYF{sexdP^b zYnrNmqev{FL0u)N9p>PE=dZ*-7g+9EO8eg>>wO}dE0a`<#j-%)R}??T9a=LM#D*8E zDE3%D6zATw>%W@s*PlNK5fkJ!KGUv!?X5zg6J#XO(%YtB%_^h+W&ItA+^VlwrHxhX zG3={(tgavEe6xq!IV#r*sDO0V`+70Px z@te?cH;?fo{L>1Qik380HnBu;3u4JMP{JC8*-ldW!G>y9B-XE6ziz%86noCZ>I#O zx8z%sKJGN!gEEGDKMKo8fj}n(PyE4K=`xR2pNukil)Pn=scF=$#bqMyv0b*znzb2f z?%U=Ptrxx0JL#{q;yiP3-A}_7C9)Sb;>-M%3f`)#xV4A7PlQ(-iDo!P6jNc%%wsgX zk&`MX7lSM@&W9KBll=>Q6M3u=9e5qDhU%|J^sLUhfJ=d>)Wuo9Ex%qg8lKa&;h%hL zdh^=y!o&%|`uhN9lM92t7<+hrXAJqR(W93`my#@z7!e{U8gC2x%*#IGv?g>5 zQbDjPd(DjK?l}i5UZQMlIh)saM5N8t<7A4WFnSa>s$~rh@ycm)X(#&KHq&pOBgi7V z$4&u3AcA%v3CSu_mlfpv0K*B?D(e#LdmcR}J$zAc3Vv9s{n3l8Z&gx5ERWphHoIpt z%wD?aJ1E|kn>tYu8tbezy)K9mVr6=V=fAT)g(t$)IuvhKR^IRzxhM?EY%=<>o#-$a zp6u>hm|xn_w<4hTDQF?tn(f-}G|cHoyS=_E7En+Vs%!XRshb|6ZD@SHL zM$z!RvIl}fk_wH^QZiATz3e?}eQ>1-7`b%Ho{61v9i=)NpKfz-m35Yqx||Y)%LHpm zDfi>N47T5wCzgr2_}AZ4l2F8sv{d*wPvPpP07mjjEMe)W}k=!mLI^? z>x6%}ed33fuf01)D48RvQI#}$b+T5LCd)3lI~8;P@+-GI+S7--Vnc7%`wBbMZ0_gf zvRZINyQ`#3dDQBknP|s^88#4-%yaHXIAFOPgDgedSke>{eF=QaAj9SHFq>9$b{=pc zs=GvZW@r=EgZ9-`IfvCN*ZnSUp)T|-EjkDtZ14-)*dpzLSkt{v^`#KlH}x68$^N45 z4|eR}q`b{_+SKf>q8wRA{hgE^`$A1IRVF- z5xUIM%qwTJMpgu_R1~pxYfeXul}ZDO=z}5k2Ms9zshiI}bDqXtOP3SDLNP;Bk%sA1 zKN6OpIvc6cTy3F}xiQOP!E_5oAh~W2gL#OW#!^GE!@YlB&uoUqob&^)I~9KCLH zwdGy3lXb$3L!9@}){}yBlMO{{JCML#isbI_x1i{tVq1w{n8!ZsOZDq1Iabrl95=*a zPot+`Nt=gNVN5sKD+Do|#F;xe&nz`XnC!?6iMACECvHl8=J8PKmW9#rLkiFma_}P> zYO+o{B>K0Q)+B$XwUPpg`@G}qATFW$xm17FQrDXAZCDA|v}40KeqnWtymsQZ+nzeP z7plVNrMwJ2Wiw0&;Ezb?WM8&El=pt@Hc-anC{Od8pbsoXw#Hjzy`oMEOR~m!Ic6IF zElC;oa&{b)PX?D0V3vZ(GOmICyWJA=LyDx%p{Ml@qf3PsZP}4ML|nR2>@w0b=yzSt zd4*Qpo%^(;#-7giE$4Ft?5i3$OkDgBf9KIRUb3V?HOk`FEa%^=l%Xy!wR7kKX#XSc z0B&$z6iYb90xL(J*SR(Lm(o9aB<=*?cu>q{x-~oA=i6uM`N1 zpxf8%Ki&Z!1oKYB2A;c9C~0v=A9GwiQBwtN^4j9VfEfT*;W8#B2gzo>Wm9uU&E_&G=gP25DUYDSlTfwkXSZNe z7L@OZbKN5`_12JXt<7&^2W?b9y$_E(r=|WDGP;n3;BR+Wkxv?O)Nc-1s|#7pySma> z*x2dM(NN+L`Bq2mhv28Oyn+4)IT@B|Cy!P?G&rMJYD=+MH&YQD6S^D6^!Ra(Y`X79 zB7WF=3GXd6sFYjrIF;&=sYs%kv4_?Av)|&TKf5PxV}94j(=Q8_`V3B@eJ|44Y)Pde z2K;WRAbjq?U*z5YX1{)-$A0>U^Uevimsf&C0}o}hOy>rC5J+TpZ&Mx(!l#$`IHn<> zAwtQ6-Bq_d^_snB%#T)#cP+b=8puF~51#bU9QV{dzFY^)+Wm@-=QH{`P9zNRL#GJr zDd|B_qdiw+gT)JhTK)JLiQ54$ob{&v?%{`hE~-#SotcGw62hA|Z8&_8AN_J3KlNxb zB}q(S9Rf|p@Ex-qt`7KAaE_xV`0LVF73@Wh-*J-uk3F0YomWr+GjFvNp)opcxrt}# zY_I6B@_I)vaw@*J6=IVN$;jX}Pm6d&`Ve*U5~a!`iMn)%m)cMr%$4tSM8n{I*Rf3d zg{NOk180+EESb@mnhCBM5!T8`iHW6Ux6U+-$ud4~{#U2uq3Yv3g+ATPK%Xf3)OoR> z%jFb5qbkh&Siwiqj6p3SV z)z^(jeZ2_KfsRRq+-&#luU;98rnWnEmm1{#J_N42{kGmQ<^6! zhpm5Z|4i|s@OGpdtZjWvNE3(ePB)*#NlC*}D7juT0>6Th+ui->u$&cHl;2MX@75b$ z)~#TSIeDG8uckYHmRwE5W3Om9D(1up8T5z8e|bJV?d9P$+%{}$0LN6jragFhmI>pG z8vav)50gSjgSal-O_!=77!a|?Upwxk{?If+%cjIGR|=1MtaknQ^B3ZWVf(U`nEcy< zlzaah8yzy~;m4>i*P;JBiguSWpYIZ)V?D$p(V%b-TvJD2*$) zXV)`~5~b9=?zVuPN6*ktGfP^JDrOcsa2k=_;}Rmp_$&sFW+o}ek7AEYs`+m)-Ytym z_XH7u#CI^<1okG<5ac<(H?)0#S83KZ%t4ojQ+1NNdlO{n{@ z$WZ(h;B4(o^6zMMiuP0sbYh_s_CELchff184IPk= zqg{1>V_1n8Ed%v;1=Cn>w7N}fPD4EB=IwTg&8hD*YEdnK*!R;T^zq;PGo z4zXy+$k%0o9iF*Q9SE>jmGdgdDb*7jg!zC&rT7+O3@`SygQv@#r#FT$(m*pZS)%6p zi+ZDS&t=xk42f#g)`e8uk846ugbk|y4zcK|A_4J!^naaw#PFI&_?r`B-?w zPF%Ml(DM5GDc?vozuIEwJBTkBHZR3F)^h4oDaRIlufjxQShxG1bDe!CsYvzbQ)|EuIqwJVN&)^q7c+_6-uWvlxvS-dj#>_J zb}a#cG>X%TY=hZ`Nd{15txtj(cUWvLC|MUCewl2XuSCuFYs}R2#1reEzk|WDhJLsO z2JcJ_ij0PI|B0%=Ew4KdmKLmRu60goaY3IpHU`J7;K*D)Z52PwAIMg;=e3|~;lYm9 zM2z~?tGatG#h>1Du4wVQi}NKg3HVJBl@Ilq8h@rt>W#|x3IOeB!wki({}XD zPm@K7U_IEZTp7Gy5nl+S!x;{x={Do|vif1MXvMC8=M_tkAE$>eRZLon1V^7t!1)jV zsmZ>Q`r8CeXm1UHqQ9)TFFz>;Nt_DPs23*tVJ@f<;*<2_5bU|!AUpBflp2_E{Y2>P z?wzSRn4IicKBr{`^ogyk1}cs^>8@S=oY_w;1cOurRxyR8e4@p#45?W zs294Lm=y0`p=MDYbWXjaG%;H|YXWOcjBqSO9ZRjbWuthSZOGV|{8n=@z+Dm6Yr9_c z3pJ>CFAf!-UZRrk{(VxG)rNi0UO1VZQf#qP9+vz@>i)!3Ie9Zu(@RtDJs(|MCT7Gt z(1kba>-g(iaVL+$Sfeby=G_+_L~*J3S5Gwrz|a!|PCdP@Wax;v!(U)q*0z$%CEV}h z5%40SviVCequWNC(#gLR4u13i(9v{=cDMcT@rw}-qXow!I`cIr*@&+p{qy#*=9p83r?i9kB!j;)p4~7n_ZXfPn4)N{{(K8i zM+pDRzQ|v8uDhuAVKlSXD(uZn!EyM_Qy2uR^NsbpVBpP)CitxeC9&lQ%EEiyp)rDQ zD|T5pOP(}o|1Ww}Y4p$)ZznV+WUnY{z@JtKa6`-gEQ|A(1@i+TuJ!bwVYgMs8ozPN zTb9@&&bTm%$07XeRZIT%jqyy2H)j8AjUln`cyN81!8A;O1&O0pEGCRtN1}3nlxbP! zCe%T#uy71^)^9mP#CTG}PrdDTq1E)3_Qa~`i_b#cl@~?!c8N}ZR#=+0RK^Oq%j+!i z53yTjP$)5J+OF^N`P-4gJh6{$*+bUaxDqJxa(1qs+Xa=Uq4J|oQ zdI!8~T2VP6yvjvgSlZJ(1iRqU{i;C|5@fM9I_;|v^Q)A7_;}9{TEDNWwenbhR-dY8 z?)c;e;XnJ2BRMhgd0^AK2#=xo?T~!i>&OBl;|DV***2Ds#bLH|yl|t==B?F?hLo!k zu+)s{Ijl)`_EP*`wX3U9BQQila*US~Pow z<517%|5f3#G1DqsHnm>*tSwfJ2Xt>TFc8Ac9=g~roYyCpsDcb0YNBa{z%7=?9@#VVn$0^3VbV8Gg;pS9kR}s!Bt$C*G=7T(q`k zQr_*V#R#CvpE)a%3kqJJ-ERrU09G)R!>xW%}py7`q1^IYlp?JqZ z9n^P>7nAu*O-A!!;xxh6+~z}GVGWCsjr*tptHqK(Dj1qCV>#aJfBxVR7l8a4<6Z|Q zc8jzLsBvg%D=E$emzxuplM}NoK@{^+1{n=Pl z%4UlSg-zr(ETG|Re$3=k83LqsKw^EwC{MF_S=^b$I^tv@9W(FrrfF+jsVi>lnZszQ z)lZIT7i@b1=O%LmFWO9QeZEo!Eq8DRpCIlstW#!_oJCF(SiwupNmqEr+o+m%){+7p zdoIC5!C>V{>Edxz_+PT~w)oJJh61ZIJhQJZ;J!Hbq;%D@y~&qnHr&|Gm@!|wR2ym` zsI$9u|F8vufc@$p%`=?cAoh!byJCA;=X+hnsdK>9o|;z0MTv5u7iXcHj_;`4JpsMY z8IwwdXc=<@`8@PaIi$x~NIr2a)_No&KC;7T^~@|)Yx#x287aow>E5Vpt>$HP&p1W| zhsOszZ+K=!2*npVU_5buftTx9o6+2#SYV!DWWv9deqvAdDL0(q4&cr51#f3PBkkiO z{L6g-L$GQzmd@hrJ*RgL5R`zdF*rxJC_YEvcAgH zU`&aMT)|WJsb772j@>v^%%U(_=mcGx3ez;&(Uw~54-joN^GdunsSO0zpM(C`p3mX4 zqYD|y$*Auk@mK1`Z(Xl&F^H3i#K>=is655-0IzPam=~eS`u$X$sVzQjNu?|X9pY(y z@z0kyjxu5{qJTSzU3so)RYNbI)vj*wwK*G`ys`7Q{c+Qh4v{y6Pm$rn2`Oc}vb5h_ z9gMN2{aa6B!vu4~m2W5EuiwK?U@&LZLf+&AT1csI8h=D%{^_?UJA{6nZ)i?vFC>(k zOEhzvR@NxPZ4!Mv$wShas;OZa7eCPu%`MMn)L9zS{i^@( zzY1&XYi{YcR(nAenb{u+HT3YnX)-?;($aga^C#{*SC`Emkjo2=JSm_!)+CF(_xSbC z{MN(ot@Nf)=5USWl2q21p(QUD6tjXe^x||tl>mLwcD4J4k4r0f)CnK)H_7)RE5@a` zqheVIBTpwhxkYq&;xx!;y>?B?WkBqKmCe)tsrZaotE=BeI|e)?5K(Q990Q)*0Uyzj za_a-G_>|Qi-pNr&W#YjR(gU)&{ZDv*tZZ0bKRc{i9wq1J>&oRf4=Ot>b zAA}d4PDqdGdR}i2Jd>vEXcMxEzZ#4x#C3WZ=|>(m`)t;LOJlhC7+?34xlXdr!?*q% zWKQSbh5zwA&$qy1!KZoYSYxzNZ&I@BB!ZDaaqgz8xeQz|*psX)KK(&Nm37gZ6gBLH+TvPx%ODnQoon2oR^clf|}JTK9=TjG02o_xCb zaEnNGPUJ#8ksMqpbbZ_4ZNevUeyLU_%@)Ylf$tr%e#a^y{tp#O3 z{}>L+lNIPcTkfdj^j1$W??!eMNKcP9Yt6#)kN^kB1NJQAw81p7dpo=U6oYMGd5j*C zL693GH(Uc?xfAAxr+iU4J*!)!vQfhMu5<~5DD91R{-1uy9(0a#&;7{DQ=%T{c^F${ zT)kN8G}JQeFO@pBZA9wx35-{|OjnP=&L~DRu$fl;5lyZIs^E`CMZ_Ak)5MxcsUn{V z^ij!H**6mYqG-}Y+vc=I{^wBL?x8DVgsue>eo~pVoppBp(${F+XY{`jKi%s+Jf&nC z?zo>no>ZcekaJl7jItB`z_Lp*x?s(=ko^rurQX$!F&-r)d@XU+aRGlU!0=);CM~3c zifaaQ)J1{?cqN(@8@~n(iP>|--;9s%=rFQTL%Xb@sLC1@i`A z;U8#@b}$b5Jcr3USf#D#EOyNqq=V68cNb~FygBHjU?_cfz+#TitPnE_dUC0S?62=D z+8~_1MZH^a?Mmlej=z=qMWsO4#mz`W=rwK#0R)?1q)(UCXVQ~I=H@c zv7%C+2q}I9ex(0%(C@X}*+$E+O&Hu`3a3)UqpcwpL^> z`-*HcW4asx5TyLi7*jIzgj1#Dtda0wG{fTDKwHl|yzS2A%l_7%C;XJG+dHC!4x2Rp zupb^;w^Gq>w^(7#n0v)S7rP|`GoG07JJ!$(J6qv|HKm))rInOF$3F5?p((*?GS&+I z6s2T#&{A^-CfY#kW4)K~#{lJA2AxUnroPt72ItBLz+J5<0?xzrhi0_bjMRhHS9!8A z1sPDzu7FE)(Ln1>pkp|A`R^@-2=k2@*u4J|YjK9dMOH7yBM3eoIG5hWkfZG{t9y84 z?_!v~Y0Wu(S3@J18(p|~wfeMyo`2PSE975StXVtCpD>x10f~Fn9~vUu*cO%iqC4v~ z+@f~A*Hha-7|npJ4kXE_j0yT72A%Ne4c|y#%TYZ~K9BkzJ%Mg62`#LLt|faVWoPHi zrv1VB`@Y9Snig!NrGwOZVUyV|q}Pc}dXVP4yk3@7%}%&EE(Hb)!`zZ|+$p_i4_0Dj z%?Mb%>RrI7u@UduJWZ(a4Z>T(UX=PurNUynfcD3Oj|*cKAJZBxKs9r2!HiMZF|&G~ zt%`aVbB?67FsTVUCB1J#3dz5$9mGWY?<+v`{{V~!Kh7EzDk4QHm4S4eth7+@*A_j( zQx1<|nb9YwKDp_$oLF)u-F@c8uy`Xqcj?DxdE*Tv_Mk7-lUNv(EnS-oL%i9w?f)WY|}-L_?LccAaj= z56R{Je>J3Eklh44+83i5BiB6s)L>*k;Pn2wPwq?Y8YAM@lI>-N6Wts>+K^kk9R-ll%qHDl2urtlSHO(qs@9@TU7#y} zpz(v#`QZ{%WzpyV)iYqeL_E4l;Iow5VfMYf~FcOsMN7GNVHS^&mJ{lfJ zLwNRonE1-ih(+Px{$cj_OspKk#^^kv|8we~dgId&A-3&v z**mPI{IF^t^^-68{DFGyML43$R)tnMLU*si%62c2MUYH`K*`VT+O;OzsZ*JS+j`S{ zyV_KPaYXnVaZ?-iuEbLu6`rfV&`Nu04Yo3;=|k})Q4`b_H#3d@xq?FfF=;S@4|a^o zh3L_UyUb$0F2|j7us_&QTO25(+S25IHBt1;aJ{!-cgHX(H<59-u_i;p{|wG9LylW4 z+9xy02=XrUYgacfI1gEG+wplngYm?z^zhi&tX=%#Xst6JkLDCTouUkV_21iKb$0*l zS>LL|G)rBz!U`!VqbpUXh3w;JD0Ti6d((tR!ixppnHh`?V@vO%%LioA@gu7xBJSRw z!G2@7oyrEdjv)lPD@z1$`X8Wg{c$=oOkB%DEP1Gz`m0NzkNcVrXVuVnu9-4S?6^Q)WmaZoRK$^mdC!(bE8Bnt80p{fl7EWgn8#3ZZ0%6ByeEmr zP0G$4P*SIY*91a*&n`R#%vp`{{2e$O1y4U^|4*AODE##eEQ8~K{uAgD4zv8Km-hLY z#_t92t?J;<@TgC|kL!lE2e;2e1WndT_{hyNl5M+uOr5L*g+1qdYrwo+WeX>MU{&E7 zxuYhchfP6XufW}>hRbZHe$`^0e`0n5YW_q$eU(&raS@UCKT{Z;&;G(OPb)DOQs+1z zE>2F0GK5cv6gVmGR~x|@S=q0lR70iIcof{`=lj@nDcE6+;M{7-8~4XQ%X35{YQga4 zZbRjkM#-6o50?SOSEbF9n%;2N#SIdPr(D;QmUUbEk^gsI9+U7M*pGxIY;^byy2p+x zw`b|EH?lPr+=pdkS(K z-{=*Petd%>M2tJQ17q!-x2pY?J#&~)y94L%-Ji&re=fZ9ay^@;+&zOV%u9)!wWU5s zGn1823>RE9BnJk=NBGVM6eX7Pwy4)#I(|`;ABm2%r?k!1PzYI%e1c_Vx!5(vbr?%6 z#1-R}I~WJdAoFH!)Ufh?!L7pm!mqWbps)x< zpH`Th%_FDTVM->*Gt?+!n|=n3SWC=JPT=vkQ7vt8-pz~&N(|Uc?U9nDLQ!?UGOc8t zW+oIRCr=uh-E}wE?ayY#eOgwI-WAsLQdK4|vT&p^*}7u}=E~zs+tLVlP49_={UD3z3pIlo$9NQrx83Djzo$CJ(XmuXK+$Mt{E!g)UK)d1E`i19N`BkYL zMd;}km4v5fEtR|y0Gt)aLheWHDq|-uNO0*wP-5VIUs|$j^%rj65i5IN$AYK?A}9Ck zuJ%)3_gzcL=+~C{B=!J-LX#0U1O^3{mCs8f1{vou!ew4bu$;u>>mLim!gwXN?9gmx zlyjf+M2fr*Tu`Xpk9G=hznu@N`TsuMLGbaIpEb~n?jr#|dGPGit|6#i<0IO#lXV#X zy?55FWcX$A33`g)FU*}5$CPXbj?Rsa5U=mwfcl79JfCw(ErMI_Z^*? z%V}EV_st49VkG@|I9fhm!1asF&=%V=-}5KwO*&&@QewNvhT0Vkc*P77l||?xT2LAK*;JJ%?RKSwOS>&tu56 z{0>j2+{<$b#~n6F+Svp5tge3QAybS`Bm*B{IUOkV<^d)gm{X5F9=MpK-yb)^3d(!u zzAMJq=9c5(TLokOfgrx4#4g&Vh~PLeZHSi_HOQdJS&V1{&f_GX`H##aYcNxR7n$ov z9oyopQtiZKAldtc@Jll|j%+ZKbxNn2z;vrh`6DRFP8j%u5))%!8}%YEW0Zm}K3QZ% z_D_UvY+9diEueh~UhC~XvoHcixdQaoH@*8DB(Lr1%=6n~R|3itkFQMliJSVpYG0si z8Ru0H)ah50M?#sHx$mv*UkU;dhBLp9%#SXjlKzzKDY#&r^n@~EW+iw3(1C7}?Y^n4 zS${@}Kzh$h6%a6mzT-bfs_O@+bGo(;G9FNoc|AmQUGs^&y@TEUcs6i6;ibPZ?Mwo! zaGLD4$$e*>{IV)IYmJ@sPZh_{9m6zR2M+rCTSPi(bv4%<;~0*Hc@!LT>O!K9Y~Yqp z+}c*p&xhw5-49`7lbUhg^}jgr*t!$^N2@UXqGflGYnA>Pokqg%Us}orM01J=dBl|U zBRf%Pggqk%{U(R!^VJ{Bj1W!~{ro<=_GE*{#q%4D_+S7LgCI%-h!*z?jR6lVR@?M@ z(tm+c_uXiMTfG0mkpUwn5tg&K=?_?k)5{wyap@y6U^V*r;AY^Ir@Xf&s8EYM25K=8 zVmFcVVttM9R+G2-w>)@w=EfA5q@r7kcRyN$HMMrIDnv=GrNFp@2xsO=lCgN-XirCj zZNo(RiPMQq3j{6?UcDqjcIq|$p*anpdQsi)~(?P7_&0;!i z-FKU2=gy{rPXO24dFyYWXD~s>a`}uNo-dx?19dD(%>PIVMgG4r+xT+h7 zb?6wBv6gt1|8Q+c%DQA=%a&dym%#7D&WMpyOvg*-{OI-t=2~}sjeA+u_^E)rV2=rf z&tHc9M+M|K#(#NS4k(;0o;a{w4(mN&(KH|_(Io=FoDX6&TyNi@}8*y)#01jl6C7dC3?_!-R``s!B2 zcU%QF-HPE(s5$?(1cID8OMOB(WX2!jH5A9r_O`BQ%k3rLp!k?qf7CExY6`#~+vV`Z zevvgr!W=?gyma&COMHO3n@B0K_lpPoGLvtP*PhzrK7JY$?B6$r|Iw`) z=J@#HCSBI`yP1Sux-%0n9?d*R;=e-5Xs;mJ#CN%htRfGocT_gdZ=|2w$$LUI;SuJF zKG(MQu+e?huLG|p^&k*-53i4M#hb{VW!-3=A6PIZ>#Vk=*hZ>px^Wc<7;R$S1$Hs) z)OG@FEQVf1!wRiu%lBBC(h4|~0^E4%+KdpcjP|9aju5e(Du5QJ5Wwnp-%EZSVqlc; z{3VSQstYkd)J7dV^9qA4(*HxR-Cue?0Q4$yCe#`k?OKf{hmOUNJQG+>FYwW5dFN8$ zZ_r}WA)jHaFz(@CdWRjC)WcxGL;C9XDN$zZ3VFeJ8F@ zckvQnuCT+%qYJEF_3L;dr&r}1IEBdv;^O&D1{Tm*JpIK&P5(!+$k&V72 zIQmU{w^Kb=DwE2H$^E(J z85eIr;*VwaX#{(xK^A=!_+td{>_NDHTg(hLXhceYI%x4}en}G4wrnF*T!Z>E>dux{ z`h7JZ@AwQ=KWZYmUDuUkrNY-aJlJ%`BAL((r%Us2E+*!wbA&{8#SEpw<%J;ua3`+*_^`4t~C>o*p9?|LOP@NQwiM7##05254hGAaQcw zwH+aOvoEJSMV6316FyN#L*jqO2hdx`s{H&nG=|xCpz;Hm*Z&Bfk=7*) zO2W)~$?RuhDhD)qn(pzCn|WBzYxl2HBV9*RDrUct-+JoHRj#2`A3g0_(wc23@_Z3d zeuu#wx8d?RE3Hv>Q(lPXah6h>3;J;IKCBU+4MwENH_H=VkXP?L~ItElIKwS#+ z>VsgQV-^&ON|vVY1h9lSxGdWB-+Xic_=rh{Zm^;^|2qp{^sKm)Voe~p=tWXeY>2G= zbYT${d0q8U7kopX55|>YR>CJh4hvz^U}bf%wq$G?gIb%WT+U#4azAUjy{UOmVlwml z%9HnWQhWMvqH(V6tCgwHM<5*$r;ZO$KqSNNMo5l6b*Y9~DqLg$;Sf-_8@ZZ!tdHS4 zwF8M#Q1HRjGk|%-f6=d(&}_E#Yt4pmzS(+P4M^4ZgQQHjpl!Q(76itwwl>=Et;-9>eFZXYw}%91e~itb>9HwfMKb#G;_N zNs2%3DjJ`dvr4=f7{7KYFB1J(o0Ji54OjbEqRoH(@@PQCqO9nTKDvoOnjD>aq~$BK zlP010pg$t@lI~j*7-&MC{z$XSFXF$p&aTKozE9Q=>7bqxA8e1(d9`893M!m`(qRI~ zw|*5QtPVIz21ZO9%#rTmgtIgN36HFt7%uO6-3#Lia-O250+R zhuM%qyMji`B@gU>%{J`$m#}2K3rLSr;5^WL@u03j(-Yb7wsBqv_vv%jI6*I%sJxcl zE}IBB&c$Aj>OMSZLZGYowUT;AyH9LFGOT5e-?-WT-25`M+aF+$!wVD`IvF83_`o9= zHV7~@Xa+9lKh3^Vo&(y=1 z70#oo!?OlkWtIK>y>;(J z``XmqNyia&aMG5DPE|6&CxKOe?19a~Llt*KLJF)UJ6r0c|FqRo{VI6Br^&EL^rBwV`BfHG{ygz3H^A2Qy8Jo22(!t#6Z3nj)X~wMkWPzy1T`a$wYRgvgMxesJ=zEIE04K|u`$oQ! zZHrYUiJ&`2vgd|v-RXYc+L^l;S9;=5jT^XZc1w$GLvp!X?4olMQGp-5EDR)|mI||p zZVg{XLk6<=%MRR-Uq}Eh1*y#|_+Mk*3kuaoNZ#hI+*G{S>9q99fO(Sm{mm}1ZnDdl zR%i~cz!n%iM>h!U(x=6J_$7-i6?X1Rvr&8~T^Ah?L1oqj*5VoNKt2W)3E z+YU}j%Ji_|z4ZdO3l8(4Pug$MN9J1@o$~JT9p+^4VTLgEb$^ zWXm*f^Q*>0XORU}0i-ms|5y#1)ECIL>JNgW7XbKT{kbhz_Q()KPh+(|W$n6JhO-M) zDM2_JFhE_gbnSr1;|4NjK-FgFFro$^0g^SE~~&V@)yaN!8r&9=d{ zIXk25k`dyCek`%(Atu&w=*f(c86{fw0m5`4X!fJqCZGcnG@uJ2kSfi^6M%W#fyL zz^ibEj)G#ZMArlFmft(4ae4)L)ldMGSig%SS0kdFT|^*io#O4ERJL=L`#Hf|Q(P6m zz9kwKJw^8>3oRrzTW$YzlW??o9|oWLJ#S>C#Rpi52Q}hEwM*x$`!s7KC>i}1Fjpb4 z62>e})CSaHBS8DT@Q(B0Pe83^fhtSqp8ri2Pu2_FV@3I%Sa-xL`h*AymCqMqf|QKL7|TWr3#okP?zeU%|~UAT@fwc*RurlD6%Ox!de54P%WSMCx>TF%Sd zu;0+PraLT#vk4>>JlP!WDmaMW(*yNR9KML6(~M7i{?AMlT#)ojloKR9=B}T zZ{vZQkE8PGcNT9?jaMl8Opiqf;~}9}Mu?4_&3#6HeMAyadI38RfRsE7W_^DvF8#{! zI~;~Tyg+}uLy9_np{ct457$2i#+N~(S7J-4*qi`!RXuPZ1;tNp^lqvgNOpDspg^HM zq$LBEvk)utZCfX>_xRKRuic`>md8$5eg5-j03SVV>gt^}=TBXzk;#zX6ZYu8b&& zknH1p4h25nh?>7CHzk)d;^Z1MsRNoTVELqN`|fVVu)8{w4S<8Y<0K9wHT08Lb<5tP z07~V5V$B7Bp*1%Ebq5y;#B3FzDz6XV&iE>07oE#h%_q|7R}veC1iBa-kSf5GXw}YH zfCIv7H@m1Krr(J-r@@%zFN{DWRA<6d6F>zF-ppcz{E{356f%cgC+y-EaGSuL%~$bZ zupEs^3}eN!58z0}(~~^W1FC%=^HN?tub@k41bgLt%^1<6JQi>NrFk_LC zk`7Ho9w2noYAdIcu)Tx0T#0Rp;su|wK{`EG&O~kV>>^#C0ALADVKUb+4iU5B1#AFI>*ylyZ|Q$vhd+B5O7F^U4k>7LMQ`=6)&6`i$2z+(v!(Kw z{8RIWJ6I-)?MV{Oij7K3HgY&#e>(hVZhA#zW=qqzNHA&J)^h%>T!ntio5GwIZRl~- zb@ug6U}IK?nPFhUZz~XDn-Ri&te0sIs`$yjc09R_NZykHf*m`wT@^7dKvcmQWy_RR zSKa(+9J zBSy@P<-3c=0g*5$@1~VP`>^KY_$nhN2G&C2ml?WbtX2Qjwp)#!I``4Qs~W$(hMY3% z=+cB$toWbQm#hnD9@a*RVFGLoBk%*Zbj#Oo`ojw0d1uMqKm5~p)WZuBfgZ%(WME6( zRCKoEi0Bc8UWHN18=6V4BH&4SbK7BZ9!2Y>0_>~$er_4o?QTq>EEC7EdLB2&o0sY zlcjjXH-nDcaL4UV_a)g|%@!KHh^{MfuhAPr<#fPG_MAwwnycIENbN$lfrQ;Z3r1q? z3}0{4K#%|c2L?HY-^2-&n9yTs=v5)bYTAOYx&L+>kV`d6-sJ9aVH0%*R*(FA!eA{5 zH;^Y&79*-8{p$Lho!?&PloGn|WjJ=%l=v>~+Y;MCd7lq30g$Ej#ncKlRAr(zWUr7P zWBWT43QVdI&CWHtr1k(g(GvGfTMqxmy5+}R_QEnxMz6utK{iH#UckM-W27_IUFk5` z>z5Ma-0_Z-lwhMRE2$~H^WxPuBJJ}NryoZ3=7yTy{c-Ud;bnVs(`NSJQxg~EC6BcJ zocqnUEQ)Un*iQZ0C>+|nJeNy0I$m4!UQF1MUcCSUG}ou&urH%eQPI!eazyc3CLt;qu zv}q#F=&;h(ZIEW9!vFlyddKA%!an$74K@st60m_K6J; zI&{acmC^0N@rRv&$MU@Ahqom9|Fz$jiiE9!stD`EVX}IU6`IX~Iu4Exowc`++PokE zY@rYw@%aig{^aL7DH3mS`;OfSV~FJDkN8iA<^#>BW19(Ahw?iQW}F@T3@}9uDZA{9 z6(vZnCp)xjkBAsv(|vbn1@d*Ci{0k6fXE1bX*46x`pPu zd__v-pFVUGy|UPy4Deqv^OHrygD$dq)Q0;r>hLjKfa|Sb!j!`@?ztrbFb&7wG!L*H z;5zwm<1KESa~#y@3F;}p_o@CeODU2=dLROg7PUP4*|ER*v?6-9p=~SgMhN{1KKU-$ z1zz2mPy)gkl9DItV=Ke&lMr@(RdJ3amZ8eUb~rk9pIq_dB%?HxvXBiHnnp3idAJ76$*Fql>1CkHW;+Pf%Alds3g=mVr>zf+yb7uB*4` z^8-NAi(rZZ_WqTNoS89lBu09R7a6xsZj%1Lm+#GJ6>$)tIl-_L`Q~q9mR$tSB{QS& zYZJgswK=i!y=9BIb~c})?Ev-zOWMZrdegOYcuXIYiYnBp2t9yY>sF{`*q_TY4aH@jR$J**IHLUrbjQj)rLQ!npE_m z?fVW>Ta+X1Z-jQvTIx$Gk|fVimr;A)oFv2z5fJ=YG=P&=0m;@$@Cp48AOd!vYKXT< zJqeBnvPB_%fbCmYzJRX(y@Die63n}RePk6_Bb)o~`BS_o&9I#{DzT{&0P>yBqPHh5 zujU3Wv{exs%`;cXjz*93SMff%&;^Ekq>}WTcSa_vsGv6Zz@)`|8eFk=Y{= zB}3U;gyQFXXKG1~!<#1urWFUp_?!KDBq9vkdL)hdi(WO&_E~@Gh9BY~Z@JN+GfN>VzxYO3) z_>jbrrUl@UF7(-%^V{@VcM26)rHKQ4=4V)8!6lKW_xzu>@MqW?aY z6@<$NU?D6h@UvfGq?>M_(=3cvLjM`ChH_|?Rs`O2Z9hCjw*V=NB_TE&vcB_%R`C7U z(#yts4tqT>m9Tk&Q_o>1;i|81~q6e@wyj*C45XEY> zmTn!~qElLkD%-tqUVK~SZmGfB@qr4=yD)1eRmkk*fYD*5ymQWvY||N0qts6GQyCo>PB@(|k&R>dZX%8KT#s|_}b zQv6>3It-rhh4V;;rH3ZiH`x2ktBQeda(9?A>)p1<0qhwo8OIwIfuvKWpV7+EwW&w9 zSv`$)dDu)#^XK25hpmz~?7QV9p_c(EB-}d|4Gu zr+4jcWj}iAMsj8q)<~-88&K&#z1#Z8%Q)0j2rK@!B*H{VR-NqMKgqyDkia=eyDO1t zI3rjpFRRvx;P89&&vlxq5w-gKJdwrA>jK0GpGAb*#TYX4-T zuCl}ymvWAooiQA$H4{~SW;}gTUV5xW+AYye^(Ky4lazPjZAhY3pin|5f!tcA&#^;X z2a2enLj zO_SI1&;Z`B-p`M`s88?9OOLT-p}qI}hmWL@*ZbBN&1P(tJueZtFFuU3(Izm0teD1Z zX3&Ynrld|OE7+Zn8}>HP%vb4;?-EG0c*I3FjJoun=mmJ}zMyo>ScUG^75>H`YKW4$FGiWxJSF^UHK1IRK$VzJ=2CG-X&6^_ia;^NRXDK z=Km5&V!!mwp%cH~YssQs3Y}(q$R4L-^5!yAEmB3}u5-BE;ato)Q(o&VSD(?t();VZ zHcTE!ZEU`>u(afsHUuB|c{q**k(^^&c3y*#)XB7aUYH^w!Pd%d&E?Sif?&_S$H+$< zvk(oIhu%_L8g(FX!vXnqkLor^>@IU3$2fs(;l-Q)tt4J9q`xz#KK;C-3Q{BJyX_zcWlVj&c3}8m^*WQLqiE!NP=DJ! zg)TT*4(!fEh#vWug7-vx@5LPEhXE1N`R+8UcaH+X`+Y*qE@SUHRdZ<}_cmO#A_`iZ zk_ih+BzrwR9iZXToy2;CTn#sS_2gw&uZZx*(t|*+=?}j1USB1MX{yW}&Ix<$7R}Gj zggk1TC1{%?6H@F=WL=~_&zrP9?i{ZR8OydFB^jzjdJ`N>;Pub}Ut9OT%sz_Oxo8K* z67?`e3O9Q}=5u(At6h=yGvq87ofz=p!?Fm2gILulO9SuE@?4F$ zXk3D_uiQcjV>WuMHubnQfB@5En=@E#<8IDr2b|MJrU>GFb4sWFdTP!E!9gaZW#c>K zixfNC-x-hjVkpXX-u0?}=CM|=yBj9PNJTtvCG6rl@+rgBq00K}Cj*x~FSQYfBfmb6 z-Y6FRb+1GDym{mAr+kz-7hII~25It|s^c6x3xGqrECnP8J-J-!0IcdXSWJNIkZNh1 z=o?^Y{}!Wp~Dxl-w2&()^&tM^IDtn-DxO`_6yR34May;K+s5(%R>- zPS7ze*^@7(Z`f4wEve<9bC%OYIfc%SLgU6oSlU<$n@4_;k@^6DM>(OyqGcr9*#DV&~-P z-T3Q&pD&T%ALF|N7@x?JanP`GUpcVPW_yloCEbtmcHq{Si2wg`^f?WffgKiN^I9Z@kInBT3rbWussI%ZJI zyD}E`urlV_3)0eRVmS4Dbhq)N!2=u1DrEuxyInsQ{|5rE!Xd8HF z{Y~X15E5sBka*fVpb`t^Lgbc{CDTT>k?EwqmDV(4U4NbI(lTJH^9p!b!tvKVmf&(T zVlh?3Nd)!I4fE}Ja~;N#6!kj>R9)^ZL_JyW2LVfHn?0ym+`K&SWC-v%|6pwR=_sSb zHK1djyLH5A-a)fGBlqi)Ybfp&y6^6{qZi9>GWw03_S1BFM{J2)SSg3B^J<02i(e{^YSgJg-*oQPF<^D$V zK8dzZ1yvpzuh}qxukMQM&{2CB8b_)*Of^xnzbwlL|5HP-y=j7ZVVA0GWsQ+Br=PB) z8Mzp7!0Vukbe*kqhP?JQI}DpXF9QRP<(`wB^s>uR&ZDS0igUlvDq=hC>A#S#YTPy~s3o3U}VKM2(*Pa-PfWsfx|0-Cj~OlySMUj4!2qDJJ@g%O=D&1Bqks zDC$oe;3}txzUfCB67Jw_Jo=D-6WRVP3MDEwG1jPD{=ST&xvXBNq?qnbBXU@!vLUv& z>8^agoZFX#7VuQ?oe3N8%173O;@K5&chYaTs7P9Wq`HHj$otYqNjfb_@e!}V)D6@9 zWF*b!)MiJI*OQJJ*+G#%2aa_?+@9i|VQk<)ZlRM>>m z)C12v))|AHiD{en+oWVr{ooqo(i2OPK64(W3i%nGvaaRyF0`ushe>Ijf@byjB;H$= z!A>Ca3W}XJ^DuF4qi&>Vd}CN{KT1MTd|kw%`7*t(GU*<2rJ4Y3NKKJq8?<}ud;YeE zW#H>43!2L7lNLpqXKp4_*50c%NqgdnH0@MAxE^sC4HpCpoIn1yKD%_mwnW)k@$0nQ zk=Gwc{w9EVe?SE6K;~h6%Q5nSSXaGIkJj!&W~ydgU-gu^JEJ8E%b`S{r=QG>`Qx3g z?>io$7X7Q6DndTwX0n;k}@1w{FBC6 z1B-eUqd@DjPnd(66tumoM)zpnPf?f85G?bgTF-2$;*&l5d25^SfeoZPGfJ(55XWyn zg`wSP85>!_XHH&tAg(HJ;=fJLkT0TKv{T#M*!o3Sf5sa7ru+#K!}i44iz1)T9Am6V zLCiYw?|977>`s|Tsi6s+ZXHN0g#ZS$wVoZCTPkc_{{StHB1YzFiWdT# zd2u2#cWW=kUuAHyLt{->K@NwswbyOxaKHL)Q1l;x0$yj`VwA;nM@#o$Y%*)q)8T+n z@|?N#XLd{b1$JublK{M$WnY{ zs=+g;lUeOM_V(Pj$;?jbWyZR(10^y{oZiGOp0iPE-XDkNl++1q9^0|W6yX4p%DU>! zVF~ay9}%H^agb4Rbirn=wfP2(HFS5M4}GcQFRD_m`IX~QR1MLYsdUvOud;t?Rnr$? zg|15EOpO1$n-IWUMbfn7?t4yZ&dzf}-iJFERo>=yi8_J%CY@jqh3QY z^W6lCh_}W%1A{+0FU(`0YM?XUx0b&jjkmSeRtTWm*pqoEWt&V9JKCMn(SyHB{csO% zqj4*Gg8996*bqB3y1)C4wih8$xk@4zKMDa!5%TVMSOiaf`@kXlTpg>}j(}1dk~`PC zC6LYX&X0%XGVSf0_R3vpBc%{F1zw%snsnkUea;%6qS-}}-W>ZcKWN>iiLBEtg(qah zx2h7FEPIrFHMZ0%1}UhS+p)f7N~&CkF^lva3RA2O*l2(GSZ907gM(^Xz0~@t4%5pL z^ZY{P?J_L$3R1xeod*_U&0_!Nez*fN7EZvtgfI@Aim;kXutX1)2k6I+d zf7#;O`f?{a{MT>mi?--I3&F=jh`EltDcHl`RkXXM=)5SjuQJF!oxYb3@}*4Sv_6s8 zIa<~{_6;pcyGfM7{t0`M(d7^ZCUL&<8 zH6_?}T?-4XT~TT@T4A#cy`Av^LMxYwZ!UMP;iOZE*QBGbytD5TOFC^-r}!V{8Ve>J z)oNV&Vkpv!pC2{hy;Z_du7jz~Sf{bTAq({E!V>BEwG3&7#5h;EjdnC1Vw)yX!eR#O zlB~eDF4%W!i};eA(3~jR*VH{q?b6=!#yX%w?D)YQ>9-?>Z6CAm=WB(GyVN(P-`3X^ z+0qVHDkBU!>{*PVALo_M7QO+l~=vbe*a6g6?aO3YZC3St<>&D$L$_ zP*OeseZc!6G3VVM24<`9-A%*x-c+6+#dB{4KtFYo2}=a}X(37MdOZEZfjr(y&!z-Q zf4%#PS)Oa%wdr;9jzA=p)-r9H*a+%Ub%9@pLHoSwWmzTd^+^l_bsyLa`b;-+df?}J z8G4s3=Pt&ajc~m&xlFWyThy+ZBo`r zf@#BeSG)6>?$XkCROxMlAZD_Z9O9O&25fo7R@5V(cD%nxFHDYnZd&IT>J@n2 zvvOFlB3(>^ef_edLBc_Qpc-o7iJeU#{=VNPx&}cTYj1wtoVzkEb&G!T$Uu)=_xA}w zXAaJ3Z04|n>*&g%2V#ezGvn830IdkKtezxd=VZDAK$FVC_=)C<8#9S7q5NpZ1JBu6 zMQVm&hv>2uH^NE*7eV%KZBc_2pqkUS*9#g=I_il65&sxTU-aACM37XO5A6Be(oYr> zuNzJkM0&wrKwmK$hH!gtN=C9?9OG18*|_|Vq~A5%b3c_}t+U++nfCAMXn(@i2WiwV zA8k;|KhGZ`S7HH#frBjc~zj~f%X4diCF3gXq#4Q}$GH~l_ zNH{h)twnbmw%6Va*X}h=4xfvX(1F-~kNxeE1}eR?e!IxS*>@06*n}ubEroTLG| z{guO4^QmdD3$O)@>1EZPDLJ=qV0V{6!nUf^c5a}x3td!ol-1U-J|LSht$& zoeY-zJ8dK8M_PhT>o@Ur-i(0KWOjsh=m}cMggIRVtC~m;w3pUlGh)G>#ctGXVa2~G|UDfn+ghsrnrS_+0`yd#r2-sTel0+Z?{G%zcq6I{m_|V zbUt0jCElPVoVL7u(rMk}lixG8*JY~Htv?jhc8LfK1Yi3~cNhFAS=}GnouUqK2ai;g z4>g?~W-dCOQo_Y8Ty|~aI_!Sv4tS7p+!h+TL=KF~sB3eui7B&avWbM#MwM;-MgSmo zd?ykBsB-$0L1}jocEo3-JNRmW_eC;5t8Y4OgD5E%#RF(C3+A6(HBR31ixIOch>Lr$ zk$w;Ej1HzG2R~m>>F^9#%Ot}^*im@#p41`Bo>`J+-1ZVb@IvfJ1$kez-DTK>u_*Rv z-<7z9esmPzoRB80-lml6H+_G78b8iDjH6^#M5DUG!iaF)R15 z)rlp~dnJc#;H%-hv$ONsPQ6_xOlTD~Jfxwn%>>AXicMs~1?xb3X$C0c?=9s6m|VrP zP1x5b!-CkVJp;rIA2NqwHPl`N2jYmGoS=rWeatf#m4R&e8ecP}LejT|)r6_HRIpW^ z4UgcdS7ZmYm2@8O$tygR&y*fH;vL!EY4q_P81<^hR3}n)|8rj5JEvEaYMPCC@+SzaeF3)*|OYixQ`kK8Q0mSTzxS9v>6O+=lRsG#a zCYC^MGtY}>>W!Q#ZctZO`=77VdSmAaCtt3>GPU2jChg*uB-?`YW(as1U{sw{*Qxz; z1DCP^cSQ`H%a)^TV;dkEnKTB1os&5FwL;HJbAsJ#0C20jw`s-av0WxojMWCs*&`rE z)brSBSfvLnwlk`;&uxda*8OfT_L|#_yrOsZPSklU_>gxZ{ktnq;x8=XXM=_um?#7q#RrGs%; zlCU*6V80MxHEn&!SO2Q(t83>qJ8gCne3cio$TofUG}hQp7J*oPRQ7p3(nHu>-P)nv zJO>r}1aH!)O63Cy4|J@+_5RDkO9`Fvpqd6O`Y(&f8znf!{^@`_Z@uA{F)LA3tJueB=bQf zTem*q_c{-BQ;`+cZ$F<3R;c3uGM>^Bk52FuuqcGNna6;jG(&m1b|FrJ_hWpiym?Ug zCy7N$7hjqOgWue@t*4&;Zum4>j?Nl5+bQ<2wuh1lWg^!?#@f1j&n z+R;qv6TdH0J!b0v@br~YQMO&%LzjSbN_VG#lyna{bVy4JC?TzM!_X;>^w1zN5;HW4 zQj(GaLn|TmUEa@nzaQ6vS z!iGnH&2H@`?Jw;c?EP03{8bA%GnymbL`|Ctyyrg%gEi!ag8+P zqd?@w-@Q8=+t>mRcUT;2n`JG9+fDX3xy^SnslA>c9kQ8hV03!7=}$B2{~|C&{XYq1 zYNr(}HGm&MH`nw8x<-0soR^n@{2+C6Bwm%j#-4MSL$?z;K)lh3xl8N`9>~E@vGA<> z-n#h~pc07QhJN~4cF-K)cGb<7fAZ5NT9R1z#q)ztOqSY@pVw#P^)B!p0N$ zwa>aX^^fuscaJ2u;JLgu_oE2{iuxJDoi0k=Qvjkj5T-!!V&SkH{!m)_t#1Zx>WQB; zBT!c1Ai=6;PBSQ>8I=8kS3>Qt)?jxyepbi~>es@kHoHq!suL&1nwm=0wv*_I`T8dy zIk4v}s{@2P2R8}t`1i}sUaSX|FJjnnmTGgm_zAgOFs~C-|7u-cX1Ma2K^-TI)s4Bb zPXeOEqbR({l0vPS+ptY~GDy<^QK?tqzo@jmy{f#Qc7&PKETY2{J2K}9aik3zguJPt zh)6%Z>&f|z?o(f_N!a&w!D_bB7N@u}=r z&Iu(=fl|$sf?5x>>;NU4B7O15&Or|g<8u6R#%wJq&aZt_m^d!l)!dXlb(Idc9u?)r znjQ%GF!zz>`x#RT4o+n$=0`9A@Wsd45lr#=wUd;V6@)yECf>NwX7=v8)8lMN`MzED zxKXUzS&qr+FsL&0sD$m;Q*U&$PTj?^FVE%XpM?ZdkgJf0{Xryz)`|iLU&rVt9hP8~ zzl4}_COn~n69WmIA%U&6*`z7SRvbJEt^xaIHb@^sp=hm;H#rP;a#e2`Y(igCi!&My z(Cm~o{&hn)qa8)=_yFtAFG8~f-Rh%W&w(Tmp^tBWl={S$*2&DeO)!FV;|ix}cLgI7 z)LYbs!ODv`GpIhe|CcrYo58yX0`(Ro0;~-5p+5fCyKL@J{HEso$nYz)Q`p zu>pCKPO579Q}sx2IH`#?m0Vd-VJwbGKuW5JQ;jA}?hGb#wHPOGpu!W4?lTKBoxo{FRK07GAoGY*9(i{$R6#n(L!K&BYtZdOpA+YRDlykUZ=Ie>hRZ!$IF-JE2LlI;E?I9y@CBvKSE z#(^IPbHL*9mvLaQJy7Jf81?0qC{-!03ueD0*y8JG-_GN2Mqpd)jf^i*lj?uwkx4nI zQSQ3kGLI_FBOhFr%S?2-J>9x3u#J}F7=GXU7E(*AZ9O|h_rh-z<*ZM>9UY*KA?$~? z=E^iRBnrTafPQPi4!n04uIp^Upgt*--!mAQ{ssCC;;&(NLbw_N?2O;VYm6IMZ~WN| zZ3gvt2K8utvwDc?J<-K&sDMl5>_bK`>m(6evr{*raEx{*bfMjZM~Q1(H*Yf4Y5MaR z!VLAgG6RV&&op_j7)Gosbm0}Q3-UsDVsUPn3t)bQ?b6Qd#{wL7(0|mKjO|St<+u7) zCJ|iZ^2>#eZi4gg>JUt4HQyuCqch@2Z5Z%*p9R)WKA(CV2MI%|89+|1(XC#QX66XN!}rpT_@@~Za8V|7n5HfFiZNPh7tC@zPz-KuLFAuKv>L0Ld8>^VnDL% zO5w(Su}Hr50fdA`3#&b6i<5dw3V&v_u^Z6w+DI{J>MPxUp*k1+ z*}@#OBcU+1IaA#Upe{or-KMg_g;oI=-OwC&|rS z8DlB#1%P`fbBY)ioS#hGO%_hT_bOS2#@>t{CO&jd2+GJ0{J|R*4wM-*4g=rf`PNyr zl)L8dCYOx&QfwI_D0f{ti?#!KA}h~ie0IwgsxsNy9T}qB*>~0((&eT#=_}&H$1!PyFf0T~LSt zj8X9q?16(Ds(>R|EGEt2$5LA)7KCTBMkUx~Q*nWBIpZ=`1N!~4Eaw{#2i z-Th-mM>C)-L9 zC&^E`6~vuanx=9Qlc77EC#`WFo&=Xgk+8K{b;D%dvUM`EZ&v+GPnN5lJom8Vs7kz1 z95pZfQY>->u7O@}Oz{mujqjT=pv_$tD^pUl@9^%>N~_Bt{q*RP_aT7)$GbmnZPh%g zUCU9_+ddL3$uij1DqPHtv%|05HrrUpky4grJ(6hy`$pLE#QupDrr$}ud$0zm`g?q} zmZf;g8}yrY-b8K~y;K-|8gOK8U1RTj3p<%^d{#1^bPQP_J@-3c+ZBRRPq>#t3IV@z zJs!QG-G&1CW(=TUEDsBEXf{SMfbm<{bCzP3FT&t~;l|!MGM?`|xXVd@^u1UT>B9>M zLK$V49}xy8h<&p7cdEn2Po$_il~6{NsYiNF1_tP4R1f6fvU8X%aOFluo{Kozgh|-9)h)k zqv9De>4_2?;S{Q~b_Qt$_T2VeZyHBs*#xoEZ{DJV)7(r~cq|KF-5SF*SmuV~3 zR?Irt%2U+rZ@PHJzTjthU|&^+O2Y`BPIc1nmgbWDy2lFNw5t+{TE@JRaq(;UPjmmr zVqDY(>fcj%z5S3M|4u>h&$h4Vjpk4jsTkT4wdD&2s^-+Px*x1jY#3z&$}V`sH$VPdunYVFC62;$ILW(4uaj8 zYAKLS&1VF*zw`6kroVVxW$9Hu!ZxR0rz>7rAnuqjXz&6*84nkx^J3@&wzq}4&9-@6 ze&G2PZhR@G*q`W)egL*xqKy~Vf23Z|+w^~2fKP>nFt6sugu(;^pP+TEK~J?cy+oZn zS*$DnI_)y;r=oBi^_KZGr-WOq4;iK!8=~;yVX%rBX?~O9@Esr{ZCsZ*^n034H)9T~#SxE@l9y%f*pmkAB`jU&90EdK};}>^$*qaa`sCV#t8x zdsy#3n(jlOa~&K!;QcP4mX;0Zfm?1#73FSp?9*l|o5%7@*zMR!U*}8?n`#Uj zZNx7j;&^N;SkQpD?(ZSAARq~&G`T&uII z?~n&?5i{#$2~rj?mu;@=h|gnApo0W;r4%Mc5N@{*S1WS_Av#%2@l}n^ai`Oo#0%*J z$)nPTiW?{5->Gtl#wE7^TheMK@eb|YK6b-R0>rgZvN{g|n5T*}Im>S}AO{Xsz+ z;$wbPLg%5O+Iah0#j9FFS3lz&;FQT<`6&Hdoz*Ld1E|4lfX2UG5BdJ!`*CP{4o(HA z-DenCt|qGE&?dV6Z5iXsIst36;2bf-2w!0!FIm?LZ@;&&av#E{AWklf6(Q-t4$wxi zWu)6jq@+2C{<8sKZMnV5U*{pP$|7eGMzHxdLN$<*XIB^fi97pB(tl0_?Y|g!kpTQP zm?V9&CYp*@5D!EoF~XvW#@_v)<-?32FpFmQDJ|6mcSEBA*h3Cn??7s#e_ajuSz^%Q)Ni6&fg2q8e`vaItzR zXHu4R^%@LeVF{XjAEmPz&<(n5Sbp?$g=o}t;l z)|j3Q%)r^m`nZw38({E6K5&Y4XTwwaqbpzEgw75Z1NHW2KRZi*qQF4Pj@_qI!7jmA z5MRbU5Q|f{UtI6&9w2%hg<}V>Vn(kIWAA;ejc&d$;@vR~BdDF;meLa!eNYMz&X} zCVLyiEoltlsc=e!b*S=p*uElPF%MvG0*LK#dVU&0Ds-~k6jAk9gO+1Agw(dU8Z;lt z$4IloD$)b_m*YDfZ+eu6vX|C(k1uLT&lww&SFa2*XFo1qZ2YKi*9!S-QS42z)(LVEnVsCc_eWFO6g`rqCtpwh>5@gqN>c+u7|LXReo6H4fg z(?BS25P?C}>uzuzd%!0V_+w3RP97=sU!E_F)@h)!DdmXj zqFw7hHHA5Q;CM#fS-7^{-JA2IfUI$P#QtUP+7kb9$55<;p3X!8rgc}9x5a`hTO}`l zdS<95aI0{}xXaHEjA_}{8PJa+D@Xzl9m<>1Q%n{)7St*w#%ht}6SJi08m5_JgfGq% zlSvNG2KJ@(jGWWE3?}6Z5}=SXOI%^_Tb>RHM?SMaO-<#BocDqsK6m174hg$((7=j! zZP+)@OuAMD`cJ~=>X{+6O5Ds{er^Wu&gUwxn1wfI;iRb3*XQ7OPgZ_tF>B&$COpy7 z%}9sh?`+Dd4S0Nv%k11~lg@B6X7vix6}`3BgK~65V028~ZA3BNe1>dV zaL1zqHbYd_gF4GP zR@b^Tps#w2QUtga{h|k6W$!>b~7R*u-Cz zOqQ*b#kz=Xh65D2D^LJ%g+cXcStd!>kDDm^IMV2^m`|)wZlXae$5t@ivc=s4Mo^fk zgn^vo6!SBe^3Q$_;`BoB?4Wh8wQM!f1+A_*C>`;k;8r!RGvIs+r;5}?+a zNc9Qf64HW#CNg}%&c6_YL_A-K%wKGb5_aANzca?gPj}S3wG$<^ zw|Uq&r3!>sjUlU*@tY0;jdrHbSE@8i9&bVN@h^nWSlUmOYx17|gmFoT&W3tUG$&aJ zX&3TQ7+0oG^LklFQ2(23c(IdSRmP>s57@zzR~(Ze)Hg*Jw=Z0ILIKAL4gY4p3rnG1 zN53WhA^@;?8>|~ZK$v4fb-X?{woLvUI`R~cu|}5sY6Q>}yP!~^l^KL4fQk?72ZmZ= z1k>XS| zep$eeCuo!c-UYvmAIE~T->`k1w+*{wNWKdR+THlO3U_rQ^v9_SzJ2kx*9yRJlnT&g zxD8Mq@a*tujN+=bn?>uCFUQ;P%;9adwxtE*S3vvUI!wrT-->_s1L-f$JVRbyKr%q!_ak2ihd;ihcs~zbl&3T1E%>bK+FK{+GZKKQTl`X77+s zB4kkHvxmQoY>Cbg;nI&JA5Obm9}jZ@@J4Z?_02QyPwo~}JFHUxGLu2QwBv}VaqP0^ znV&3tFR<2qouTxdUM*aS*!3`AR!aUyWGPo0pf;CiRZq!1IbWRvio~;80Mt}S(8J(< zDjB`h1;(*xgf0-v`#qikbvs~*^&1)s66^NoD1ffmdlUn7JuUOxhOY{NmkZH$;#zqt zWEK=IAt<8@_%&S0v}@1et3}Hq@$o$_ubeD)r^@%>i$pXAmHx}cg*avA$$KK+5FMUu zw1@ai_C9G6NGE{50W<+l(QA(<{f}fEifgTO@Lew6u5S~n1UMr^118b4be-?D(tjIc?;yUHYdx5UX>C$Y}0I5-_ zWl_EeC)GIIR9a4=sBUV@r^ce%HxrHm%TAm_QN_##W!3 zu4|B(rKnH<@?WZMOa`}YK#~^`5~^ME>v!P#o8LjZ2=GwZ+9#r$2so8`OCZyrF)Tpu zd(XZ`co^$sGGMb{lmjjMWYOqWJoq8mWAsnOuQO}){>y&&xkgL&i=~AJDPxqI%4BWP zjs5(oAW#m~aW&eBC|7x*PnWGp%lokm$9#20cTRWeK;Az&6?>uey@zL2F>l329<#`G zb!006EDw^4qbS(GM?SsiNEqi;Zz&5`v#t0Wq&n{AB16KPP)I4ACJsj<9A1p5zx_+} zw|AT_zDx3MRory(#(p* zkyS?>ML#e(Kk61JnZjct@166THS zi5Bgn8`kv(0A_qch)4v6B^Lou#)2{=y^At{@uZ!aqTnJ6@D~K@Q47R#PsQl(rQyAR zY8us{Q_8;C=YO`((-&Tf#4g!1lz!cuStA3AKtNS}pbwRQB624f2#H zwFj5u1}^Q*-!xn30_0Dy0roD?fk|0#btZ;u>RtkT0Xs_qdcb$({{t*UthCP((14^O zuKbGQ;j0dk&@s1pz>q6`8R9-jSd9N4jfiFvD2^LfL- z^n%T#;+!j9!f=9DaKw<+uEMJIQV&S?WldnuZ^w6Uu==qIK8YYkRh@d?W$49K6c{F3 zs0-P}xfAp|1jn8Gy_wrv3;f9sUuQi(vq(@7?J-*pl;RlE=RE^kQ@jl4PUpWSa zo+z#xKc~8x{8A~29iRhDlX0}Sji}|weBCpI3;zxxws})Z_Pqa+^>6R4J#T6OwtGTB z+EyjJpX%_#+*21nY4>5M25F%j;c{l^v2ZQmUI1noP#(-L2tL14L%nvr3Yu{z<#_-a z?Y^-$mZ7{C?L z(H5J>ldSO6`sTJ)(Q2XDxEKENlHNRZFR&ovpG0iz>s}0Z)){EmyIxamk&vs=*2WtR zSNn(#tNK}y^}n(39@LPrf0s?(T|M_XHn97^a-t)nz%}~JoR0FsBv59npn8ggySFQN z(4N$IRCD&HT-uR5ZZzpE|7 zE`M@7Lu!u1IL7C4`}5mCNdNLVYwE5K#(}f#O81;bLZo5F$MWjLbBgsn!kAFCpwKT~ zl1_a0_*X3p8hitpaNY=3K=3B5KthmG_@(a);ME&sCwmXSquB6K;U*n%v1b$nY2)^qEIht?wDR= zsN}6eT@Ll9nwhWg(KKJZJhsUrqJqy3A=n+DTUlfCV01v__MENL089GIu`Es}7C_$F z@5D>`iay0OnGAF>Hcq)e0Gej>w^!rsVY?XWxNREUAFBxSmd!_%xhmDEa6#igTc07| z|7Yt})z3~(DmU}i+ev1k$b|}ee)6y`789~=j?)`7;32ap-uBQJ^pN3lRcw*sav$9r zwBnUB#7pAhIn=!++oUKje-0&&3rtUd9EED@f(%DqDc?{c{cqOQ( zrO9*4$tl{IUvH!cXTqHXA*BYb8bhIHAX@vd(T<_u?R#tXk3&@RV{OBeEzgQP(;O)p zd>ENyqaf*sx#x8{wd@_WJknjlip)Ck;Mrt_XTwaMgo9F%H!*hR0sOv~7tke4H`?|N z-}Jc%3yXf)LozB?4I341fC#{ewuX`HCHZ?8&P$t269xEid3U#W8iSx?xUtV$E1+g@ z@zOgc104SYkQNFKYU=8=W@kr51La#hl?f~Z0le<-* zB}1@_1Xry)%%CC{0A6JKO4Qxl+u%p-e+Rz)4su3s zq{{);`oetj_-}8DN^Gyp0*@{+hBd7ebXPUGQ4A?8QzQRCJVROU*6s&}Eg}Cv?h}J8SQI-GcVL|BeqdN7Q`-MGRQjYUppfsEl z`X9jXB#dxE{E66WzH%nk+TQ{1H0+kP19bWut~(&YLwAln>#RYiJ*&} zyhxLIlnzN1hKQ{8SuW2DGl2S=#%ZE>kv=1bg^+p^dO zbE9p0KH9{yWEMEe`L(sfHAR>m6O`PUM2cAYJ1RVOds2+kux-`3TK1{|vDVn&_@dcU zRsOXe`M>733qX-azoPbLOR557rqfQB00eBal#ufS?0ZMgf`o`00GSqapC;z`*f!e? zEP4QLb7GWX>}2G%Io+g$Czwbg0LebJ#Y)sz&tJ%TQyf<~694TugsfBIgq)+SZRJLz zWfBH8MPne)R=mm3ijXf|3!18_!ENdI9W?*-xR+>&=o|_A#vQ=wa)ASb>Sdwv3d->+ zZmofkz^`FfL~Rep$9b_etOs9~{zz%Q2$XJjJYs7i%fUHP>GxMaIT%0Uos~zs`LR0# zNX+w{EMrDMFit1V-D)6=%*^j^?+TFmXZE(D9{hfAgK~)N@?`u+n)u&KXSLwnu08@z_i@)wY<~ zDZq)_b4G-K>HB#sxKX?pOCt}D=y7hDe4eVIHzpMfo~1ikxKuVT6M75$3!V>gh3fWz zLq0}}nmdOD$kwv5q2m2E} z7XKLkhbxM`t7i&SLmeDwq{-jcRddveO*=Qu-2JF;@^W15Kuf{q*d_?EnsMjwBL%QN zoc~ObFAZ=$HtS8)FeTga%%8Td2x*|Z=GU>wZ;4ul)&$SZ(0FrV>H^q(>(Q5zmW9*K zeLXf6Ng5|QI39Sw29Qf0KcZS0n%j#|k!X?x_FW2m9iR5I<=5v~yHN@Sbe{_}(e$)M zh<7ZL4>szG?{%?5&CtrlvuP%l5hc%Q0>gw0S+X z&W;th>TEh{CXhMo%!gM<<`O(tobatz;|-W_D1r3 zCgf~6+Za*Hv>5-~(Xoz+;Y`kOaxuRc>T^;n%^d27EX`z7$!y?ZJQ)^mG|!@wmvpku zm+^izhQ!-_Y^o~IUW>}W)o<_&0ccxGnMg}~7xB!jfiYWaqN@YG-N!pyQ#YCu%@*BK z-AGF4^niD{cGmZaHqSK-3HBYdj_+RHJl~K2S;uqXuz)a_F2HE(wE!l#0Q;*G&`E;d zG(nzlL`>V(N`;8~u?PbTGWufcCra4qC!g0 z9}~dW#OYfANJHXrRoi#fbv%*{p2-uZ^_?`|PTByWyH%WDm;0-lg$#dt&m-iALVt2U zjUn4>iQ;y^EYR$i(CwFqFVrsk9klj2G;J%)zDlv+;uBzU%>OgFe;7}-Fg+PRnm+SO zn5(z}b+drF;*m0us6eRt9(ypKL@0#Gzu2TCdU*RWIv!iy9A@Yn2D9zaIG&2<%`uL6 z+bn8U?2MdR8haHPCDrAu;l>Hg&r%<;uv2w0G!H=Y)fVojAv`QO7N%HSGDS~Kc(*&R z>OX9xSvqqm^TU#Ae`Un@ezcM|9IEy4_xPdtlN$*=+3MN0dgDt_Yq;H&pA9R`{pjQC zn`xifY@L+4HT5LW=5TYnn}22lM%{lT;}baQ+iE%g{0zDq?D=A_GWU3O_X(BGJmcb+ z5isRw4jA1L@IZivZb5>Qky2XF9a4hR0Sr_Ofy*Jju!?oKm=6_rr;6&o79MUai{*uL-zep+#5!3lSYh2t-C6Tpw`y1u-HItLYg{d< zn|aQlQX={j3x*rRZvZ;F$(9TUj)m=63hZ#g?p$mJ^%)4Vn~x&M_AXZ$rPE#lIjtT0 z-n7cp7IXQc2kY(!t}*ft|1-XTe6G50d?l|8Sra=a9k5C)scEepX>akBe%2M}Z9{%? z-|yC7MGa(zEZlSQib?@{7G6`DDd_%46MpYOS<7G~k+p0*rt!vJGp=@mnItV`=@Gef2$p+8 zF{~L;?*yJ5gB;ikY6QlZeAUE#wN1+}Vl3tKShViQ@mMr7nDfa0@L;*!wqdnv10Yi= z>#t;8cFF8Gy}0|U+~51L&J1vu0Ge=~13bF$#t1L;Mr@7>_AkfbI6yuF)td?$2An4j znpZ-h+UNX;Dj=KrWX~z00hB2hOO+?xfEhk<@uNPejHL<2zG3#^D{*e#yOc}uc+>ru z`@KoynZ$N6!Lqo4`+M1rHV!W!a({b2225)AcaR0V zjU2h;oP4SpTU$pCK!sPx5wl0(LH{NPrBX)A)|MhdLarMYKEB|M^NcGTqIKjv0sZY2 z90IdFyIb|EbALD-$t|Fj>MP|k3?BpQUhx|I9~S^%sYyKlioN6gl~f%uOth#t9;XgA z#;bU|cM!><&pi}-GZ3%E@%UNKm=flOp|_2|+1VB+cN*KKK`i}re&dpgk!nqCH*16#HT}8yCg-i6=#|Jb77z5b{!KR= z0_>7=;Gv4(4cA4l0!hT>uTY!V^FGrt#LARFj~ND|U)2J5aw~vGr>#BurV|wU0>F+; z>}oxJ(TXE2BHptMA7SX`b^GB5Y4!UPwLh~;tYM)ut*_6YLE&!TWnc%;-mS&}#up3} z4v$S*m+@r;?SdWGT)LxHUdyK3^fsb1zLeZoT@NKh8X0HJV{{}_Ka9gqf$`AfoI!_g z$4S2~i->!|JHvuj&~Kuc9IvlM|Mq$fgYE2^($`oca?sDY%NNl|HhJSku>NDv`V7iY z-BkK-cE-y}m>TfFd~k8EfxDPNQ6BB27q_FIbJq1On2}!49kve!%0BN>9$WO_8&;;e zM?TtS5Kw7Y_m;@EO#DiEo;i>}Zo7Qn9Iv}DCRmrnR?Dy$LYi)erbJUi{`#F|?^Weh z#lZ!|f$2O)y>`Znm3O9Ra@>_|u2XnYex`p$sj0W4jniKUg(eJatVgD&FZWLj<9_Cm z#2FO-c)@MHkg4%^cDTTZ&At;>;m+oJI``V02vRF2^{`wRIeKM2JL>DHKaQ{a8Kzy0 zDbnJ(Q^7UH^N-ul0JH=E78?{0+5sRyB=8jr-UcU*@COlN?v!$LOG<~60l5`St$=Ej_0|a)A#!LZ_H;!E z{K|!b$DM0j63@{AQTt;ZlP1lVWtd9152{r@WPqSDbfbU`)Mx|fZgaA`@Xj0(n*C1L zC(WcO*WW@@`N*2eUyK74yKlY$%PQ#4e@^nep_r2~&VH{N3QN@f!I#xiT*mp9CT!U_ zJ(bR|{q?!BnEg31fy8V>Nxm0DtU_G>g$BCn>Q6i2(4MWHihyn1FU3LJTv+F=vxSNN z_pF{V)0xk%b$2L=innE89_K`hKYrZRv91Op2oVOx@nUYa)A^>2Yhm~@S)pDg=8M2n5HMooG%D8EL|k)BEwkLw|Iv1%dZgoPgfo#yhz<{nmLZSK~J@GQD50RJKH1WO77QHb$ zAlH84PlZ$%D+^t9`bRj+{EEwNO{L!iS%<9eeg}oA<*m>Jyd6wq1ipz5^=fNwqQbxC<6s)e_A*`DT<0_sDfr)PdomF9E|j6*$PZg1`=Uwky#Zha_D?KUHc!HeGD z#kG2<-U77dno0u3#!}JzlgV#r*KK*e9g8-id%pK5q<=4Mr}C$TW!Q%ImNKBSkFr-m zF8q!~EivBi-<-(ZDg}w%KS%nKP8tB%WXbC9;@z=!UXezV3CjdlE_c?L8zv72w9z)9 zYpnoV`LgTwx7xtGk!c1+-iq<#R;c44vdaLo&laEW}|9+QDj~FKfY+hRo_a{ zTOfptXSj!HNgBr{+nQQf$L^}wy($Ug9kGu%Ecry)lU zvmRRGu3l3$DM8YQ&h8tnuX_(Impi93mXO{{ulc|tnaD`6Iy$aMw3)kuL6gFXhOP6%|8dHd*9#>{73Yk znoX%Q@t>NyD{iA|=UbF1%cN;vOQFkyA2#3I`%L_4zz91xJoUx+CYoqJeE2n(C*aqC z0;rD)?vLJyl_#Nj(fw-+O@;veUPA9R`dRNH|1DXbD;FZ|9H&WFmO-#6op}cW3dMz7 zpFRR^`_aWt?XEXKTmKkm&p-p+D+Co}GM^1Mxss}>LacGuokdmpX~K&~$d@F8!7APT zYc}tH*Do8#)ZEgBOK>FTX{8G%W9iI(sd9|(BV1S0&n%uG54&^O%0YqxybydvbE|5= zCrcV_J9a@$1M_a~s6fH2O25(JN+LtzkL;C%7Dt61+2drLpN<4+`wS7?M=7x()T8p% z`(A}N(mg?TRJfb+q@A|HOKMrr24@I5vd({DW;9Tk`lpW=y`}ezLy$r#3tPhn2lT5G zBSSr2rdha}a~5?_x^4wY7WwqHd(O28_Fb*gj!G??r}`$;u#Ms#!iM2t5ue&(lPbR$ zgSQ#uj9&NM3n*WLGs=iBi915SDW>)nM`N;6mUNmMH2P4PyVQFWB4_iifqe00FOZ4@+ zukNRbU)^Da(z)OHb~l$WB&F@~cQLNwbS!V|M_;y|fEZ$W!d|ZhX;*gOoP2qd7KRVG zK6>PRNeKyL4==`i<*_j$A2a$p8jup6l(HQ0dRp~Zg2>P+Zw6>Ji~E91bXc6IIAgY7 zpxICJDq-mKC|$D1+xPu;$=olghtPMQ7E&D?+Nh#K$ zs_J`qEkLX5!0b|nL#w+(hETP6y%{L(agZVNJs#Y=6U-x@?cIe{yhTy(C6wO%Ua^7` zC*aD-qc;Uh#@vk|M)2m#pNvD6W)=P;`X(=eg=e4djKWj^xe)bG!L3wVhUIs`Y^=Id z)Heno=M<-eanYS7XX;5xlM)x*>#m89D0ZJ7Icq@SZvb&{K(1{;{h5;cZ$P)M8#ysZ zhrb>mpWB{mQlsr4k)ai7vErsMp1ghfk6m;e-$X@+0I7(QBL3P9nAVQg%-}` zuZ@sjR#cD4j{!2#&&&p0DTsNX`KH!BTj)L)2kDLg^~9M7=pmE&$SgOT87*MVO2(~95|Giu=cwqhjfQ+sN{cdWju;#QCKF`~Cd}FWay1^E9nY zcBUK;bG4_R|D8udK==L|Ir;Eoi~y63oD`(PTOU9kgPUjtsggS?pioioiCzsz5y5(j zs7hEsWhdfi8n8)F5s-rh#F}8;i%eB)(ZwoPyB`ORA*y`OhjN~<*M(Sz%Vdh6t@q_< zA49n>o}J4WZJYRT!u*W3XF=8l%xB!7K0zhRL7}H0XyKzlA#BqrWD0ibcFGSvXsjNy z$4sYe{)oz?KUEE}cV<45$5mh5=YhxaDO*(Y*^0D*(e~mGHYlbn^&%|UFW6sYvRPlg{Z5vMkDnDCdHc5ayP>C+(z^3RLlo6- zeM!H|THdr%eCTUjvcLWtPf}S?{mSdp{WEECs?aAVv}&+S%w64uw@(4Xu(nLxF%0gY znWPk?o1~oI0&|I`C>chu;e^TL@vr7`Ulch3f}y`2n>c0|blg6G~=>bP=u# zt6Sjh{`HucwHN6X$P0TgJm;+*bByi`%5-MZ6^~;aef&)_I9krcw?}=2=QTu%)3y${ z`OuNx!sjmW9taZ>BEzc(J?PIH=EA#bBI|jx(F634*yeKN(NZ_p(pp4^#2(pW{4Z$a zRPEwV%mNhaK;Vv$@WxNO-L&ABgVCy2seTq;6r)F;kQXx&ESm#c!8EwVX8FO< zZcJ&@7Q`~E@gHj$u=sYh&zfHukWwNU(tRA<&BmK_FL=|Um%hVrTdeGn`?@|2Mt?sl zkmk_!H7tX5&eD$gUJ(w*n=j}Wy`6eX#6lO*9v4||`!n8NM4{nq~Seu(5U{IdZSP!Uve zXGbrySmE5sNVDkO=xd6H(2JL#D$e9GUW1-*r9I`5YjL0M0z!%0WFZ@?qhBRFN~{0pjF3@~Tl*wiYE@$(dOzt!=5}qg2$j~3Mjo@#Mm=1@Oi493f8BpR zhOn2y%CZ7O13qm*V(?|Hv`H)j1Q=E>28K6(1s;e$j?*G|p)^1^&EEQLQ{2$IGm3uv znc|^r-UD-ybCCPz^Y3x3o0PwJ)LbI8k(=JisCaX#bW>K1bMct;&i$7Hr?(+N?*zdOA0&qq&df6)KQ}(C%@MyEI9cQ_oby6Xv1x4*O#3ubaJol z`%HG$&w$Vy@bX(;K=HGJX(p?lc!}rn7fIytqrTNWk&g=Y0?T>>>dtS7N_rt?;a+xK z7Br^$G$>9Dty6y^r$4%;_2NA;+ZjXM?CbFksE^aO=l!=46J0R<8@g-r9@HD_cQX7# zb(t&szAnJQYXb*=?XVHhesrLgOdJ$UxcBg>KTs^w%ok$Tybq2+RE&I!_GmmEcqEME zxT1~rmibTF%ccq1Vxj0+#?i`|>ZocG%*XIH{?C4tLZBBG1cI8Xagj;wuai8v5eF>j zdJzp)bOfxx-i{^5U{5ScHlAl3m@dOS$02;w+;JSZ>O4-pgO)Koj(e0|QNC88D__2L zDwUGIlsnP+ByQGUy+LWkn`t*fx#_5vzS=2GoX$ZeuFU6F3YnR@-w@=H#S5`kh{ zQOm&@t$PX1{VH2yR9{J8G|XiZf8HYjLFilRI}6OY^u7{22|3J**ALcadzr?B^vPZ3 zY{LP|ftG*0J>q;pjri)r9KKs}{d-;qh{DH7P6@msyR8O*`$xZC4Y=n7HBOQTl>w3X z0c{)ceD!!YJ|V+uaJ7Eo)MnzM7zy_olRdJ1~KO?Oa-4*fY~uIN-!Khp%`Y8+5e z;KZT8-dYU_kh$&kIUZ}M#egZ|t4MAU9usmc=w71cOp9j2lSL5i0=dTalGhabBg91IUymRCcmH&9n6lRv3-AfDHh>h8@A zt;3_pj_=IVm7GQ*H9v`dfXbTQKi=aYZF$~I@&xJ2ZM3Suz#^<+;y-hqdfw@if~#lg z&C@EmDRvc1>;LJ(+q1}lnaX5sgGb@Ct)#~CO?L4X`wf2gFQxkBt-viN?w?%d^3|>{ z^eAcu-gc_T#}9GP)@Cmey_?a)s{IwTa3sAKUtY$feH!cn@JoTq17fxUOQwBw2zlo@ zCVUD%@Y)b|CI4LAyoSEux|YVV^lE+cQ}AIn@FJnZmY!z!gA03*Gw7esZvj%()%NbB zue(K{qg3W22{f5dF$>h0VYGK*Aofh(X*<=GK>3}04bI6>J65#!?X?xiggx=HDH28$ zRjfnbsM@lFINgNM*sT&n_h@J~Xk)-*V_=E4x#);MT0f9ga z*MfLr1Uw3_B~$YQh01wPyxF4YB3n?pRfx{tFeU(*mL%uUQmnCXFe^X`oMmsNCPVK0xeqgXrZ-J5X(dLO1 zBisyd`SQM;%}z36hRe>$P6^Xa@x5t@fK6ndpFSx5)wO95{bxp;9d9L@4M^%(jTsOy>sR~=j^lhK4-2~lnZrHgcI1--oy^we1I^^gy}bYwV%D=>M6)=Rdf84a)?COc1Kuoz1XY^?Ys3R+B$Y%`VD9vrgOgBuL)T(#B{SvDg)P5SSIgdX zwO=Qysg6u)sn+!u>6;YdP||26go*;kX+>{=6&1O3w5oLOpsGEuwj?K9PR62YlL#i9 ziz)ew9!qg}EJ;}nTa!PQq%Q z4Bh;g8FYgp%x)%*zv>A*8EY%lD}jx9t_7Tur|!0L!kW zv6&#&11=H)4}!G5F@Jf06OJg;C3x=xRkNn7oP=6ZxvgX1P#N%!9gJ=ok-zFBJ$g=6 zEC?WqF&Fu_LI(quIUVnieg$<@jiQ~+4riZ$n)rokS?^Jis<_5>?SzV4!Niu6`ZdLm zCLdpj_Jx&5_Nn{%3y0fqvy+&f@s#s8#(D*)$J-MpZ_y(fH+Q_Ae+9r-)#g8^WRWgb^iI@K&XeqGC|2u z#(R8J)IY)$V-2yH`I8ayShpa>*e>y?tf>D3cjmKC-_gO;oQB_N>7%rs*1dL85K=i@ zb@PMQgGnCt)jKQ=1EReZ2vi8%MA|I+ZPAL@Z6wpiY|?MECeLn5AF1^;FabL&d+_6S z?09($uevWiBxJy>?93_oBQPHJ@MY@^#c&OYybYfmvAt9?Js3lTs-aDheQ)SQwdTu z2+Gf{lu?{gy5&&f_$SDEIg*L}TyoL6fu2i)L)nxDFO|ubyrWq9Yn+|U{Qn%pqjZcW zU`5q*Fx(i@O=jkZlbVlq=4*Es3}x;Xyg5>YMW^JG?y_9Ml@Boz)Tbq0Z1~9DZu&ai zvVdS~s$w3W+0487dyCNiy>OAZOWg9ysPgF&$`JKazZNQAX!Pg zS!Sh##mpbn(5}Ke`C`+kXa)G@Y}4X%IGzk-cG7GTLRwRKm$4wj%48$FF^}5jX94X1 z0Yg>YIgO_q`wEmk!~W-lf*bu@nBjN9b@hD03uc<{XL_QC>NTtqYL-;UM!mH~isvG% z4<)rVl$a=nIkge%4oYt0r%)UrpJ7QQ7h^62TS3W*O>wmKT1&Ly$RmVebFAzq?wJ~m zykMCzQQ3{!AC{Ric!cb4*y1xgxx;28;BRH2+GJl_diF#mp4iO^C5Y)-Qb60%#VN*$ z9n~gJN!aQX@a$(AbqkbCDYFclgomC}#_wZxr!MMOVY?#jGoPc{>nt!h?Cd9dZ)0Ev;8vFg-xn^As=s66LBZ z=Uruf)?^qEz$Kx+{n)9I9S>hrrJ8sRKlM{Cbb4^yft9z6p1fNI<^~lqSLz}c z)(Y0*eu`vU^i@nkxfXVvJi&T1GT#UPMqCP$1Zg)4JNB;&!;hjx5yV-)P0(?uHDsAP zEkduDJczh));0n zf7FU$WS{2lY7=*Syd+w~Fz>O{r(-l$^h-eQrrsmsK@pvAAHAQyw}Y|i>iE8X9jOBC z4B-cb*L$6Gv~doH474kf2O?Fenm?jv^jD-&SM2{7>lmApf5W zj1P6!Y8npru%gumu2(ETva*E>`3CnPet5Q_;TJjV(SOmf7+Y~**QQt-H+NAi(-}wRWFD(Vem!7c3*^`wh@7al&q96Bz zhhUZ!ecu@todN+_0k^1{IAN^$>Gmz861QU(@~^y8j<`rHpGxfFyHWETmh-V;)%0|g zRVz@8SwztV*Ft0WkEX{HNz^PC5yqMC?YYTB3e}|s5>}x#;#v&RZ}JKv%9tbkCQb3n zhf{u8N#|vUS;h}ZsoTK*>-*9zazXqS!OqylONy`jls6_}nyT{&B5$ z?-wH#4g+1cOQ^1_%Ug#~MB%t*y`0I`o`o}e(m^#hlgR1q?m~tvE^4l~i+C$(?G(jZ zLwT&e@w5nim;PtZZQ+Vt`*T}RBn{$8qtOGBFCI5JyVY8k#^-@1zK0(@MLKz7EIR7Y zys(1Qo**}RxRD!ZVA}0O8~gBAwa;GuTNjeBM*ssL|C#pAZ%g&~>3eUBD>w|V74`M^ zQPPxM1Ky4r%#wF+pV&f*OSqnsB=3%}#@T`h+s*C91CmpLYJx16<3&(mikDJpigETO zQuQ@{vH{amKsLu%MD>9#O4jBi8h)$Ka72ey*-HdxCO+*HT7Uc)PL-4B9ekGahK*a$ z*;m|njzd8}9HPs*Zei7w^o~hrdB@gJjYevQV^D;&Pg-`2G*NGKSJ4!^PooYe=29pn zzaC169BQ9d4=uG>8Y6AJ@?w?x*smsHWVWJeUs5hhgxPp>>$3R2ya4N3jJlCKOqxVg zeG+J>X*5s$uD!UWa3q7XNvbP7N3IS{HXBfwrk%MG>Nh{%$iyP;nPZK^@$M+sgMoa7jR3~4qn{VlLl1#6V_^+ zGv@*ZFUPfMdkKs$b4R0n;#?~sc4AF>eD6^S=Kr%U)5w*j;%>&R5ym0@T)#)4P%X*T zAYi|i_siYj*Ga$d*jcCqhI5mESKEwHgVOM&hIkyi3$AYUy89?`<9V>$44S{59*)E0 zj`)^|>^^DN@d+8dJV~k7u=YE}k=QeYKNsR+yTpZ{wjh{DH1Qx})P>v}MWr0SoDkWi znS$so=xSDfG}u$Fg-An(2#LGze^v{;^=4nOe2Sa5@gKwlV^s^(V^RRXvz#VcGU9Ra zKW5%p2pDE03l~=6fC`QcDeukfiQ-ow+c#BDDK2p!^Or!Uvnr`^Zr2;sSGe(PuB%B3 zH6y$3qZd2CJZ4cYyc{A};MIwwmHPe=Tv*C0>VE>@3spuPO1-F=c=&efQwoloPv^p# zDY}p=oAwBu|C6w91i=4`B*Za66hUGlW&d=g<`C7~Hort>!}l~)8yU$l^@Z?18FTgz zvpHL2bFEqnym9QO#I#zR`_@JCiEX)$M=A~DxAj#tlOD2E=}`Gr@F%4kGFjyqCeQm` z>1>Z{rim~9a!$$T4f|TDZN$wa>##MSPfh*Butyda&xXvoVa<>~A^&8mSMuyfQ800z zQS+xz`WBvM<3p@0*EI@#9&Pj79Tx4(zC{F-N8Fnf?fieGG9<410ghj)Jl4ar5h&>FSgxILm= zl?)ucfiGelSB5-x?Nco_EdT!8SsdW=!a<@-H(*(}{9Tf!tkq8wR;iTl$ey}?;rLSq z7C_x5Mkw#Ex9(LEQ&S;!*9^$6mOCM_4qoWLQRVZntLuZ5?*x&0?NWFzgTwaWVwI5G zEkegFW;6^ljGaEd479pSZ2rf3Z%$QSi69E!>+!(tq0Veyb8WV`&(U*p>|PpO_tc>| z2bgjDUHUcYY>Y0-7+_5W8rAVgmB~;c6|l9Yirlzn4nOSItMI4BkYd?aQ%`ClW~SA^ zkFZc0)|u>Yk8O2Ism^4nz#in?Fhb`?pQko_3n>`TlO0c)=b0I0Y<2X*tBWCgtYjWm z>*gLvy;WcF2>}~hE{{?0^Ha%HQma;pD;YD@%CYIH(?ab3Nj~~^Q3CWd*p&o?)_Azb z8mYP|?D7$pPsPJPg+q^1Y{;0fyODw=qYTRaA~xfdhmv=oZ+@T_0AHC!{qcZGFWk9- zG(T+P$ z>_}jSa@PshjMQx_YN$_lZW-jyg5V>8&K%G`!9u3_h2?uIE?p zUFZpZMS$>#JmUL9%iPCI6+K&*SVIQy)}9$6JZkzC(8BS;4X;i#`K(E@&h+~VFaX5lgcWE#7Rr`3`A@9ucZQ)`3JigP%v%H?lr&A@Tn^t_7j@th5=gG2@ znrb+2xKw0eRr5F6tzS-)1XpiZHe>Yga4&Hc-xz#I+V{^)u9E;9w}*jG2k+@ypm2ir zL<$Hgw1#96yhV2B=4cc@CTHahf`7ef8F$Q2zFiNQ!|@Ryf~I6mk4D#ay+_HrSP;k0 zyzBCSEoF(ZChPv;t+9Re0&TS=P1z|+1iwR}uMVv~pxp~_sW02Tx^t2ZJQ^RfOl&T( z=_Hh(WF(74KBC)N@l&m|O9Uw8)%^%!k?j4>RnZrLwlhGF8_jRD6v_M{ode~>`-^J> zc0%gAy~aQq9A>U#NRTT}_4I~w7*qE4l@9d8c#Cngm5pU6T9$74#pJMx%M9OzXoN81 zSrlBIT_nm0UDRKFG|3kb@xn?0BgL2*meTjb^rEw#-eaV?==(La`Sv^xj|=?iM)=@K=psChIK9C{lNSD_ zvPiafPBymU;=i0w3~!bgF+C{dlq+^9G$QSn!~~g%wh{p2$F^TqIPlx8GuG z_ESNQQ^($D(Ej0NB-C-|WC6eTRto)_$U9xe`^n^qnfRB-nZKB0ths;UT3mQN4SHfZ zyyggz0JsVU_$`NDIu>$0Fw6ern+>urLricTrSVVMkMFj8Sp;YkhI}9(qM)+a$B+t9RmRg&+pGDV z$(Z@FwAzAn;fPF1WYNNEr8Y5#q)1!hu>f0PO-j|C$9rk6H5+=>>5mJz!P(*VQ$xu! z(w2zEGPbUUcF%)(FJte3MY+TtJXq8DEgKGIh>l0n({kpXq%N%eATQ(1yOf;kdSO=D zOiGVLuEMh^SmtI;XK-paitz1Z*^?IsMG@%Gvi_pGs6fpb)JG6{@T+AuhmA0k=oMBY&=YvN0S9Q8s^yyOH)fkN}WKTHa88@=XTAibYi1IyOSR z9fCCl-9@aFY^el2)LmG0PL#Nj87nT5Qj#U9v@s`7NlaWP?Q4>0SUwqdQUn>H3^Pog zp_==wvWVfv&0Zi;_bN?T4Rb}OAKSa;G@h%c7HXc$q4VK;7H3$T2Bu?!YVXdXc0a{S9(&__4HzDwpc~{c zvsm)3guq{(f_4tEGAQiY9dLT+t1OWWafv!$@|b#JW%v?&5t%R0j+G%ZeHoY#aiTV9 zz#3t-J=I7l=FB3K!`9UCfTJ{Xn~856dp`~O${J&xk{L7rI_+a^tJuDCdxe?WGnv^M zLEH^8vwJS@b3d27dZLreUIfSD%SdK;bG#!XY24I!chK?1B$@UhD^-V7v=iY4(_O_E z0Rg_2mg%I3LEG0VMwHrcu18TCt(mUWIP%imL+!t7`K-WI8Lf-218IhvSC4jg2jgnHfi2a zMx04!(GY-mt`QR=fG`q#pu0Tp$hyYHLd)8KA^D~#_k)BF9bw-u@#l*_n zWs_p41z8h+IyXW_3-R0sEw3!eixIMdzDf@n#(4YR=>z=Cub|930j?Ero11J3xBp#= zE$lm$KKlzq-k|N3Y(nsb%-LSl3NRsFWB#{9*n=CRVG8lx1pf z!?+7h*W|&b&qmnEuK0@1Hr8o8Es{Oay?r}Ndt&(g)>H1@ zil;0sTV9SylrQAP=E#3zo&~u=F!#sJkRMoiDBG$4miEX znzh%B6m6lPm4vD@@Eb^Ig(H-2jyo4PdSE6I7Mv36_*UM{Voj{d7moGJ#n` zO!~;e|C6%X%lW^{pB29E3A~^!XjWZ`>~rHZYld}Nes#+?vcKmsg#SzXX-U@2a*El2 zDsPUCL$N2Es7~cz_K7j3X9eb-L0y8b!NcJ47CmoxQR8}4B*`~H!REy)#f)s-jY_|9 zanBQqF#}?#G>*KI&${@_1`pS0=eL!E516g40}2@X*at^E&C}%uG-}c_ObD-All2OE z;^uKU66IUnOb`)By`cOoly{NkBD#$GHD+&8Fg{r%{9yCeA5d^x*Nd1jE|+zR>=36s z>w9M~czsCbGh0Gbh^$w6ezFn`l_&ZuRNJH7FaGU{1b;EJE7U zP`w0$%D0Ai)@4CyH#Y5H2P5B4{KNhBYqG16O^hT>j6V1I%+kh~ip z;9#kBQhCof2pfUpV2~HFa?QrF0Yq&t(5@I*CP>Zs!LQvGhF?{(bm*}k232Qj46Bh1 z2)k(B#rZMlH2HX7D|;w~-;dIz%=d1gN)3RMw3>TbTspV=Sq);JHGv2>kvFfp2ovT*Ag^WSQU{quW0^}S@T z=4HlWlX!UU4kCWDSJUL+U9#Mb#NC&VS%HrL8IuCa@Gfwld7SfRvXbw$OMRuApGwVY+ ziJ`%C1xECrNC)`7cq@g{2>KNn5gW{!)k6~pm%`thf9fjP*lYa$B<|?LG0{k954Lr^ zo+>tR-lU&zPi`5<)FAD{He4 zkv^~@?3wtkl-jF#v=hPC&OB6$2&Z?46hhF{_-T!iiDD#I7<##=K z$#&kV`+5w@gcI!$2|na)AmnHuBMI&JBM9r-r!!$A&3@Gx)GKX(j>pW*4d!P+qLnBD z74a)mOiE*D@zP3IC5<@#bzq@{wqvZ^uqZ9y;?k6@0l-caWA6}tSj0gG_P}_`;Upg_`;@ePMa1R7Bi2@AdK!UTf?|fd_a^F2EnlHHQdn;8MU)aHy9{cQcO?PS{?E1d z3M`fF_2TM(%{(6|D&btRe9%U295v(dry|yC4sI^ddLo!OnGO5S4(NotqVuC)_K6r3 z+cw2n39lUP7^4Z&Kr)>7rmfU&Zd_mBb@0ZeflMujwBonvLnJ=%rEo*a@0-g|S_l2N zjCgs7V><69I`CP+7V^mapBZ|jE7y%yQz(Y^(UpKS>j^7oiUW-;Q9WhBW`52%LoB6Z z69EaJZA0>Y;sHgMhGpXBHNT4fk6~WC&<|Bwk8Zy7H75MNM5uhs)YNfYa#QAdifh4L zY03Z|4-*aAYGXsNy*^`-dS|M05DrvMm+|Y{*e`1erE5`2F6Zj@e2;XkPbFv;*kjqW zo7TL&l68ecf@r`$jKGUbgDlACsKdpIWMYYI?G)iC2v9z6Cd?cC@$ zGbl@*FXU!Yz2xqKha}#PJrp*H4nBI+y0STs&R9H9 zS-4T%%aoL?Z8;9rcpMksmG?uW)_X=UR?)eopI!8Yi8fIv4sqsr`o2ftmo!6kMc7W~ zv-rYT>H+7(X3RFgUtoZmX(#tB6!W+i$Wz#Q0MHWe| z&s4FOsvvg+h3Uu`%CTk*Jf+c#JI~HgeWR9=k-oQQMHqnXwDoLj9gNRnTZ@4~*|Rh*Vfkv(nv!K8nicgcQ9d9m~hS~`-7VKU+w ziH!?nihq&^ zS0IS!Bx^zZL?kmN|K5EqO#lynOf!WR*aS&}JW+*3JVoVy*g^uTZs)r#e@h6gPZ&Zn zfK0MhP8R08sBpbvYHwD@qg??0Jx!1=OH|r#WE-sGL5AC`0cBWasvadElAZ-``HBCc zsT+L!ne&0|kB}1n0~zf{k)6zrL(Eb!+82pu)yf-FdKwo@%s7g~j0l#35?0{!iPEcJ zk6N>n5FeSpp@Z->E~O3D6?)5GPY{GgU!MGs-sglxQBmh(Yj+Wc&PU46ya}(_wv&}U z*bgaUARf?y;@^5lujac_Gi1!fk*dg-ecUv}#r-+e+~A_;IZ{vOHpOw5U_^XqMUprf z%i--zTu2xEL*6x^hKp@vXPLYnLgO%1>ftYC_%hStroLYFB=wFBunahI-(;VsbKlXl z8AF;GrLon32zNP5aQRl@R$kS9qQ=Azwdt=y!_3^d5K4@Ae&dm5zn`f*>|UV)I5rsm zhae~%^-AYZJMOU|twrtka*VCQsP1fG5Vz-|8`kvG-(JpYfecXHrJ z14$cPcXj!dp1TOheds+*n`J?~SjPk_aE z!MWVaXS~lx`)Hx(=&@8z(!dt_qiUSfNrI#pQ<_ke zdq|um>fKex9~}+n)Q~JN@fB|gtBKkjUFo)8`gu7fam~Y5Z*16WT!w?A=!fsjoSOre zt5SoDPT91E%{O0cx2F_nt5$MtY51;auh>2DFfQ?xjOCu;wd)bCz%Uqu=BVK)H)BX^ znlZh(_8QLNpH=_kvCxMZG@-^5fDOW{3uM5LDB?hMU@WU&gEaez&}OIcZm%W%j?XYv z0Nffj_yfu%`Vhtcz*OV;#pai^IamO>fSCQNzdVjzIa+RL$Y?2iF<%MSbaDJ?dn=2r?i)pMZ z38yAzDt=%aVNs14t|YA9Q&|!0nC0?C`#roEo*Xk2!XZ|Lhe+18OD{p(b@1yI&1!wy z*~}%AmV%XS54t4C>sBgEE9%l<+ry^~vs}@0S{q1SY~JL~3?l^H^p+DD+o>=P-{(|X zn*OjqxUr|n=j`G0GaM?WbpEQOYm^v?WIo$f3_LfQXMT1W5)HTNyLJhbvky__N%nRr z;PqY`+!jNMbM1H##C?*B zY6kN52F&mQ=X4D9y{|DwWfiG~Nz^B+i;8{ofogM9dl?I6&&!n`5yN*LFN8xCmk$LC zI*AG->q?qjyp^xJ7*aDvMBWXX?G}9$@8gIYBteX7X*qIbdCjwECY4r-^NjW?04g`d zLNW}Uy`}YwYZpDX2yrFR(`ubiw7qG9bw8^Ew;FEa0>wIY96m4Z(K2ho75Q;W(~P3M zl;ERCI>DM7DN57VuTzetV@{mCm!{=*Ne1C2FjmEEsOQ1YmP-fD&Z}l#lHUtq zphUD5KVv!g?Y1BxavL_Ya(S`hv(#`zt0w`#*weD(bA^1uEFcttdWs3{5~Bjq!|P_z z-I(9}g(oMW>Hnqs-t^$?rM(pjVX>KSb$|+C9OFQiBMO(6UYPOwi?d7^S`H8Bz4+<* z#6n2Zw0!auxPL&8fAnwa`U=q9i!fZLfC?j~@vu~p&9+sS5V#Uz<{4M7;9j(04Hh~U z4{DtxTISKb3?sX2*CvNV&hJ=hCNxBY}NUyP7}`8`q#w8_PpY|HHa>MRgZ(-&NM`$VbyhZqzmbztiRTrhFpKI~&g2 z(IA2C(^O$*mOxi)MrnJ(jO)z`0bOD_$2{<@5Qx-(>MvVsM$DeSfcni=!I3a(m+7semk?pH1UjKI~B>zpe|JS?j${(QODZGlBSww-{5OzMk z_ZR$RhMyW}Z)riqAGRU+t&=|=q4HwB1tLUXr{(Vrpa&0TeyFUU|8>W$N4b0T(u-g4lSpnyYNy`bbUj9TqshK$o-5-%g*c$g-b6A(y#v*@9 zCC6}I(v-iCc{+Kgj&QE?kQmgcb%kr9L@l7~U2ObH{4KF&VM(nf{$s6i#`(qai+$^C7%7D?aA!@W#j=8IyJ_jsZ&(lCd!}@{1M_A` zE-bFv^l_NBq~JYARZbv2c~M_4MqsSvpZ zvaEC8RpJ|lKD#>H?L6QQ$`@RL#f;BH^mrbiku;yT^lKV;2KF3mdJXcbLBDt%qU%Oz zqNcua-Lb6Qu6bPUplud4UFmt$Zy+=Mv45^>1Z8zn>(?#vry$b*KKF3J6ppeQ4-NI5 z1j^TKqbZbi)4nhRd`@c z9L_J_bsrZ80P&LkAOkvHrVFvuK|5a~RH=ZdO{Kk6r=Icz^{qWbEl5!<9V_S~%6+T& zYQ`@(_WDei-nm(ODmvCedT+nx#l4t?+o4DKGkz90KVenXSOTOLRBl?)8(# zsn=s%4}A_Q7u-pubyq3UeaW88Bb_@YYFkgvXR>c<*T~7k5`n06LJS#EBsz2klk{s7 zCk{=WgUZy``+Y3Bv=w%ia(1n8@KXGMSq)dKV6vE&94E5Rz)C>~yEcbd=O$*gritXi zHrxbgtFMd})(@)}vB>-G5waq%ZOF@m+!JaDF24sSKmU2Geu@B9OW)>>E|DnpNl@!( zd#JA-6xCcm!XFx5=%Z4kw;^i#z6aSaDq3v3l5^3arq7j>rs1AD?P%s)pzea5&51=C z8r}IJOQs`I-HJWYSAgLy`kdU5bRC;3+D-fX_%8yXO*Qib6P(f6Wl!QTqwm;+@lOah zGQttW(Xi&S)XvEaYKJ0!-CMTJ{jeEL#H%Bc)7uOCZDiw~@^2H}q(l>;)S)XXZxy10 z$jsx(GEJt?M}wa&T)Up${zkz#6cY%}c>i9^Y=!pnk|L~9s+pio7uwmt7<9uD;mQVH z7gdak`OwN%vN+mz!MSs4m z_y^@Tv0ac(s-}yE@QNVJEmvg6!XS@Ho1^`0L2l7J;eb)jcw)RkV&A9>UbJj;07JFS zkAa&t?3YVvttIMP8_rBf`Z;tP!>?oF?~SBQt6B)_{4Aj-&Wp>ugP}<&!qQV^f7-5J zdL9uC%fdb}c=un-$oCZQ0~%ev&j`Xjd#z9!H7$#yV;7g9Rh{XY?=apSH-a@2j*xXy zMLemheBJBaM>Z^y;lYpaANP;?`b)Q+HwI<~HmA_qN}!=Pp)$^Ra{WThz=@*v?7DG| z5+Y}(x#U`Wbf5+1D_o0|qJ9VMtrvM5fzYW;gF}2Tl;viw!Xn`t@{rZlVnk#iumy=8s36Y}vvw_>_ktx60od=xJRa`kd-E*MFd; zo*AHB5+y=3eXK6>9PhhX7E9+23j-FOnyWJz=fjU|!#@Sl##7 z$^1qnLV-xW;$B7+(B1-?FtVus2DScC&eD7e#pLG6oso9(AiN*qA zHGj=_yC-zZ=LxYG7>;*H%;#mtP5@Y|Anr5H_)7A66C&!=bO?v^&58dfnG&FPH?|L* z9`@wNe4)x$YWzC3oJX4j1*wEXc-eB)xT+s%Pt!9DpES&4Bn z+D-F!kCLn1oO4jWxd9UPhDapPO%8$bQ^y+N%KLMotNX{SawTczboLpej3Y_Bl zRzL^%t-WQfo-!iC_x`d4J)8 zF9YRjZPK}>6xEFZBl?*;^?<;aGlm}_-Rpa=O5cV)v)QS$*hsZ*WKd6bvQCd!WcVtd zlK3H`<9e4*Bv3*KF}>k~e59z6!~FSZlL@9=l$gLaLRVGq5l~ICNCTQ-6x@8Qa$Hm$ z_fu8^MygL^lxmU?x3s58nx&urWR-g`rNhUY+~gXgqCKMFg6k;>6VnD(``<~fVKVH9 zq>GOu5HvZp1gFc0@=HoSLk+a7a`rzKsw2=IFta9^I z(1fX|`n|2Qv`l8>A$)CzOp?UTLiThLfZs{nKDwmrhf+pUtfC$-QQ`KqeXkdDxs&`S z`Npmo_KMCRH1YOA7d=Skhnfinx?d6<8?mLUpCOVHr2^pXNsb#VH^GpZIL@=t%kYN= zk{}t<(Ga?dQJjvIqa7y=?xqQ47zkIRK1J9&&7Nb|RwpLXzGy?CJ=ZM>cbfoSECxgS zTEzufl50C38o1Mm&lf`Em3V89w=sqH(At7rmloK^7d@$!an@OzmZJ;wTI~~H3NY^N8;8OU!fGSnp1HRMgqF!zn z_wy`v=K(T!upoYHD#Vh#{xrdaw6E#q@oRIG!5+rFR8KBqVnnhTgL5Pk*HUgQa?o3_ zr%{+@-A>vp;38->RPXTA#cQY1BGQ<*&cdw9gP(pxS)^*qMEN5cbP1Uq zPB%2T!w(XA&A+sn-S-Q-Sk|Y>dO8{qaujboF8+bGS>k^^fDrTm-#qr5OsWwNbOpMT z%0U!-;Yv3@MpxXLFUfpi>oQV=1Ja_;+_spZfd+{Ma=Mk_=-lKAG9+KOpG}^Y>TY4i z{y|%TZH?7ak3?Ja`oVmLDno~JK3ls{6F6=Aice`XY+Sq)Ml{566#)0wfbU5Lvry`I z*fh-!hv@Y)@5sKHG<3n|n61Bp#SvS4ONPJQ&kw$mJP9@<+wc9(9N`=fJhCq&GnN#% zT^Gdxqsa!iB=jIkG-Y?!^K+0gzJNMc^(wWvKVCmlyJLATtPLG~c@S97B^{J=q&9 zmSC+%lgoKFubxK^KCW>)>u{LQ=f|VvtfcOWgs;@gQ8^FiI1&`6Dj4<(r6 zMw0M2(v!%*pB27YRgwgL?;dG;xs{rojnPOEhLO&x2!%lnov~4;aHEq**$kWK22xz2 z_|>(ozVnpXNP*csyrr7WKfagR>azs?Pau(-^ioh^oanTB2MD+9qZhr_8I&9TV$ zGg_}?zxWz{|4-X*rgnEju-Khz7;VX4SeRCyGF|9<7FE!Gc1TINFXTH8fC!0!=*O-- zBHfXYZpg66u(Jo_#nc7|T~HHJ`9S#87_uDIcmA$OiLh!W5xxjk#?Z%WJ~mn93U^no zd0<)0eW(97hJlSg;EwnOQgfoY=W@sHB|91XSDsatf}QhA6jB2>Z^6_oG~JuWeRBDa z<5iC#=HElMlH{~^vz+ouXgXm5UNkvdG_fMuWvq62@*6~DMm5QcoqRPFc{9&`YV~}J znW8>w|KqK)d97M@{j#T$tvWfsYroy&(^r*cPkSTAVMYU-lzr9xQ7rXVvG0X7&c;^x z2bt4LJyIzd6iocaIg$CCS@H6OhS6FY;`;e0#q7{x8*pCfdfEF_uZI$f>yBPUaqHYJ zfxmeEyK6sib1VCc?guk6`%ZaiR|D&5ziRQ{UfZe(pL^|XYu|!4h&2t@oQbJm&=Zy7 z$0a3}1%S@%+FQ?p@M}%gfSzUn!IuQD9P&%Y{qqsVF^O^^z9h`#lNk9%gdRiYPq%Ox zXRsaO$r9u4-LCg9f*${9zh9`X$lsZ>nxiB(dx|`#;=SB0byAC8=*AC&-I_R!{NTOx zMjDaf<@>5i&W^lqm|pd{Qk%+T*Qe+kF-7)RC(5;ExJt&2jjM+wS=Il>E1-Uk*mpc= zx>easA0%~lVEQk|{F@EqS&40ybdrt`=?bs@DRe?3p0$pB>)} z;m4ns_(v5+tRgou)9*)%&q}cS#r3VCep>Xm7(tk*UJ^k#rz{?W!0+t@AWP(BvZxWiS6_|PyAf!F&6T8M=@A5Zp4W412xvFe2lYFj6ogJfFY-#$)l#5B>TTW#V)ll0V~#Qr?L zIA^1-rDsU(iNxvb_nXa+;C?`(3we+|VW;Bt?hp--EI9K0H;^8{0u^{~8>yVD(t??w zueOIgc1q?=HS^Dgot_tYx0Mh=L7kp%!vutAaPzGcBxqj(m*T#1p6jC(^>FiwUddHg#U?cg`^Zb+i&`gOSVWPYY+#xCJ+&#&`&$#-S zq^2-hT6Vt(DZ<2lBB7rL4ruT#zDK*%CQGhsxbvszHka)0I{Uf3>e(FlUVCTCBjwek zhdNS-_M+Hwb3I(vK*L2-49yQ?c~k9Y|J`pSl@;gy@wT&=jtW`sr30lSO_On%?+nX z@_m>&x`=#jp4f}(44j6}6nHgPt?}hT!->HFS&N9$Q!#Ek4b=?K>ar%T#4dqJaD?>I z53s+}(JHtNJic z{QQ#5vR~jTZ3Gc#5{78Y)QL`S7PaHKb-H3^B zb!^^RtzklY}jn zSk;ga)wD!bpQW0p3DxKDHgtg*xo9E5q4&OcF6g;*=M{f8Wli=~ktcdkSHGZ5G7imR zZ0!%GjGOh#qOulW^nIU1?g~5bpqKBrHNG0oxiAu*xisP};&T&Z8|53jEugKtR9VW> zJ2D3g{-V5bz!Dk;o4EenTJrqul;EAM7zS{4XDZN(WsWN`izwcP)nN(j{}@8a_#hTZ zOt(d;SqGMTcw0jXt?W7eZ8^CAn=H(SJ`w(*z5?gQ$0{k3=XOn zP2AYP>>kxrBV7r-MQ`jE$)JrEq$fS^A~0);%XVdEF-m1TK(wlAMgC{syNGC%9pso$C?f!s!B%VRU)t!($w{6G+YN&$ob&CMAt&KCZ?kGuCPMm!t$9#Ym+OD5_Iz6gymD}HzLdBw5}

V$L6}YW>h3Hzl)#jl@^zVVq7Dt_RMK{WSf*4 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 literal 0 HcmV?d00001 diff --git a/threedecision_maestro_dir/images/3decision_RVB_White_transparent.png b/threedecision_maestro_dir/images/3decision_RVB_White_transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..1550f8fa3c01cce7e2cfda98c4808b9ee8536c50 GIT binary patch literal 136660 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 literal 0 HcmV?d00001 diff --git a/threedecision_maestro_dir/images/LogoTitle2.0.png b/threedecision_maestro_dir/images/LogoTitle2.0.png new file mode 100644 index 0000000000000000000000000000000000000000..2058e564b7ccd40e21b38d18cf91f4fcf3f07c6e GIT binary patch literal 13871 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/threedecision_maestro_dir/images/cog-16-dark.png b/threedecision_maestro_dir/images/cog-16-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..724ad8e9dbfed6260f81d6b342b9e491b63fb9f7 GIT binary patch literal 824 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|T2doC(|mmy zw18|523AHP24;{FAY@>aVqgWc85q16rQz%#Mh&PMCI*J~Oa>OHnkXO*0trA2)D5MX z7ce4BT)+fZEwF$Y!3L^k&@lR72BbI(JR*x382FBWFymBhK53w7k(nV8B@w}Ff zdWk9dNvV1jxdk9y3^o;3KxS@gNuokUZcbjYRfVk*ScMgk4HDK@QUEI{$+lIB@C{IK z&M!(;Fw-;8Gf=YQQczH^DN0GR3UYCSY6tRcl`=|73as??%gf94%8m8%i_-NCEiEne z4UF`SjC6r2bc-wVN)jt{^NN)rhQQ2mNi9w;$}A|!%+FH*nVXoDUs__Tqy(}E4j}F< z$xK7olvfP(7SMzGAQ^o_Jp+BX*+8u}AWsb3~j(FQN*D3*yw{SM{*3r zr(jW_SM9iL^x?r~$0Zr}kR2G3FFaiwLo5WBPPXMb6d=&jJauitQ$d~Fy*=g^y!Q0$ zZ0%UueyKssD!xM4Tt84*kH>oE-;-+j@qv4`w>pI~{aM{R)sR23`lCD39XGz6y3Q7y zB6C{j7I>b|_^{TYC%q`z_r4}{>J-9t{+li>`Swr_2B2> zKTCd`m1df`u+pk%~gLC5o|M!=FMe-9UmheX3X2P`IL{-y2Cc=rMOPYZ-da9~ z%X96L#Y+x;+}(4nxVbXwep10&TZ#0<>{5d~rx{28SM^T*x!*C#?o^}c;x|rp)jpOV co)*_JdCU?ys8qe@BhXt6p00i_>zopr0J)BIZ2$lO literal 0 HcmV?d00001 diff --git a/threedecision_maestro_dir/images/cog-16.png b/threedecision_maestro_dir/images/cog-16.png new file mode 100644 index 0000000000000000000000000000000000000000..0867d85b8e9ecbe4a7a58706521b1e68346b6569 GIT binary patch literal 281 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5M?jcysy3fAQ1G;; zi(`mI@6ySJd`%7_F6tp$)I#JMCrvwD!MxHeE^TkC;WQa9pDh()HIL43TJb`~KCyk~ z$@_QrUt(%t+YxE%ZI!Rlcx$b)zRU6J4l|A8eeN)`KX}Eo&S69SjBO#4c8QhVa{Yht zlau{VuIYtE_sc!2J*8LgEfhZYGV^%K`PgR@c0ch^y3p})R#3>y2Cc=rMOPYZ-da9~ z%X96L#Y+x;+}(4nxVbXwep10&TZ#0<>{5d~rx{28SM^T*x!*C#?o^}c;x|rp)jpOV co)*_JdCU?ys8qe@BhXt6p00i_>zopr0J)BIZ2$lO literal 0 HcmV?d00001 From 91d27e41cc88673e6c300cdb0b30391db272fe8e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 07:53:53 +0000 Subject: [PATCH 2/4] Initial plan From c2879fc6e5fdaa4ac2513afa54e361c7b9ecc24c Mon Sep 17 00:00:00 2001 From: pschmidtke Date: Fri, 6 Feb 2026 09:32:11 +0100 Subject: [PATCH 3/4] Address Copilot review: security, SSL config, code quality Security improvements: - Add sanitize_filename() to prevent path traversal attacks - Add sanitize_surface_name() for safe Maestro command injection - Replace bare except clauses with specific exception types - Set config file permissions to 0600 New features: - Add SSL certificate verification toggle in Settings dialog - Extract color constants for maintainable theming - Create create_dark_palette() helper to eliminate duplicate code Code quality: - Move identity_matrix outside loop for efficiency - Update test_connection to make real HTTP request - Convert DARK_STYLESHEET to f-string using color constants - Update all inline styles to use color constants Co-Authored-By: Claude Opus 4.5 --- threedecision_maestro.py | 6 +- threedecision_maestro_dir/api_client.py | 71 ++- threedecision_maestro_dir/gui.py | 643 ++++++++++++++---------- 3 files changed, 435 insertions(+), 285 deletions(-) diff --git a/threedecision_maestro.py b/threedecision_maestro.py index 47228e0..c0411da 100644 --- a/threedecision_maestro.py +++ b/threedecision_maestro.py @@ -41,8 +41,12 @@ def run_plugin(): try: from schrodinger.Qt.QtWidgets import QMessageBox QMessageBox.critical(None, "3decision Plugin Error", msg) - except: + except ImportError: + # QMessageBox not available (running outside Maestro) pass + except Exception as ui_error: + # Log unexpected errors without crashing + print(f"WARNING: Could not display error dialog: {ui_error}") return None diff --git a/threedecision_maestro_dir/api_client.py b/threedecision_maestro_dir/api_client.py index 657275c..82756ec 100644 --- a/threedecision_maestro_dir/api_client.py +++ b/threedecision_maestro_dir/api_client.py @@ -19,6 +19,9 @@ # Simple logging state - stored as module-level variable _logging_enabled = False +# SSL verification setting - enabled by default for security +_ssl_verify_enabled = True + # Private structure naming attribute - stored as module-level variable # Options: 'label', 'title', 'external_code', 'internal_id' _private_structure_naming_attribute = 'label' @@ -33,6 +36,20 @@ def is_logging_enabled(): global _logging_enabled return _logging_enabled +def set_ssl_verify_enabled(enabled): + """Enable or disable SSL certificate verification. + + SSL verification is enabled by default for security. + Only disable for development/internal servers with self-signed certificates. + """ + global _ssl_verify_enabled + _ssl_verify_enabled = enabled + +def is_ssl_verify_enabled(): + """Check if SSL verification is enabled""" + global _ssl_verify_enabled + return _ssl_verify_enabled + def set_private_structure_naming_attribute(attribute): """Set the attribute to use for naming private structures. @@ -78,10 +95,10 @@ def __init__(self): self.api_key = None self.token = None self.session = requests.Session() - # Disable SSL certificate verification for all requests - self.session.verify = False self.config_file = os.path.expanduser("~/.3decision_maestro_config") self.load_config() + # Apply SSL verification setting (loaded from config) + self.session.verify = is_ssl_verify_enabled() def load_config(self): """Load configuration from file""" @@ -100,6 +117,11 @@ def load_config(self): log_enabled = log_enabled_str.lower() == 'true' set_logging_enabled(log_enabled) + # Load SSL verification setting (defaults to true for security) + ssl_verify_str = config['API'].get('ssl_verify', 'true') + ssl_verify = ssl_verify_str.lower() == 'true' + set_ssl_verify_enabled(ssl_verify) + # Load private structure naming attribute setting naming_attr = config['API'].get('private_structure_naming_attribute', 'label') set_private_structure_naming_attribute(naming_attr) @@ -122,12 +144,17 @@ def save_config(self): 'api_key': self.api_key or '', 'token': self.token or '', 'logging_enabled': str(is_logging_enabled()).lower(), + 'ssl_verify': str(is_ssl_verify_enabled()).lower(), 'private_structure_naming_attribute': get_private_structure_naming_attribute() } with open(self.config_file, 'w') as f: config.write(f) + # Set restrictive permissions (owner read/write only) for security + import stat + os.chmod(self.config_file, stat.S_IRUSR | stat.S_IWUSR) + except Exception as e: log_error(f"Error saving config: {e}") @@ -195,6 +222,12 @@ def save_naming_attribute_setting(self, attribute: str): set_private_structure_naming_attribute(attribute) self.save_config() + def save_ssl_setting(self, enabled: bool): + """Save SSL verification setting to config file""" + set_ssl_verify_enabled(enabled) + self.session.verify = enabled + self.save_config() + def configure(self, base_url: str, api_key: str): """Configure API settings""" # Ensure the URL includes the scheme @@ -270,18 +303,36 @@ def login(self) -> bool: return False def test_connection(self) -> bool: - """Test API connection""" + """Test API connection and validate authentication token""" if not self.is_configured(): return False - # If we have a token, assume it's valid and don't test - if self.token and 'Authorization' in self.session.headers: - log_debug("Using existing token for connection") - return True + # If no token, try to login first + if not (self.token and 'Authorization' in self.session.headers): + log_debug("No valid token found, attempting login") + return self.login() + + # We have a token - make a lightweight request to verify it's still valid + test_url = self.base_url.rstrip('/') + '/' + log_debug(f"Testing API connection with existing token against {test_url}") + try: + response = self.session.get(test_url, timeout=10) + + # If unauthorized, token is invalid - try to login again + if response.status_code in (401, 403): + log_debug(f"Token validation returned {response.status_code}, attempting re-login") + return self.login() - # Try to login if no token - log_debug("No token found, attempting login") - return self.login() + if 200 <= response.status_code < 400: + log_debug("Connection test succeeded with existing token") + return True + + log_error(f"Connection test failed with status code: {response.status_code}") + return False + + except requests.RequestException as e: + log_error(f"Connection test failed due to network error: {e}") + return False def submit_search(self, query: str) -> Optional[Dict[str, Any]]: """Submit a search query and return job info""" diff --git a/threedecision_maestro_dir/gui.py b/threedecision_maestro_dir/gui.py index 9ed7522..5d819de 100644 --- a/threedecision_maestro_dir/gui.py +++ b/threedecision_maestro_dir/gui.py @@ -8,253 +8,322 @@ import os import sys -import json +import re +import subprocess import tempfile -from typing import Optional, Dict, List, Any +from typing import Dict, List, Any # Import Schrodinger modules try: from schrodinger.ui.qt.appframework2 import af2 - from schrodinger.ui.qt import swidgets - from schrodinger.Qt import QtWidgets, QtCore, QtGui + from schrodinger.Qt import QtGui from schrodinger.Qt.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QTableWidget, QTableWidgetItem, QLabel, QMessageBox, QHeaderView, QProgressBar, QCheckBox, QAbstractItemView, QTabWidget, QWidget, - QFormLayout, QComboBox, QFrame, QDialogButtonBox + QFormLayout, QComboBox, QDialogButtonBox ) from schrodinger.Qt.QtCore import Qt, QThread, pyqtSignal from schrodinger.Qt.QtGui import QPixmap, QIcon from schrodinger import maestro, structure MAESTRO_AVAILABLE = True - SWIDGETS_AVAILABLE = True except ImportError: # Fallback for testing outside Maestro - from PyQt5 import QtWidgets, QtCore, QtGui + from PyQt5 import QtGui from PyQt5.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QTableWidget, QTableWidgetItem, QLabel, QMessageBox, QHeaderView, QProgressBar, QCheckBox, QAbstractItemView, QTabWidget, QWidget, - QFormLayout, QComboBox, QFrame, QDialogButtonBox + QFormLayout, QComboBox, QDialogButtonBox ) from PyQt5.QtCore import Qt, QThread, pyqtSignal from PyQt5.QtGui import QPixmap, QIcon MAESTRO_AVAILABLE = False - SWIDGETS_AVAILABLE = False af2 = None - swidgets = None # Import api_client - try relative import first, then absolute +# Both forms needed: relative works when installed as package, absolute when run directly try: - from .api_client import ThreeDecisionAPIClient, is_logging_enabled, get_private_structure_naming_attribute + from .api_client import ( + ThreeDecisionAPIClient, is_logging_enabled, get_private_structure_naming_attribute, + is_ssl_verify_enabled, set_ssl_verify_enabled + ) except ImportError: - from api_client import ThreeDecisionAPIClient, is_logging_enabled, get_private_structure_naming_attribute + from api_client import ( + ThreeDecisionAPIClient, is_logging_enabled, get_private_structure_naming_attribute, + is_ssl_verify_enabled, set_ssl_verify_enabled + ) + + +def sanitize_filename(filename: str) -> str: + """ + Sanitize a filename to prevent path traversal attacks. + Removes path separators and parent directory references. + """ + # Get just the basename, removing any directory components + filename = os.path.basename(filename) + # Remove any remaining path traversal patterns + filename = filename.replace('..', '').replace('/', '').replace('\\', '') + # Remove null bytes + filename = filename.replace('\x00', '') + # If filename is empty after sanitization, use a default + if not filename: + filename = 'unnamed_file' + return filename + + +def sanitize_surface_name(name: str) -> str: + """ + Sanitize a surface name for use in Maestro commands. + Only allows alphanumeric characters, underscores, and hyphens. + """ + return "".join(c if c.isalnum() or c in ("_", "-") else "_" for c in name) + + +# Color constants for dark theme +COLOR_DARK_BG = "#444444" +COLOR_DARK_BG_ALT = "#3a3a3a" +COLOR_DARK_BORDER = "#555555" +COLOR_TEXT = "#ECECEC" +COLOR_TEXT_DIM = "#ABABAB" +COLOR_TEXT_DISABLED = "#666666" +COLOR_TEXT_PLACEHOLDER = "#888888" +COLOR_ACCENT = "#60B0DC" +COLOR_BUTTON = "#505050" +COLOR_BUTTON_HOVER = "#585858" +COLOR_BUTTON_PRESSED = "#404040" +COLOR_BUTTON_BORDER = "#606060" +COLOR_HEADER = "#4a4a4a" +COLOR_SUCCESS = "#7FBA7A" +COLOR_ERROR = "#FF6B6B" +COLOR_SCROLLBAR = "#606060" +COLOR_SCROLLBAR_HOVER = "#707070" + + +def create_dark_palette(): + """ + Create a dark QPalette for widgets. + This ensures consistent dark theming across all widgets. + """ + dark_palette = QtGui.QPalette() + dark_color = QtGui.QColor(68, 68, 68) # COLOR_DARK_BG + dark_palette.setColor(QtGui.QPalette.Window, dark_color) + dark_palette.setColor(QtGui.QPalette.Base, QtGui.QColor(58, 58, 58)) # COLOR_DARK_BG_ALT + dark_palette.setColor(QtGui.QPalette.AlternateBase, dark_color) + dark_palette.setColor(QtGui.QPalette.Button, QtGui.QColor(80, 80, 80)) # COLOR_BUTTON + dark_palette.setColor(QtGui.QPalette.WindowText, QtGui.QColor(236, 236, 236)) # COLOR_TEXT + dark_palette.setColor(QtGui.QPalette.Text, QtGui.QColor(236, 236, 236)) + dark_palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor(236, 236, 236)) + dark_palette.setColor(QtGui.QPalette.Highlight, QtGui.QColor(96, 176, 220)) # COLOR_ACCENT + dark_palette.setColor(QtGui.QPalette.HighlightedText, QtGui.QColor(255, 255, 255)) + dark_palette.setColor(QtGui.QPalette.PlaceholderText, QtGui.QColor(136, 136, 136)) # COLOR_TEXT_PLACEHOLDER + return dark_palette # Dark theme stylesheet matching Maestro's dark UI -DARK_STYLESHEET = """ - * { - background-color: #444444; - color: #ECECEC; - } - QWidget, QFrame, QDialog { - background-color: #444444; - color: #ECECEC; - } - QLabel { +DARK_STYLESHEET = f""" + * {{ + background-color: {COLOR_DARK_BG}; + color: {COLOR_TEXT}; + }} + QWidget, QFrame, QDialog {{ + background-color: {COLOR_DARK_BG}; + color: {COLOR_TEXT}; + }} + QLabel {{ background-color: transparent; - color: #ECECEC; - } - QStatusBar { - background-color: #3a3a3a; - color: #ECECEC; - border-top: 1px solid #555555; - } - QLineEdit { - background-color: #3a3a3a; - color: #ECECEC; - border: 1px solid #555555; + color: {COLOR_TEXT}; + }} + QStatusBar {{ + background-color: {COLOR_DARK_BG_ALT}; + color: {COLOR_TEXT}; + border-top: 1px solid {COLOR_DARK_BORDER}; + }} + QLineEdit {{ + background-color: {COLOR_DARK_BG_ALT}; + color: {COLOR_TEXT}; + border: 1px solid {COLOR_DARK_BORDER}; border-radius: 3px; padding: 4px 6px; - selection-background-color: #60B0DC; - } - QLineEdit:focus { - border: 1px solid #60B0DC; - } - QLineEdit:disabled { + selection-background-color: {COLOR_ACCENT}; + }} + QLineEdit:focus {{ + border: 1px solid {COLOR_ACCENT}; + }} + QLineEdit:disabled {{ background-color: #383838; - color: #888888; - } - QLineEdit::placeholder { - color: #888888; - } - QComboBox { - background-color: #3a3a3a; - color: #ECECEC; - border: 1px solid #555555; + color: {COLOR_TEXT_PLACEHOLDER}; + }} + QLineEdit::placeholder {{ + color: {COLOR_TEXT_PLACEHOLDER}; + }} + QComboBox {{ + background-color: {COLOR_DARK_BG_ALT}; + color: {COLOR_TEXT}; + border: 1px solid {COLOR_DARK_BORDER}; border-radius: 3px; padding: 4px 6px; - } - QComboBox:hover, QComboBox:focus { - border: 1px solid #60B0DC; - } - QComboBox::drop-down { + }} + QComboBox:hover, QComboBox:focus {{ + border: 1px solid {COLOR_ACCENT}; + }} + QComboBox::drop-down {{ border: none; width: 20px; - } - QComboBox::down-arrow { + }} + QComboBox::down-arrow {{ image: none; border-left: 4px solid transparent; border-right: 4px solid transparent; - border-top: 6px solid #ECECEC; + border-top: 6px solid {COLOR_TEXT}; margin-right: 6px; - } - QComboBox QAbstractItemView { - background-color: #3a3a3a; - color: #ECECEC; - selection-background-color: #60B0DC; - border: 1px solid #555555; - } - QPushButton { - background-color: #505050; - color: #ECECEC; - border: 1px solid #606060; + }} + QComboBox QAbstractItemView {{ + background-color: {COLOR_DARK_BG_ALT}; + color: {COLOR_TEXT}; + selection-background-color: {COLOR_ACCENT}; + border: 1px solid {COLOR_DARK_BORDER}; + }} + QPushButton {{ + background-color: {COLOR_BUTTON}; + color: {COLOR_TEXT}; + border: 1px solid {COLOR_BUTTON_BORDER}; border-radius: 3px; padding: 5px 12px; min-height: 20px; - } - QPushButton:hover { - background-color: #585858; - border: 1px solid #60B0DC; - } - QPushButton:pressed { - background-color: #404040; - } - QPushButton:disabled { - background-color: #3a3a3a; - color: #666666; - border: 1px solid #4a4a4a; - } - QCheckBox { + }} + QPushButton:hover {{ + background-color: {COLOR_BUTTON_HOVER}; + border: 1px solid {COLOR_ACCENT}; + }} + QPushButton:pressed {{ + background-color: {COLOR_BUTTON_PRESSED}; + }} + QPushButton:disabled {{ + background-color: {COLOR_DARK_BG_ALT}; + color: {COLOR_TEXT_DISABLED}; + border: 1px solid {COLOR_HEADER}; + }} + QCheckBox {{ background-color: transparent; - color: #ECECEC; + color: {COLOR_TEXT}; spacing: 6px; - } - QCheckBox::indicator { + }} + QCheckBox::indicator {{ width: 16px; height: 16px; - border: 1px solid #555555; + border: 1px solid {COLOR_DARK_BORDER}; border-radius: 2px; - background-color: #3a3a3a; - } - QCheckBox::indicator:checked { - background-color: #60B0DC; - border-color: #60B0DC; - } - QCheckBox::indicator:hover { - border-color: #60B0DC; - } - QTabWidget::pane { - border: 1px solid #555555; - background-color: #444444; + background-color: {COLOR_DARK_BG_ALT}; + }} + QCheckBox::indicator:checked {{ + background-color: {COLOR_ACCENT}; + border-color: {COLOR_ACCENT}; + }} + QCheckBox::indicator:hover {{ + border-color: {COLOR_ACCENT}; + }} + QTabWidget::pane {{ + border: 1px solid {COLOR_DARK_BORDER}; + background-color: {COLOR_DARK_BG}; border-radius: 3px; - } - QTabBar::tab { - background-color: #3a3a3a; - color: #ABABAB; - border: 1px solid #555555; + }} + QTabBar::tab {{ + background-color: {COLOR_DARK_BG_ALT}; + color: {COLOR_TEXT_DIM}; + border: 1px solid {COLOR_DARK_BORDER}; border-bottom: none; padding: 6px 16px; margin-right: 2px; border-top-left-radius: 3px; border-top-right-radius: 3px; - } - QTabBar::tab:selected { - background-color: #444444; - color: #ECECEC; - border-bottom: 1px solid #444444; - } - QTabBar::tab:hover:!selected { - background-color: #404040; - color: #ECECEC; - } - QTableWidget { - background-color: #3a3a3a; - color: #ECECEC; - gridline-color: #555555; - border: 1px solid #555555; - selection-background-color: #60B0DC; + }} + QTabBar::tab:selected {{ + background-color: {COLOR_DARK_BG}; + color: {COLOR_TEXT}; + border-bottom: 1px solid {COLOR_DARK_BG}; + }} + QTabBar::tab:hover:!selected {{ + background-color: {COLOR_BUTTON_PRESSED}; + color: {COLOR_TEXT}; + }} + QTableWidget {{ + background-color: {COLOR_DARK_BG_ALT}; + color: {COLOR_TEXT}; + gridline-color: {COLOR_DARK_BORDER}; + border: 1px solid {COLOR_DARK_BORDER}; + selection-background-color: {COLOR_ACCENT}; selection-color: #FFFFFF; - } - QTableWidget::item { + }} + QTableWidget::item {{ padding: 4px; - } - QTableWidget::item:selected { - background-color: #60B0DC; + }} + QTableWidget::item:selected {{ + background-color: {COLOR_ACCENT}; color: #FFFFFF; - } - QHeaderView::section { - background-color: #4a4a4a; - color: #ECECEC; + }} + QHeaderView::section {{ + background-color: {COLOR_HEADER}; + color: {COLOR_TEXT}; padding: 5px; border: none; - border-right: 1px solid #555555; - border-bottom: 1px solid #555555; - } - QHeaderView::section:hover { - background-color: #555555; - } - QProgressBar { - background-color: #3a3a3a; - border: 1px solid #555555; + border-right: 1px solid {COLOR_DARK_BORDER}; + border-bottom: 1px solid {COLOR_DARK_BORDER}; + }} + QHeaderView::section:hover {{ + background-color: {COLOR_DARK_BORDER}; + }} + QProgressBar {{ + background-color: {COLOR_DARK_BG_ALT}; + border: 1px solid {COLOR_DARK_BORDER}; border-radius: 3px; text-align: center; - color: #ECECEC; - } - QProgressBar::chunk { - background-color: #60B0DC; + color: {COLOR_TEXT}; + }} + QProgressBar::chunk {{ + background-color: {COLOR_ACCENT}; border-radius: 2px; - } - QScrollBar:vertical { - background-color: #3a3a3a; + }} + QScrollBar:vertical {{ + background-color: {COLOR_DARK_BG_ALT}; width: 12px; border: none; - } - QScrollBar::handle:vertical { - background-color: #606060; + }} + QScrollBar::handle:vertical {{ + background-color: {COLOR_SCROLLBAR}; min-height: 20px; border-radius: 3px; margin: 2px; - } - QScrollBar::handle:vertical:hover { - background-color: #707070; - } - QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { + }} + QScrollBar::handle:vertical:hover {{ + background-color: {COLOR_SCROLLBAR_HOVER}; + }} + QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{ height: 0px; - } - QScrollBar:horizontal { - background-color: #3a3a3a; + }} + QScrollBar:horizontal {{ + background-color: {COLOR_DARK_BG_ALT}; height: 12px; border: none; - } - QScrollBar::handle:horizontal { - background-color: #606060; + }} + QScrollBar::handle:horizontal {{ + background-color: {COLOR_SCROLLBAR}; min-width: 20px; border-radius: 3px; margin: 2px; - } - QScrollBar::handle:horizontal:hover { - background-color: #707070; - } - QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { + }} + QScrollBar::handle:horizontal:hover {{ + background-color: {COLOR_SCROLLBAR_HOVER}; + }} + QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {{ width: 0px; - } - QDialogButtonBox QPushButton { + }} + QDialogButtonBox QPushButton {{ min-width: 70px; - } - QFrame[frameShape="4"], QFrame[frameShape="5"] { - background-color: #555555; - } + }} + QFrame[frameShape="4"], QFrame[frameShape="5"] {{ + background-color: {COLOR_DARK_BORDER}; + }} """ @@ -481,19 +550,20 @@ def run(self): log_debug("Loading structures with transformation matrices using batch export") self.status_update.emit("Loading structures with transformations...") + # Define identity matrix once outside the loop for efficiency + identity_matrix = [ + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ] + structures_with_transforms = [] for structure_info in self.structure_data: structure_id = int(structure_info['structure_id']) external_code = structure_info['external_code'] matrix = structure_info.get('matrix') - identity_matrix = [ - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 - ] - if matrix: if isinstance(matrix, list) and len(matrix) == 4: flat_matrix = [] @@ -656,6 +726,14 @@ def init_ui(self): self.api_key_input.setMinimumWidth(300) form_layout.addRow("API Key:", self.api_key_input) + # SSL verification checkbox + self.ssl_verify_checkbox = QCheckBox("Verify SSL certificates") + self.ssl_verify_checkbox.setToolTip( + "Enable SSL certificate verification for API requests.\n" + "Disable only for testing with self-signed certificates." + ) + form_layout.addRow("", self.ssl_verify_checkbox) + # Log events checkbox self.log_events_checkbox = QCheckBox("Log events") self.log_events_checkbox.setToolTip("Enable logging of plugin events to console") @@ -705,6 +783,9 @@ def load_current_settings(self): if self.api_client.api_key: self.api_key_input.setText(self.api_client.api_key) + # Load SSL verification setting + self.ssl_verify_checkbox.setChecked(is_ssl_verify_enabled()) + try: from .api_client import is_logging_enabled, get_private_structure_naming_attribute except ImportError: @@ -731,7 +812,7 @@ def test_connection(self): self.test_button.setEnabled(False) self.status_label.setText("Testing connection...") - self.status_label.setStyleSheet("color: #60B0DC;") + self.status_label.setStyleSheet(f"color: {COLOR_ACCENT};") # Configure and test old_url = self.api_client.base_url @@ -742,10 +823,10 @@ def test_connection(self): if self.api_client.test_connection(): self.status_label.setText("Connection successful!") - self.status_label.setStyleSheet("color: #7FBA7A; font-weight: bold;") + self.status_label.setStyleSheet(f"color: {COLOR_SUCCESS}; font-weight: bold;") else: self.status_label.setText("Connection failed. Please check your URL and API key.") - self.status_label.setStyleSheet("color: #FF6B6B; font-weight: bold;") + self.status_label.setStyleSheet(f"color: {COLOR_ERROR}; font-weight: bold;") # Restore old settings self.api_client.base_url = old_url self.api_client.api_key = old_key @@ -770,6 +851,13 @@ def save_settings(self): self.api_client.configure(url, api_key) self.api_client.save_config() + # Save SSL verification setting + ssl_enabled = self.ssl_verify_checkbox.isChecked() + set_ssl_verify_enabled(ssl_enabled) + self.api_client.save_ssl_setting(ssl_enabled) + # Apply to current session + self.api_client.session.verify = ssl_enabled + # Save logging setting log_enabled = self.log_events_checkbox.isChecked() try: @@ -833,83 +921,71 @@ def layOut(self): self.setStyleSheet(DARK_STYLESHEET) # Force dark background using palette - dark_palette = QtGui.QPalette() - dark_color = QtGui.QColor(68, 68, 68) # #444444 - dark_palette.setColor(QtGui.QPalette.Window, dark_color) - dark_palette.setColor(QtGui.QPalette.Base, QtGui.QColor(58, 58, 58)) - dark_palette.setColor(QtGui.QPalette.AlternateBase, dark_color) - dark_palette.setColor(QtGui.QPalette.Button, QtGui.QColor(80, 80, 80)) - dark_palette.setColor(QtGui.QPalette.WindowText, QtGui.QColor(236, 236, 236)) - dark_palette.setColor(QtGui.QPalette.Text, QtGui.QColor(236, 236, 236)) - dark_palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor(236, 236, 236)) - dark_palette.setColor(QtGui.QPalette.Highlight, QtGui.QColor(96, 176, 220)) - dark_palette.setColor(QtGui.QPalette.HighlightedText, QtGui.QColor(255, 255, 255)) - dark_palette.setColor(QtGui.QPalette.PlaceholderText, QtGui.QColor(136, 136, 136)) # #888888 - self.setPalette(dark_palette) + self.setPalette(create_dark_palette()) self.setAutoFillBackground(True) # Also style the af2 status bar if it exists if hasattr(self, 'status_bar') and self.status_bar: - self.status_bar.setStyleSheet(""" - QStatusBar { - background-color: #3a3a3a; - color: #ECECEC; - border-top: 1px solid #555555; - } - QLabel { - color: #ECECEC; + self.status_bar.setStyleSheet(f""" + QStatusBar {{ + background-color: {COLOR_DARK_BG_ALT}; + color: {COLOR_TEXT}; + border-top: 1px solid {COLOR_DARK_BORDER}; + }} + QLabel {{ + color: {COLOR_TEXT}; background-color: transparent; - } - QPushButton, QToolButton { - background-color: #505050; - color: #ECECEC; - border: 1px solid #606060; + }} + QPushButton, QToolButton {{ + background-color: {COLOR_BUTTON}; + color: {COLOR_TEXT}; + border: 1px solid {COLOR_BUTTON_BORDER}; border-radius: 3px; padding: 3px 8px; - } - QPushButton:hover, QToolButton:hover { - background-color: #585858; - border: 1px solid #60B0DC; - } - QProgressBar { - background-color: #3a3a3a; - border: 1px solid #555555; + }} + QPushButton:hover, QToolButton:hover {{ + background-color: {COLOR_BUTTON_HOVER}; + border: 1px solid {COLOR_ACCENT}; + }} + QProgressBar {{ + background-color: {COLOR_DARK_BG_ALT}; + border: 1px solid {COLOR_DARK_BORDER}; border-radius: 3px; text-align: center; - color: #ECECEC; - } - QProgressBar::chunk { - background-color: #60B0DC; - } + color: {COLOR_TEXT}; + }} + QProgressBar::chunk {{ + background-color: {COLOR_ACCENT}; + }} """) # Also style the bottom bar if it exists if hasattr(self, 'bottom_bar') and self.bottom_bar: - self.bottom_bar.setStyleSheet(""" - QWidget { - background-color: #3a3a3a; - color: #ECECEC; - } - QPushButton, QToolButton { - background-color: #505050; - color: #ECECEC; - border: 1px solid #606060; + self.bottom_bar.setStyleSheet(f""" + QWidget {{ + background-color: {COLOR_DARK_BG_ALT}; + color: {COLOR_TEXT}; + }} + QPushButton, QToolButton {{ + background-color: {COLOR_BUTTON}; + color: {COLOR_TEXT}; + border: 1px solid {COLOR_BUTTON_BORDER}; border-radius: 3px; padding: 4px 10px; - } - QPushButton:hover, QToolButton:hover { - background-color: #585858; - border: 1px solid #60B0DC; - } - QLabel { - color: #ECECEC; + }} + QPushButton:hover, QToolButton:hover {{ + background-color: {COLOR_BUTTON_HOVER}; + border: 1px solid {COLOR_ACCENT}; + }} + QLabel {{ + color: {COLOR_TEXT}; background-color: transparent; - } + }} """) # Style the bottom_line separator if it exists if hasattr(self, 'bottom_line') and self.bottom_line: - self.bottom_line.setStyleSheet("background-color: #555555;") + self.bottom_line.setStyleSheet(f"background-color: {COLOR_DARK_BORDER};") # Settings button settings_layout = QHBoxLayout() @@ -940,7 +1016,7 @@ def layOut(self): # Status label self.status_label = QLabel("Not logged in") - self.status_label.setStyleSheet("color: #FF6B6B; padding: 5px; background-color: #444444;") + self.status_label.setStyleSheet(f"color: {COLOR_ERROR}; padding: 5px; background-color: {COLOR_DARK_BG};") self.status_label.setAutoFillBackground(True) main_layout.addWidget(self.status_label) @@ -952,18 +1028,7 @@ def layOut(self): def _apply_dark_palette_recursive(self, widget): """Apply dark palette to widget and all its children""" - dark_palette = QtGui.QPalette() - dark_color = QtGui.QColor(68, 68, 68) - dark_palette.setColor(QtGui.QPalette.Window, dark_color) - dark_palette.setColor(QtGui.QPalette.Base, QtGui.QColor(58, 58, 58)) - dark_palette.setColor(QtGui.QPalette.AlternateBase, dark_color) - dark_palette.setColor(QtGui.QPalette.Button, QtGui.QColor(80, 80, 80)) - dark_palette.setColor(QtGui.QPalette.WindowText, QtGui.QColor(236, 236, 236)) - dark_palette.setColor(QtGui.QPalette.Text, QtGui.QColor(236, 236, 236)) - dark_palette.setColor(QtGui.QPalette.ButtonText, QtGui.QColor(236, 236, 236)) - dark_palette.setColor(QtGui.QPalette.Highlight, QtGui.QColor(96, 176, 220)) - dark_palette.setColor(QtGui.QPalette.HighlightedText, QtGui.QColor(255, 255, 255)) - dark_palette.setColor(QtGui.QPalette.PlaceholderText, QtGui.QColor(136, 136, 136)) # #888888 + dark_palette = create_dark_palette() widget.setPalette(dark_palette) widget.setAutoFillBackground(True) @@ -1000,7 +1065,7 @@ def _create_search_tab(self): # Column filters filters_label = QLabel("Filter Results:") - filters_label.setStyleSheet("font-weight: bold; margin-top: 10px; color: #ECECEC;") + filters_label.setStyleSheet(f"font-weight: bold; margin-top: 10px; color: {COLOR_TEXT};") layout.addWidget(filters_label) filters_layout = QHBoxLayout() @@ -1216,7 +1281,7 @@ def _create_files_tab(self): # Header with current structure info self.files_header_label = QLabel("Associated Files - Select a structure from the Projects tab to view its files") - self.files_header_label.setStyleSheet("font-weight: bold; padding: 5px; color: #ECECEC;") + self.files_header_label.setStyleSheet(f"font-weight: bold; padding: 5px; color: {COLOR_TEXT};") layout.addWidget(self.files_header_label) # Checkbox to apply transformation matrix @@ -1273,7 +1338,7 @@ def _create_files_tab(self): # Status label for file operations self.files_status_label = QLabel("") - self.files_status_label.setStyleSheet("color: #ABABAB; padding: 5px;") + self.files_status_label.setStyleSheet(f"color: {COLOR_TEXT_DIM}; padding: 5px;") layout.addWidget(self.files_status_label) widget.setLayout(layout) @@ -1283,7 +1348,7 @@ def check_login_status(self): """Check if user is logged in and update UI accordingly""" if self.api_client.is_configured() and self.api_client.test_connection(): self.status_label.setText("Logged in successfully") - self.status_label.setStyleSheet("color: #7FBA7A;") + self.status_label.setStyleSheet(f"color: {COLOR_SUCCESS};") self.submit_button.setEnabled(True) if hasattr(self, 'projects_table') and not self.projects_loaded: @@ -1294,7 +1359,7 @@ def check_login_status(self): log_error(f"Failed to auto-load projects: {e}") else: self.status_label.setText("Not logged in - click Settings to configure") - self.status_label.setStyleSheet("color: #FF6B6B;") + self.status_label.setStyleSheet(f"color: {COLOR_ERROR};") self.submit_button.setEnabled(False) def open_settings(self): @@ -1436,7 +1501,9 @@ def load_pdb_into_maestro(self, pdb_content: str, object_name: str, metadata: di try: # Save PDB to temporary file temp_dir = tempfile.gettempdir() - pdb_filename = f"{object_name}.pdb" + # Sanitize object_name to prevent path traversal + safe_name = sanitize_filename(object_name) + pdb_filename = f"{safe_name}.pdb" pdb_path = os.path.join(temp_dir, pdb_filename) with open(pdb_path, 'w') as f: @@ -1465,8 +1532,11 @@ def load_pdb_into_maestro(self, pdb_content: str, object_name: str, metadata: di # Clean up temp file try: os.remove(pdb_path) - except: + except FileNotFoundError: + # File already removed or never created pass + except OSError as e: + log_debug(f"Could not remove temporary PDB file {pdb_path}: {e}") except Exception as e: log_error(f"Error loading PDB into Maestro: {e}") @@ -2245,7 +2315,9 @@ def open_selected_file(self): if file_data: temp_dir = tempfile.gettempdir() - file_path = os.path.join(temp_dir, filename) + # Sanitize filename to prevent path traversal + safe_filename = sanitize_filename(filename) + file_path = os.path.join(temp_dir, safe_filename) with open(file_path, 'wb') as f: f.write(file_data) @@ -2288,8 +2360,12 @@ def open_selected_file(self): map_type = 'ccp4' # Both CCP4 and MRC use ccp4 map type # Use visimport command to load the map onto the entry - surface_name = os.path.splitext(filename)[0] - cmd = f'visimport entry={entry_id} map_type={map_type} isovalue=1.0 "{file_path}":::{surface_name}' + # Sanitize surface_name to prevent command injection + raw_surface_name = os.path.splitext(safe_filename)[0] + surface_name = sanitize_surface_name(raw_surface_name) + # Escape quotes in file path + safe_file_path = file_path.replace('"', r'\"') + cmd = f'visimport entry={entry_id} map_type={map_type} isovalue=1.0 "{safe_file_path}":::{surface_name}' log_debug(f"Importing map with command: {cmd}") try: @@ -2324,8 +2400,10 @@ def open_selected_file(self): if file_format in structure_formats: try: os.remove(file_path) - except: + except FileNotFoundError: pass + except OSError as e: + log_debug(f"Could not remove temporary file {file_path}: {e}") else: self.files_status_label.setText("Failed to download file") @@ -2381,16 +2459,16 @@ def download_and_open_with_system(self): file_data = self.api_client.download_file(file_info) if file_data: - import subprocess - temp_dir = os.path.join(tempfile.gettempdir(), '3decision_downloads') os.makedirs(temp_dir, exist_ok=True) - file_path = os.path.join(temp_dir, filename) + # Sanitize filename to prevent path traversal + safe_filename = sanitize_filename(filename) + file_path = os.path.join(temp_dir, safe_filename) with open(file_path, 'wb') as f: f.write(file_data) - self.files_status_label.setText(f"Opening {filename} with system application...") + self.files_status_label.setText(f"Opening {safe_filename} with system application...") self._open_file_with_system(file_path) @@ -2407,14 +2485,31 @@ def download_and_open_with_system(self): def _open_file_with_system(self, file_path: str): """Open a file with the system's default application.""" - import subprocess - - if sys.platform == 'darwin': # macOS - subprocess.run(['open', file_path], check=True) - elif sys.platform == 'win32': # Windows - os.startfile(file_path) - else: # Linux - subprocess.run(['xdg-open', file_path], check=True) + try: + if sys.platform == 'darwin': # macOS + subprocess.run(['open', file_path], check=True, timeout=30) + elif sys.platform == 'win32': # Windows + os.startfile(file_path) + else: # Linux + subprocess.run(['xdg-open', file_path], check=True, timeout=30) + except subprocess.CalledProcessError as e: + log_error(f"Failed to open file with system application (exit code {e.returncode}): {e}") + QMessageBox.warning( + self, "Error", + f"Failed to open file with the system's default application.\n\nDetails: {e}" + ) + except subprocess.TimeoutExpired: + log_error(f"Timeout while opening file: {file_path}") + QMessageBox.warning( + self, "Timeout", + "The system application took too long to respond." + ) + except OSError as e: + log_error(f"OS error while opening file: {e}") + QMessageBox.warning( + self, "Error", + f"An operating system error occurred.\n\nDetails: {e}" + ) else: # Fallback for when Maestro is not available (testing outside Maestro) @@ -2498,7 +2593,7 @@ def init_ui(self): # Status self.status_label = QLabel("Not logged in") - self.status_label.setStyleSheet("color: #FF6B6B;") + self.status_label.setStyleSheet(f"color: {COLOR_ERROR};") main_layout.addWidget(self.status_label) self.setLayout(main_layout) @@ -2506,11 +2601,11 @@ def init_ui(self): def check_login_status(self): if self.api_client.is_configured() and self.api_client.test_connection(): self.status_label.setText("Logged in successfully") - self.status_label.setStyleSheet("color: #7FBA7A;") + self.status_label.setStyleSheet(f"color: {COLOR_SUCCESS};") self.submit_button.setEnabled(True) else: self.status_label.setText("Not logged in - click Settings") - self.status_label.setStyleSheet("color: #FF6B6B;") + self.status_label.setStyleSheet(f"color: {COLOR_ERROR};") self.submit_button.setEnabled(False) def open_settings(self): From 7ec6a27a8308440cb4ff03a4e0a8f3f5ea21094a Mon Sep 17 00:00:00 2001 From: pschmidtke Date: Fri, 6 Feb 2026 10:54:49 +0100 Subject: [PATCH 4/4] Fix test_connection breaking search functionality Revert to simpler approach: trust the token if we have one. The _request_with_retry method already handles re-authentication when tokens expire during actual API calls. Co-Authored-By: Claude Opus 4.5 --- threedecision_maestro_dir/api_client.py | 39 +++++++++---------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/threedecision_maestro_dir/api_client.py b/threedecision_maestro_dir/api_client.py index 82756ec..bc7669d 100644 --- a/threedecision_maestro_dir/api_client.py +++ b/threedecision_maestro_dir/api_client.py @@ -303,36 +303,23 @@ def login(self) -> bool: return False def test_connection(self) -> bool: - """Test API connection and validate authentication token""" + """Test API connection. + + If we have a token, we trust it's valid. The _request_with_retry method + will handle re-authentication if the token expires during actual API calls. + If no token exists, we attempt to login. + """ if not self.is_configured(): return False - # If no token, try to login first - if not (self.token and 'Authorization' in self.session.headers): - log_debug("No valid token found, attempting login") - return self.login() - - # We have a token - make a lightweight request to verify it's still valid - test_url = self.base_url.rstrip('/') + '/' - log_debug(f"Testing API connection with existing token against {test_url}") - try: - response = self.session.get(test_url, timeout=10) - - # If unauthorized, token is invalid - try to login again - if response.status_code in (401, 403): - log_debug(f"Token validation returned {response.status_code}, attempting re-login") - return self.login() + # If we have a token, trust it - _request_with_retry handles re-auth if needed + if self.token and 'Authorization' in self.session.headers: + log_debug("Using existing token for connection") + return True - if 200 <= response.status_code < 400: - log_debug("Connection test succeeded with existing token") - return True - - log_error(f"Connection test failed with status code: {response.status_code}") - return False - - except requests.RequestException as e: - log_error(f"Connection test failed due to network error: {e}") - return False + # No token - try to login + log_debug("No token found, attempting login") + return self.login() def submit_search(self, query: str) -> Optional[Dict[str, Any]]: """Submit a search query and return job info"""