Fix variable indicators and add project caching
This commit is contained in:
parent
79c2f3c944
commit
51263816f3
@ -42,25 +42,44 @@ class ProjectManager:
|
|||||||
self.projects_file = self.data_dir / 'requests.json'
|
self.projects_file = self.data_dir / 'requests.json'
|
||||||
self._ensure_data_dir()
|
self._ensure_data_dir()
|
||||||
|
|
||||||
|
# In-memory cache for projects to reduce disk I/O
|
||||||
|
self._projects_cache: Optional[List[Project]] = None
|
||||||
|
|
||||||
def _ensure_data_dir(self):
|
def _ensure_data_dir(self):
|
||||||
"""Create data directory if it doesn't exist."""
|
"""Create data directory if it doesn't exist."""
|
||||||
self.data_dir.mkdir(parents=True, exist_ok=True)
|
self.data_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
def load_projects(self) -> List[Project]:
|
def load_projects(self, force_reload: bool = False) -> List[Project]:
|
||||||
"""Load projects from JSON file."""
|
"""
|
||||||
|
Load projects from JSON file with in-memory caching.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
force_reload: If True, bypass cache and reload from disk
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of Project objects
|
||||||
|
"""
|
||||||
|
# Return cached data if available and not forcing reload
|
||||||
|
if self._projects_cache is not None and not force_reload:
|
||||||
|
return self._projects_cache
|
||||||
|
|
||||||
|
# Load from disk
|
||||||
if not self.projects_file.exists():
|
if not self.projects_file.exists():
|
||||||
|
self._projects_cache = []
|
||||||
return []
|
return []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(self.projects_file, 'r') as f:
|
with open(self.projects_file, 'r') as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
return [Project.from_dict(p) for p in data.get('projects', [])]
|
self._projects_cache = [Project.from_dict(p) for p in data.get('projects', [])]
|
||||||
|
return self._projects_cache
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error loading projects: {e}")
|
logger.error(f"Error loading projects: {e}")
|
||||||
|
self._projects_cache = []
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def save_projects(self, projects: List[Project]):
|
def save_projects(self, projects: List[Project]):
|
||||||
"""Save projects to JSON file."""
|
"""Save projects to JSON file and update cache."""
|
||||||
try:
|
try:
|
||||||
data = {
|
data = {
|
||||||
'version': 1,
|
'version': 1,
|
||||||
@ -68,6 +87,9 @@ class ProjectManager:
|
|||||||
}
|
}
|
||||||
with open(self.projects_file, 'w') as f:
|
with open(self.projects_file, 'w') as f:
|
||||||
json.dump(data, f, indent=2)
|
json.dump(data, f, indent=2)
|
||||||
|
|
||||||
|
# Update cache with the saved data
|
||||||
|
self._projects_cache = projects
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error saving projects: {e}")
|
logger.error(f"Error saving projects: {e}")
|
||||||
|
|
||||||
|
|||||||
@ -60,6 +60,7 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
self.env_separator: Optional[Gtk.Separator] = None
|
self.env_separator: Optional[Gtk.Separator] = None
|
||||||
self.undefined_variables: Set[str] = set()
|
self.undefined_variables: Set[str] = set()
|
||||||
self._update_indicators_timeout_id: Optional[int] = None
|
self._update_indicators_timeout_id: Optional[int] = None
|
||||||
|
self._is_programmatically_changing_environment: bool = False
|
||||||
|
|
||||||
# Build the UI
|
# Build the UI
|
||||||
self._build_ui()
|
self._build_ui()
|
||||||
@ -819,6 +820,10 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
if request.syntax in syntax_options:
|
if request.syntax in syntax_options:
|
||||||
self.body_language_dropdown.set_selected(syntax_options.index(request.syntax))
|
self.body_language_dropdown.set_selected(syntax_options.index(request.syntax))
|
||||||
|
|
||||||
|
# Update variable indicators after loading the request (if project is set)
|
||||||
|
if self.project_id:
|
||||||
|
self._update_variable_indicators()
|
||||||
|
|
||||||
def get_request(self) -> HttpRequest:
|
def get_request(self) -> HttpRequest:
|
||||||
"""Build and return HttpRequest from current UI state."""
|
"""Build and return HttpRequest from current UI state."""
|
||||||
method = self.method_dropdown.get_selected_item().get_string()
|
method = self.method_dropdown.get_selected_item().get_string()
|
||||||
@ -1152,8 +1157,8 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
self.environment_dropdown.set_enable_search(False)
|
self.environment_dropdown.set_enable_search(False)
|
||||||
self.environment_dropdown.set_tooltip_text("Select environment for variable substitution")
|
self.environment_dropdown.set_tooltip_text("Select environment for variable substitution")
|
||||||
|
|
||||||
# Connect signal
|
# Connect signal handler
|
||||||
self.environment_dropdown.connect("notify::selected", self._on_environment_changed)
|
self._env_change_handler_id = self.environment_dropdown.connect("notify::selected", self._on_environment_changed)
|
||||||
|
|
||||||
# Add to container at the beginning (left side)
|
# Add to container at the beginning (left side)
|
||||||
container.prepend(self.environment_dropdown)
|
container.prepend(self.environment_dropdown)
|
||||||
@ -1178,31 +1183,40 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
if not project:
|
if not project:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Build string list with "None" + environment names
|
# Set flag to indicate we're programmatically changing the dropdown
|
||||||
string_list = Gtk.StringList()
|
# This prevents the signal handler from triggering indicator updates during initialization
|
||||||
string_list.append("None")
|
self._is_programmatically_changing_environment = True
|
||||||
|
|
||||||
# Track environment IDs (index 0 is None)
|
try:
|
||||||
self.environment_ids = [None]
|
# Build string list with "None" + environment names
|
||||||
|
string_list = Gtk.StringList()
|
||||||
|
string_list.append("None")
|
||||||
|
|
||||||
for env in project.environments:
|
# Track environment IDs (index 0 is None)
|
||||||
string_list.append(env.name)
|
self.environment_ids = [None]
|
||||||
self.environment_ids.append(env.id)
|
|
||||||
|
|
||||||
self.environment_dropdown.set_model(string_list)
|
for env in project.environments:
|
||||||
|
string_list.append(env.name)
|
||||||
|
self.environment_ids.append(env.id)
|
||||||
|
|
||||||
# Select current environment
|
self.environment_dropdown.set_model(string_list)
|
||||||
if self.selected_environment_id:
|
|
||||||
try:
|
# Select current environment
|
||||||
index = self.environment_ids.index(self.selected_environment_id)
|
if self.selected_environment_id:
|
||||||
self.environment_dropdown.set_selected(index)
|
try:
|
||||||
except ValueError:
|
index = self.environment_ids.index(self.selected_environment_id)
|
||||||
|
self.environment_dropdown.set_selected(index)
|
||||||
|
except ValueError:
|
||||||
|
self.environment_dropdown.set_selected(0) # Default to "None"
|
||||||
|
else:
|
||||||
self.environment_dropdown.set_selected(0) # Default to "None"
|
self.environment_dropdown.set_selected(0) # Default to "None"
|
||||||
else:
|
|
||||||
self.environment_dropdown.set_selected(0) # Default to "None"
|
|
||||||
|
|
||||||
# Update indicators after environment is set
|
finally:
|
||||||
self._update_variable_indicators()
|
# Always clear the flag
|
||||||
|
self._is_programmatically_changing_environment = False
|
||||||
|
|
||||||
|
# Note: Don't update indicators here as the request might not be loaded yet
|
||||||
|
# Indicators will be updated when environment changes or request is loaded
|
||||||
|
|
||||||
def _show_environment_selector(self):
|
def _show_environment_selector(self):
|
||||||
"""Show environment selector (create if it doesn't exist)."""
|
"""Show environment selector (create if it doesn't exist)."""
|
||||||
@ -1226,17 +1240,29 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
# Populate if project_manager is available
|
# Populate if project_manager is available
|
||||||
if self.project_manager:
|
if self.project_manager:
|
||||||
self._populate_environment_dropdown()
|
self._populate_environment_dropdown()
|
||||||
|
# Update indicators after environment selector is shown
|
||||||
|
self._update_variable_indicators()
|
||||||
|
|
||||||
def _on_environment_changed(self, dropdown, _param):
|
def _on_environment_changed(self, dropdown, _param):
|
||||||
"""Handle environment selection change."""
|
"""Handle environment selection change."""
|
||||||
|
# If we're programmatically changing the environment during population, skip this
|
||||||
|
if self._is_programmatically_changing_environment:
|
||||||
|
return
|
||||||
|
|
||||||
if not hasattr(self, 'environment_ids'):
|
if not hasattr(self, 'environment_ids'):
|
||||||
|
logger.warning("Environment changed but environment_ids not initialized")
|
||||||
return
|
return
|
||||||
|
|
||||||
selected_index = dropdown.get_selected()
|
selected_index = dropdown.get_selected()
|
||||||
|
|
||||||
if selected_index < len(self.environment_ids):
|
if selected_index < len(self.environment_ids):
|
||||||
self.selected_environment_id = self.environment_ids[selected_index]
|
self.selected_environment_id = self.environment_ids[selected_index]
|
||||||
# Update visual indicators when environment changes
|
else:
|
||||||
self._update_variable_indicators()
|
logger.warning(f"Environment index {selected_index} out of range (max {len(self.environment_ids)-1})")
|
||||||
|
self.selected_environment_id = None
|
||||||
|
|
||||||
|
# Always update visual indicators when environment changes
|
||||||
|
self._update_variable_indicators()
|
||||||
|
|
||||||
def get_selected_environment(self):
|
def get_selected_environment(self):
|
||||||
"""Get the currently selected environment object."""
|
"""Get the currently selected environment object."""
|
||||||
@ -1303,6 +1329,10 @@ class RequestTabWidget(Gtk.Box):
|
|||||||
|
|
||||||
def _update_variable_indicators(self):
|
def _update_variable_indicators(self):
|
||||||
"""Update visual indicators for undefined variables."""
|
"""Update visual indicators for undefined variables."""
|
||||||
|
# Only update if we have a project (variables only make sense in project context)
|
||||||
|
if not self.project_id:
|
||||||
|
return
|
||||||
|
|
||||||
# Detect undefined variables
|
# Detect undefined variables
|
||||||
self.undefined_variables = self._detect_undefined_variables()
|
self.undefined_variables = self._detect_undefined_variables()
|
||||||
|
|
||||||
|
|||||||
@ -64,8 +64,11 @@ class VariableSubstitution:
|
|||||||
var_name = match.group(1)
|
var_name = match.group(1)
|
||||||
if var_name in variables:
|
if var_name in variables:
|
||||||
value = variables[var_name]
|
value = variables[var_name]
|
||||||
# Replace with value or empty string if value is None/empty
|
# Check if value is empty/None - treat as undefined for warning purposes
|
||||||
return value if value else ""
|
if not value:
|
||||||
|
undefined_vars.append(var_name)
|
||||||
|
return ""
|
||||||
|
return value
|
||||||
else:
|
else:
|
||||||
# Variable not defined - track it and replace with empty string
|
# Variable not defined - track it and replace with empty string
|
||||||
undefined_vars.append(var_name)
|
undefined_vars.append(var_name)
|
||||||
|
|||||||
@ -950,7 +950,8 @@ class RosterWindow(Adw.ApplicationWindow):
|
|||||||
|
|
||||||
def _on_manage_environments(self, widget, project):
|
def _on_manage_environments(self, widget, project):
|
||||||
"""Show environments management dialog."""
|
"""Show environments management dialog."""
|
||||||
# Reload project from disk to get latest data (including variables created by scripts)
|
# Get latest project data (includes variables created by scripts)
|
||||||
|
# Note: load_projects() uses cached data that's kept up-to-date by save operations
|
||||||
projects = self.project_manager.load_projects()
|
projects = self.project_manager.load_projects()
|
||||||
fresh_project = None
|
fresh_project = None
|
||||||
for p in projects:
|
for p in projects:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user