Fix Aktivitätenstatus
Dieser Commit ist enthalten in:
@ -5,9 +5,9 @@
|
|||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
- **Path**: `C:/Users/hendr/Desktop/IntelSight/ClaudeProjectManager-main`
|
- **Path**: `C:/Users/hendr/Desktop/IntelSight/ClaudeProjectManager-main`
|
||||||
- **Files**: 220 files
|
- **Files**: 227 files
|
||||||
- **Size**: 76.9 MB
|
- **Size**: 77.0 MB
|
||||||
- **Last Modified**: 2025-07-07 22:34
|
- **Last Modified**: 2025-07-08 08:19
|
||||||
|
|
||||||
## Technology Stack
|
## Technology Stack
|
||||||
|
|
||||||
@ -196,3 +196,4 @@ This project is managed with Claude Project Manager. To work with this project:
|
|||||||
- README updated on 2025-07-07 21:50:23
|
- README updated on 2025-07-07 21:50:23
|
||||||
- README updated on 2025-07-07 22:12:28
|
- README updated on 2025-07-07 22:12:28
|
||||||
- README updated on 2025-07-07 22:34:45
|
- README updated on 2025-07-07 22:34:45
|
||||||
|
- README updated on 2025-07-08 08:19:16
|
||||||
|
|||||||
@ -51,12 +51,12 @@
|
|||||||
"name": "ClaudeProjectManager-main",
|
"name": "ClaudeProjectManager-main",
|
||||||
"path": "C:/Users/hendr/Desktop/IntelSight/ClaudeProjectManager-main",
|
"path": "C:/Users/hendr/Desktop/IntelSight/ClaudeProjectManager-main",
|
||||||
"created_at": "2025-07-07T21:38:23.820122",
|
"created_at": "2025-07-07T21:38:23.820122",
|
||||||
"last_accessed": "2025-07-07T22:34:45.602409",
|
"last_accessed": "2025-07-08T08:19:16.600227",
|
||||||
"readme_path": "C:/Users/hendr/Desktop/IntelSight/ClaudeProjectManager-main\\CLAUDE_PROJECT_README.md",
|
"readme_path": "C:/Users/hendr/Desktop/IntelSight/ClaudeProjectManager-main\\CLAUDE_PROJECT_README.md",
|
||||||
"description": "",
|
"description": "",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"gitea_repo": null
|
"gitea_repo": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"last_updated": "2025-07-07T22:34:45.602409"
|
"last_updated": "2025-07-08T08:19:16.600227"
|
||||||
}
|
}
|
||||||
@ -2966,10 +2966,16 @@ class MainWindow:
|
|||||||
def update_activity_status(self):
|
def update_activity_status(self):
|
||||||
"""Periodic update of activity status"""
|
"""Periodic update of activity status"""
|
||||||
from services.activity_sync import activity_service
|
from services.activity_sync import activity_service
|
||||||
|
from utils.logger import logger
|
||||||
|
|
||||||
|
logger.debug("Periodic activity status update triggered")
|
||||||
|
|
||||||
if activity_service.connected:
|
if activity_service.connected:
|
||||||
# Update display with current activities
|
# Update display with current activities
|
||||||
|
logger.debug(f"Updating activity display with {len(activity_service.activities)} activities")
|
||||||
self.update_activity_display(activity_service.activities)
|
self.update_activity_display(activity_service.activities)
|
||||||
|
else:
|
||||||
|
logger.debug("Activity service not connected, skipping update")
|
||||||
|
|
||||||
# Schedule next update
|
# Schedule next update
|
||||||
self.root.after(5000, self.update_activity_status) # Update every 5 seconds
|
self.root.after(5000, self.update_activity_status) # Update every 5 seconds
|
||||||
|
|||||||
@ -25,7 +25,9 @@ class ProjectTile(ctk.CTkFrame):
|
|||||||
width=TILE_SIZE['width'],
|
width=TILE_SIZE['width'],
|
||||||
height=TILE_SIZE['height'],
|
height=TILE_SIZE['height'],
|
||||||
fg_color=COLORS['bg_vps'] if is_vps else COLORS['bg_tile'],
|
fg_color=COLORS['bg_vps'] if is_vps else COLORS['bg_tile'],
|
||||||
corner_radius=10
|
corner_radius=10,
|
||||||
|
border_width=3,
|
||||||
|
border_color=COLORS['bg_vps'] if is_vps else COLORS['bg_tile']
|
||||||
)
|
)
|
||||||
|
|
||||||
self.project = project
|
self.project = project
|
||||||
@ -38,6 +40,9 @@ class ProjectTile(ctk.CTkFrame):
|
|||||||
self.is_vps = is_vps
|
self.is_vps = is_vps
|
||||||
self.is_running = is_running
|
self.is_running = is_running
|
||||||
self.is_selected = False
|
self.is_selected = False
|
||||||
|
self.activity_animation_id = None
|
||||||
|
self.activity_pulse_value = 0
|
||||||
|
self.has_team_activity = False
|
||||||
|
|
||||||
self.grid_propagate(False)
|
self.grid_propagate(False)
|
||||||
self.setup_ui()
|
self.setup_ui()
|
||||||
@ -582,39 +587,52 @@ pause
|
|||||||
|
|
||||||
def _start_activity(self):
|
def _start_activity(self):
|
||||||
"""Start activity for this project"""
|
"""Start activity for this project"""
|
||||||
# Use main_window reference if available
|
from services.activity_sync import activity_service
|
||||||
if hasattr(self, 'main_window') and self.main_window:
|
|
||||||
self.main_window.start_activity(self.project)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Otherwise try to find in widget hierarchy
|
logger.info(f"Starting activity for project: {self.project.name}")
|
||||||
widget = self
|
|
||||||
while widget:
|
|
||||||
if hasattr(widget, 'start_activity'):
|
|
||||||
widget.start_activity(self.project)
|
|
||||||
return
|
|
||||||
widget = widget.master if hasattr(widget, 'master') else None
|
|
||||||
|
|
||||||
# If not found, log error
|
# Update UI optimistically immediately
|
||||||
logger.error("Could not find start_activity method in widget hierarchy")
|
self.update_activity_status(True, activity_service.user_name, True)
|
||||||
|
|
||||||
|
success = activity_service.start_activity(
|
||||||
|
self.project.name,
|
||||||
|
self.project.path,
|
||||||
|
self.project.description if hasattr(self.project, 'description') else ""
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Activity start result for {self.project.name}: success={success}")
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
# Revert on failure
|
||||||
|
logger.error(f"Failed to start activity for {self.project.name}, reverting UI")
|
||||||
|
self.update_activity_status(False)
|
||||||
|
from tkinter import messagebox
|
||||||
|
messagebox.showerror(
|
||||||
|
"Fehler",
|
||||||
|
"Aktivität konnte nicht gestartet werden."
|
||||||
|
)
|
||||||
|
|
||||||
def _stop_activity(self):
|
def _stop_activity(self):
|
||||||
"""Stop current activity"""
|
"""Stop current activity"""
|
||||||
# Use main_window reference if available
|
from services.activity_sync import activity_service
|
||||||
if hasattr(self, 'main_window') and self.main_window:
|
|
||||||
self.main_window.stop_activity()
|
|
||||||
return
|
|
||||||
|
|
||||||
# Otherwise try to find in widget hierarchy
|
logger.info(f"Stopping activity for project: {self.project.name}")
|
||||||
widget = self
|
|
||||||
while widget:
|
|
||||||
if hasattr(widget, 'stop_activity'):
|
|
||||||
widget.stop_activity()
|
|
||||||
return
|
|
||||||
widget = widget.master if hasattr(widget, 'master') else None
|
|
||||||
|
|
||||||
# If not found, log error
|
# Update UI optimistically immediately
|
||||||
logger.error("Could not find stop_activity method in widget hierarchy")
|
self.update_activity_status(False)
|
||||||
|
|
||||||
|
success = activity_service.stop_activity()
|
||||||
|
|
||||||
|
logger.info(f"Activity stop result: success={success}")
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
# Revert on failure - check if we're still the active project
|
||||||
|
current = activity_service.get_current_activity()
|
||||||
|
if current and current.get('projectName') == self.project.name:
|
||||||
|
logger.error(f"Failed to stop activity for {self.project.name}, reverting UI")
|
||||||
|
self.update_activity_status(True, activity_service.user_name, True)
|
||||||
|
else:
|
||||||
|
logger.error(f"Failed to stop activity, but no current activity found")
|
||||||
|
|
||||||
def _toggle_activity(self):
|
def _toggle_activity(self):
|
||||||
"""Toggle activity for this project"""
|
"""Toggle activity for this project"""
|
||||||
@ -653,7 +671,17 @@ pause
|
|||||||
|
|
||||||
def update_activity_status(self, is_active: bool = False, user_name: str = None, is_own_activity: bool = False):
|
def update_activity_status(self, is_active: bool = False, user_name: str = None, is_own_activity: bool = False):
|
||||||
"""Update activity indicator on tile"""
|
"""Update activity indicator on tile"""
|
||||||
|
logger.debug(f"update_activity_status called for {self.project.name}: is_active={is_active}, user_name={user_name}, is_own_activity={is_own_activity}")
|
||||||
|
|
||||||
if is_active:
|
if is_active:
|
||||||
|
# Start border animation for team activity
|
||||||
|
if not is_own_activity:
|
||||||
|
self.has_team_activity = True
|
||||||
|
self._start_activity_animation()
|
||||||
|
else:
|
||||||
|
self.has_team_activity = False
|
||||||
|
self._stop_activity_animation()
|
||||||
|
|
||||||
# Show indicator with appropriate color
|
# Show indicator with appropriate color
|
||||||
if is_own_activity:
|
if is_own_activity:
|
||||||
# Green for own activity
|
# Green for own activity
|
||||||
@ -680,6 +708,10 @@ pause
|
|||||||
# Hide indicator
|
# Hide indicator
|
||||||
self.activity_indicator.pack_forget()
|
self.activity_indicator.pack_forget()
|
||||||
|
|
||||||
|
# Stop border animation
|
||||||
|
self.has_team_activity = False
|
||||||
|
self._stop_activity_animation()
|
||||||
|
|
||||||
# Update activity button if exists
|
# Update activity button if exists
|
||||||
if hasattr(self, 'activity_button'):
|
if hasattr(self, 'activity_button'):
|
||||||
self.activity_button.configure(text="▶")
|
self.activity_button.configure(text="▶")
|
||||||
@ -725,7 +757,16 @@ pause
|
|||||||
"""Check if this project has active users"""
|
"""Check if this project has active users"""
|
||||||
from services.activity_sync import activity_service
|
from services.activity_sync import activity_service
|
||||||
|
|
||||||
# Get all activities for this project
|
logger.debug(f"check_activity called for project: {self.project.name}")
|
||||||
|
|
||||||
|
# First check if this is our own current activity
|
||||||
|
current_activity = activity_service.get_current_activity()
|
||||||
|
is_own_current = (current_activity and
|
||||||
|
current_activity.get('projectName') == self.project.name)
|
||||||
|
|
||||||
|
logger.debug(f"Current activity check - is_own_current: {is_own_current}, current_activity: {current_activity}")
|
||||||
|
|
||||||
|
# Get all activities for this project from server
|
||||||
active_users = []
|
active_users = []
|
||||||
is_own_activity = False
|
is_own_activity = False
|
||||||
has_other_users = False
|
has_other_users = False
|
||||||
@ -740,14 +781,77 @@ pause
|
|||||||
else:
|
else:
|
||||||
has_other_users = True
|
has_other_users = True
|
||||||
|
|
||||||
|
logger.debug(f"Server activities - active_users: {active_users}, is_own_activity: {is_own_activity}, has_other_users: {has_other_users}")
|
||||||
|
|
||||||
|
# If we have a local current activity, ensure it's included
|
||||||
|
if is_own_current:
|
||||||
|
is_own_activity = True
|
||||||
|
if activity_service.user_name not in active_users:
|
||||||
|
active_users.append(activity_service.user_name)
|
||||||
|
logger.debug(f"Added local user to active_users: {activity_service.user_name}")
|
||||||
|
|
||||||
if active_users:
|
if active_users:
|
||||||
# Show indicator with all active users
|
# Show indicator with all active users
|
||||||
user_text = ", ".join(active_users)
|
user_text = ", ".join(active_users)
|
||||||
# If both own and others are active, show as others (orange) to indicate collaboration
|
# If both own and others are active, show as others (orange) to indicate collaboration
|
||||||
self.update_activity_status(True, user_text, is_own_activity and not has_other_users)
|
final_is_own = is_own_activity and not has_other_users
|
||||||
|
logger.info(f"Updating activity status for {self.project.name}: active=True, users={user_text}, is_own={final_is_own}")
|
||||||
|
self.update_activity_status(True, user_text, final_is_own)
|
||||||
else:
|
else:
|
||||||
|
logger.info(f"Updating activity status for {self.project.name}: active=False")
|
||||||
self.update_activity_status(False)
|
self.update_activity_status(False)
|
||||||
|
|
||||||
|
def _start_activity_animation(self):
|
||||||
|
"""Start animated border for team activity"""
|
||||||
|
if self.activity_animation_id:
|
||||||
|
return # Animation already running
|
||||||
|
|
||||||
|
def animate():
|
||||||
|
if not self.has_team_activity:
|
||||||
|
self.activity_animation_id = None
|
||||||
|
return
|
||||||
|
|
||||||
|
# Calculate pulsing color
|
||||||
|
self.activity_pulse_value = (self.activity_pulse_value + 5) % 100
|
||||||
|
pulse = abs(50 - self.activity_pulse_value) / 50 # 0 to 1 pulsing
|
||||||
|
|
||||||
|
# Interpolate between orange and a brighter orange
|
||||||
|
base_color = COLORS['accent_warning'] # Orange
|
||||||
|
bright_factor = 0.3 + (0.7 * pulse) # Pulse between 0.3 and 1.0 brightness
|
||||||
|
|
||||||
|
# Create pulsing border color
|
||||||
|
if base_color.startswith('#'):
|
||||||
|
# Convert hex to RGB, apply brightness, convert back
|
||||||
|
r = int(base_color[1:3], 16)
|
||||||
|
g = int(base_color[3:5], 16)
|
||||||
|
b = int(base_color[5:7], 16)
|
||||||
|
|
||||||
|
# Apply brightness
|
||||||
|
r = min(255, int(r + (255 - r) * (1 - bright_factor)))
|
||||||
|
g = min(255, int(g + (255 - g) * (1 - bright_factor)))
|
||||||
|
b = min(255, int(b + (255 - b) * (1 - bright_factor)))
|
||||||
|
|
||||||
|
pulse_color = f"#{r:02x}{g:02x}{b:02x}"
|
||||||
|
else:
|
||||||
|
pulse_color = base_color
|
||||||
|
|
||||||
|
self.configure(border_color=pulse_color)
|
||||||
|
|
||||||
|
# Continue animation
|
||||||
|
self.activity_animation_id = self.after(50, animate)
|
||||||
|
|
||||||
|
animate()
|
||||||
|
|
||||||
|
def _stop_activity_animation(self):
|
||||||
|
"""Stop animated border"""
|
||||||
|
if self.activity_animation_id:
|
||||||
|
self.after_cancel(self.activity_animation_id)
|
||||||
|
self.activity_animation_id = None
|
||||||
|
|
||||||
|
# Reset border to default
|
||||||
|
default_color = COLORS['bg_vps'] if self.is_vps else COLORS['bg_tile']
|
||||||
|
self.configure(border_color=default_color)
|
||||||
|
|
||||||
class AddProjectTile(ctk.CTkFrame):
|
class AddProjectTile(ctk.CTkFrame):
|
||||||
"""Special tile for adding new projects"""
|
"""Special tile for adding new projects"""
|
||||||
|
|
||||||
|
|||||||
@ -85,10 +85,23 @@ class ActivitySyncService:
|
|||||||
@self.sio.event
|
@self.sio.event
|
||||||
def activities_update(data):
|
def activities_update(data):
|
||||||
"""Handle activities update from server"""
|
"""Handle activities update from server"""
|
||||||
self.activities = data
|
logger.debug(f"Received raw activities update: {len(data)} items")
|
||||||
logger.debug(f"Received activities update: {len(data)} activities")
|
|
||||||
|
# Filter out inactive entries and ensure we only keep active ones
|
||||||
|
active_activities = [
|
||||||
|
activity for activity in data
|
||||||
|
if activity.get('isActive', False)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Log details about the activities
|
||||||
|
for activity in active_activities:
|
||||||
|
logger.debug(f"Active: {activity.get('userName')} on {activity.get('projectName')}")
|
||||||
|
|
||||||
|
self.activities = active_activities
|
||||||
|
logger.info(f"Activities update: {len(active_activities)} active (of {len(data)} total)")
|
||||||
|
|
||||||
if self.on_activities_update:
|
if self.on_activities_update:
|
||||||
self.on_activities_update(data)
|
self.on_activities_update(active_activities)
|
||||||
|
|
||||||
@self.sio.event
|
@self.sio.event
|
||||||
def connect_error(data):
|
def connect_error(data):
|
||||||
@ -131,36 +144,57 @@ class ActivitySyncService:
|
|||||||
|
|
||||||
def start_activity(self, project_name: str, project_path: str, description: str = ""):
|
def start_activity(self, project_name: str, project_path: str, description: str = ""):
|
||||||
"""Start a new activity"""
|
"""Start a new activity"""
|
||||||
|
logger.debug(f"start_activity called for: {project_name}")
|
||||||
|
|
||||||
if not self.connected or not self.sio:
|
if not self.connected or not self.sio:
|
||||||
logger.warning("Not connected to activity server")
|
logger.warning("Not connected to activity server")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Set current activity immediately
|
||||||
|
self.current_activity = {
|
||||||
|
'projectName': project_name,
|
||||||
|
'projectPath': project_path,
|
||||||
|
'userId': self.user_id,
|
||||||
|
'userName': self.user_name,
|
||||||
|
'isActive': True
|
||||||
|
}
|
||||||
|
logger.debug(f"Set current_activity: {self.current_activity}")
|
||||||
|
|
||||||
|
# Emit to server
|
||||||
self.sio.emit('activity-start', {
|
self.sio.emit('activity-start', {
|
||||||
'projectName': project_name,
|
'projectName': project_name,
|
||||||
'projectPath': project_path,
|
'projectPath': project_path,
|
||||||
'description': description
|
'description': description
|
||||||
})
|
})
|
||||||
self.current_activity = {
|
|
||||||
'projectName': project_name,
|
|
||||||
'projectPath': project_path
|
|
||||||
}
|
|
||||||
logger.info(f"Started activity for project: {project_name}")
|
logger.info(f"Started activity for project: {project_name}")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to start activity: {e}")
|
logger.error(f"Failed to start activity: {e}")
|
||||||
|
self.current_activity = None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def stop_activity(self):
|
def stop_activity(self):
|
||||||
"""Stop the current activity"""
|
"""Stop the current activity"""
|
||||||
|
logger.debug(f"stop_activity called, current: {self.current_activity}")
|
||||||
|
|
||||||
if not self.connected or not self.sio:
|
if not self.connected or not self.sio:
|
||||||
logger.warning("Not connected to activity server")
|
logger.warning("Not connected to activity server")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.sio.emit('activity-stop')
|
# Store project name for logging
|
||||||
|
project_name = self.current_activity.get('projectName') if self.current_activity else 'None'
|
||||||
|
|
||||||
|
# Clear current activity immediately
|
||||||
self.current_activity = None
|
self.current_activity = None
|
||||||
logger.info("Stopped current activity")
|
logger.debug("Cleared current_activity")
|
||||||
|
|
||||||
|
# Emit to server
|
||||||
|
self.sio.emit('activity-stop')
|
||||||
|
|
||||||
|
logger.info(f"Stopped activity for project: {project_name}")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to stop activity: {e}")
|
logger.error(f"Failed to stop activity: {e}")
|
||||||
@ -179,7 +213,9 @@ class ActivitySyncService:
|
|||||||
)
|
)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
data = response.json()
|
data = response.json()
|
||||||
return data.get('activities', [])
|
all_activities = data.get('activities', [])
|
||||||
|
# Filter to only return active activities
|
||||||
|
return [a for a in all_activities if a.get('isActive', False)]
|
||||||
else:
|
else:
|
||||||
logger.error(f"Failed to fetch activities: {response.status_code}")
|
logger.error(f"Failed to fetch activities: {response.status_code}")
|
||||||
return []
|
return []
|
||||||
@ -204,17 +240,18 @@ class ActivitySyncService:
|
|||||||
|
|
||||||
def get_current_activity(self) -> Optional[Dict]:
|
def get_current_activity(self) -> Optional[Dict]:
|
||||||
"""Get current user's activity"""
|
"""Get current user's activity"""
|
||||||
|
logger.debug(f"get_current_activity called, returning: {self.current_activity}")
|
||||||
return self.current_activity
|
return self.current_activity
|
||||||
|
|
||||||
def _fetch_initial_activities(self):
|
def _fetch_initial_activities(self):
|
||||||
"""Fetch initial activities after connection"""
|
"""Fetch initial activities after connection"""
|
||||||
try:
|
try:
|
||||||
activities = self.get_activities()
|
activities = self.get_activities() # Already filtered to only active
|
||||||
if activities:
|
if activities:
|
||||||
self.activities = activities
|
self.activities = activities
|
||||||
if self.on_activities_update:
|
if self.on_activities_update:
|
||||||
self.on_activities_update(activities)
|
self.on_activities_update(activities)
|
||||||
logger.info(f"Fetched {len(activities)} initial activities")
|
logger.info(f"Fetched {len(activities)} active activities")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to fetch initial activities: {e}")
|
logger.error(f"Failed to fetch initial activities: {e}")
|
||||||
|
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren