Post Typen Verwalten + Strategy weight
This commit is contained in:
@@ -34,7 +34,7 @@ from src.services.email_service import (
|
||||
from src.services.background_jobs import (
|
||||
job_manager, JobType, JobStatus,
|
||||
run_post_scraping, run_profile_analysis, run_post_categorization, run_post_type_analysis,
|
||||
run_full_analysis_pipeline
|
||||
run_full_analysis_pipeline, run_post_recategorization
|
||||
)
|
||||
from src.services.storage_service import storage
|
||||
|
||||
@@ -2948,6 +2948,310 @@ async def employee_strategy_page(request: Request):
|
||||
})
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# EMPLOYEE POST TYPES MANAGEMENT
|
||||
# ============================================================================
|
||||
|
||||
@user_router.get("/post-types/manage")
|
||||
async def employee_post_types_page(request: Request):
|
||||
"""Employee post types management page with strategy weight configuration."""
|
||||
session = require_user_session(request)
|
||||
if not session:
|
||||
return RedirectResponse(url="/login", status_code=302)
|
||||
|
||||
if session.account_type != "employee":
|
||||
raise HTTPException(status_code=403, detail="Only employees can access this page")
|
||||
|
||||
try:
|
||||
user_id = UUID(session.user_id)
|
||||
|
||||
# Get all active post types for this employee
|
||||
post_types = await db.get_post_types(user_id, active_only=True)
|
||||
|
||||
# Count posts for each post type and convert to JSON-serializable format
|
||||
post_types_with_counts = []
|
||||
for pt in post_types:
|
||||
posts = await db.get_posts_by_type(user_id, pt.id)
|
||||
post_types_with_counts.append({
|
||||
"post_type": {
|
||||
"id": str(pt.id),
|
||||
"name": pt.name,
|
||||
"description": pt.description,
|
||||
"strategy_weight": pt.strategy_weight,
|
||||
"is_active": pt.is_active
|
||||
},
|
||||
"post_count": len(posts)
|
||||
})
|
||||
|
||||
# Check if company strategy exists
|
||||
company_strategy = None
|
||||
has_strategy = False
|
||||
if session.company_id:
|
||||
company_id = UUID(session.company_id)
|
||||
company = await db.get_company(company_id)
|
||||
if company and company.company_strategy:
|
||||
company_strategy = company.company_strategy
|
||||
has_strategy = True
|
||||
|
||||
profile_picture = await get_user_avatar(session, user_id)
|
||||
|
||||
# Convert to JSON string for JavaScript
|
||||
import json
|
||||
post_types_json = json.dumps(post_types_with_counts)
|
||||
|
||||
logger.info(f"Generated JSON for {len(post_types_with_counts)} post types")
|
||||
logger.info(f"JSON length: {len(post_types_json)} characters")
|
||||
logger.info(f"JSON preview: {post_types_json[:200] if len(post_types_json) > 200 else post_types_json}")
|
||||
|
||||
return templates.TemplateResponse("employee_post_types.html", {
|
||||
"request": request,
|
||||
"page": "post_types",
|
||||
"session": session,
|
||||
"post_types_with_counts": post_types_with_counts,
|
||||
"post_types_json": post_types_json,
|
||||
"has_strategy": has_strategy,
|
||||
"company_strategy": company_strategy,
|
||||
"profile_picture": profile_picture
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading employee post types page: {e}")
|
||||
profile_picture = await get_user_avatar(session, UUID(session.user_id))
|
||||
import json
|
||||
return templates.TemplateResponse("employee_post_types.html", {
|
||||
"request": request,
|
||||
"page": "post_types",
|
||||
"session": session,
|
||||
"post_types_with_counts": [],
|
||||
"post_types_json": json.dumps([]),
|
||||
"has_strategy": False,
|
||||
"company_strategy": None,
|
||||
"error": str(e),
|
||||
"profile_picture": profile_picture
|
||||
})
|
||||
|
||||
|
||||
@user_router.post("/api/employee/post-types")
|
||||
async def create_employee_post_type(request: Request, background_tasks: BackgroundTasks):
|
||||
"""Create a new post type for an employee."""
|
||||
session = require_user_session(request)
|
||||
if not session:
|
||||
return JSONResponse({"error": "Not authenticated"}, status_code=401)
|
||||
|
||||
if session.account_type != "employee":
|
||||
return JSONResponse({"error": "Only employees can create post types"}, status_code=403)
|
||||
|
||||
try:
|
||||
data = await request.json()
|
||||
user_id = UUID(session.user_id)
|
||||
|
||||
# Validate required fields
|
||||
name_raw = data.get("name")
|
||||
name = name_raw.strip() if name_raw else ""
|
||||
if not name or len(name) < 3:
|
||||
return JSONResponse({"error": "Name must be at least 3 characters"}, status_code=400)
|
||||
|
||||
description_raw = data.get("description")
|
||||
description = description_raw.strip() if description_raw else ""
|
||||
strategy_weight = float(data.get("strategy_weight", 0.5))
|
||||
|
||||
# Validate strategy_weight range
|
||||
if not (0.0 <= strategy_weight <= 1.0):
|
||||
return JSONResponse({"error": "Strategy weight must be between 0.0 and 1.0"}, status_code=400)
|
||||
|
||||
# Check if an inactive post type with this name already exists
|
||||
existing_inactive = None
|
||||
try:
|
||||
all_post_types = await db.get_post_types(user_id, active_only=False)
|
||||
existing_inactive = next((pt for pt in all_post_types if pt.name.lower() == name.lower() and not pt.is_active), None)
|
||||
except Exception as check_error:
|
||||
logger.warning(f"Could not check for inactive post types: {check_error}")
|
||||
|
||||
if existing_inactive:
|
||||
# Reactivate the existing post type instead of creating a new one
|
||||
await db.update_post_type(existing_inactive.id, {
|
||||
"is_active": True,
|
||||
"description": description if description else existing_inactive.description,
|
||||
"strategy_weight": strategy_weight
|
||||
})
|
||||
created_post_type = await db.get_post_type(existing_inactive.id)
|
||||
logger.info(f"Reactivated post type '{name}' for user {user_id}")
|
||||
else:
|
||||
# Create new post type
|
||||
from src.database.models import PostType
|
||||
post_type = PostType(
|
||||
user_id=user_id,
|
||||
name=name,
|
||||
description=description if description else None,
|
||||
strategy_weight=strategy_weight,
|
||||
is_active=True
|
||||
)
|
||||
|
||||
created_post_type = await db.create_post_type(post_type)
|
||||
logger.info(f"Created post type '{name}' for user {user_id}")
|
||||
|
||||
return JSONResponse({
|
||||
"success": True,
|
||||
"post_type": {
|
||||
"id": str(created_post_type.id),
|
||||
"name": created_post_type.name,
|
||||
"description": created_post_type.description,
|
||||
"strategy_weight": created_post_type.strategy_weight
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating post type: {e}")
|
||||
return JSONResponse({"error": str(e)}, status_code=500)
|
||||
|
||||
|
||||
@user_router.put("/api/employee/post-types/{post_type_id}")
|
||||
async def update_employee_post_type(request: Request, post_type_id: str):
|
||||
"""Update an existing post type."""
|
||||
session = require_user_session(request)
|
||||
if not session:
|
||||
return JSONResponse({"error": "Not authenticated"}, status_code=401)
|
||||
|
||||
if session.account_type != "employee":
|
||||
return JSONResponse({"error": "Only employees can update post types"}, status_code=403)
|
||||
|
||||
try:
|
||||
user_id = UUID(session.user_id)
|
||||
pt_id = UUID(post_type_id)
|
||||
|
||||
# Check ownership
|
||||
post_type = await db.get_post_type(pt_id)
|
||||
if not post_type or post_type.user_id != user_id:
|
||||
return JSONResponse({"error": "Post type not found or access denied"}, status_code=404)
|
||||
|
||||
data = await request.json()
|
||||
updates = {}
|
||||
|
||||
# Update name if provided
|
||||
if "name" in data:
|
||||
name_raw = data["name"]
|
||||
name = name_raw.strip() if name_raw else ""
|
||||
if not name or len(name) < 3:
|
||||
return JSONResponse({"error": "Name must be at least 3 characters"}, status_code=400)
|
||||
updates["name"] = name
|
||||
|
||||
# Update description if provided
|
||||
if "description" in data:
|
||||
desc_raw = data["description"]
|
||||
updates["description"] = desc_raw.strip() if desc_raw else None
|
||||
|
||||
# Update strategy_weight if provided
|
||||
if "strategy_weight" in data:
|
||||
strategy_weight = float(data["strategy_weight"])
|
||||
if not (0.0 <= strategy_weight <= 1.0):
|
||||
return JSONResponse({"error": "Strategy weight must be between 0.0 and 1.0"}, status_code=400)
|
||||
updates["strategy_weight"] = strategy_weight
|
||||
|
||||
# Apply updates
|
||||
if updates:
|
||||
await db.update_post_type(pt_id, updates)
|
||||
|
||||
return JSONResponse({"success": True})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating post type: {e}")
|
||||
return JSONResponse({"error": str(e)}, status_code=500)
|
||||
|
||||
|
||||
@user_router.delete("/api/employee/post-types/{post_type_id}")
|
||||
async def delete_employee_post_type(request: Request, post_type_id: str, background_tasks: BackgroundTasks):
|
||||
"""Soft delete a post type (set is_active = False)."""
|
||||
session = require_user_session(request)
|
||||
if not session:
|
||||
return JSONResponse({"error": "Not authenticated"}, status_code=401)
|
||||
|
||||
if session.account_type != "employee":
|
||||
return JSONResponse({"error": "Only employees can delete post types"}, status_code=403)
|
||||
|
||||
try:
|
||||
user_id = UUID(session.user_id)
|
||||
pt_id = UUID(post_type_id)
|
||||
|
||||
# Check ownership
|
||||
post_type = await db.get_post_type(pt_id)
|
||||
if not post_type or post_type.user_id != user_id:
|
||||
return JSONResponse({"error": "Post type not found or access denied"}, status_code=404)
|
||||
|
||||
# Count affected posts
|
||||
posts = await db.get_posts_by_type(user_id, pt_id)
|
||||
affected_count = len(posts)
|
||||
|
||||
# Soft delete
|
||||
await db.update_post_type(pt_id, {"is_active": False})
|
||||
|
||||
logger.info(f"Deleted post type '{post_type.name}' for user {user_id}")
|
||||
|
||||
return JSONResponse({
|
||||
"success": True,
|
||||
"affected_posts": affected_count
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting post type: {e}")
|
||||
return JSONResponse({"error": str(e)}, status_code=500)
|
||||
|
||||
|
||||
@user_router.post("/api/employee/post-types/save-all")
|
||||
async def save_all_and_reanalyze(request: Request, background_tasks: BackgroundTasks):
|
||||
"""Save all changes and conditionally trigger re-categorization based on structural changes."""
|
||||
session = require_user_session(request)
|
||||
if not session:
|
||||
return JSONResponse({"error": "Not authenticated"}, status_code=401)
|
||||
|
||||
if session.account_type != "employee":
|
||||
return JSONResponse({"error": "Only employees can trigger re-analysis"}, status_code=403)
|
||||
|
||||
try:
|
||||
data = await request.json()
|
||||
has_structural_changes = data.get("has_structural_changes", False)
|
||||
|
||||
user_id = UUID(session.user_id)
|
||||
user_id_str = str(user_id)
|
||||
|
||||
# Only trigger re-categorization and analysis if there were structural changes
|
||||
if has_structural_changes:
|
||||
# Create background job for post re-categorization (ALL posts)
|
||||
categorization_job = job_manager.create_job(
|
||||
job_type=JobType.POST_CATEGORIZATION,
|
||||
user_id=user_id_str
|
||||
)
|
||||
|
||||
# Create background job for post type analysis
|
||||
analysis_job = job_manager.create_job(
|
||||
job_type=JobType.POST_TYPE_ANALYSIS,
|
||||
user_id=user_id_str
|
||||
)
|
||||
|
||||
# Start background tasks
|
||||
background_tasks.add_task(run_post_recategorization, user_id, categorization_job.id)
|
||||
background_tasks.add_task(run_post_type_analysis, user_id, analysis_job.id)
|
||||
|
||||
logger.info(f"Started re-analysis jobs for user {user_id}: categorization={categorization_job.id}, analysis={analysis_job.id}")
|
||||
|
||||
return JSONResponse({
|
||||
"success": True,
|
||||
"recategorized": True,
|
||||
"categorization_job_id": str(categorization_job.id),
|
||||
"analysis_job_id": str(analysis_job.id)
|
||||
})
|
||||
else:
|
||||
logger.info(f"No structural changes for user {user_id}, skipping re-analysis")
|
||||
return JSONResponse({
|
||||
"success": True,
|
||||
"recategorized": False,
|
||||
"message": "Nur Weight-Updates, keine Rekategorisierung notwendig"
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in save-all: {e}")
|
||||
return JSONResponse({"error": str(e)}, status_code=500)
|
||||
|
||||
|
||||
@user_router.post("/api/company/invite")
|
||||
async def send_company_invitation(request: Request):
|
||||
"""Send invitation to a new employee."""
|
||||
|
||||
Reference in New Issue
Block a user