Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cursor/commands/CHK_1_completion_analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Review our session against the original objectives:
- No code duplication
- Follows established patterns
- Proper error handling and logging
- Code lint checks fixed

### PLAN ADHERENCE AUDIT

Expand Down
12 changes: 12 additions & 0 deletions backend/app/api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
ai_chat,
app_configuration,
baseline_logs,
branch_comparison,
budget_summary,
budget_timeline,
change_order_line_items,
change_orders,
cost_categories,
cost_element_schedules,
cost_element_types,
Expand All @@ -19,14 +22,17 @@
evm_aggregation,
evm_indices,
forecasts,
hard_delete,
login,
planned_value,
private,
projects,
restore,
users,
utils,
variance_analysis_report,
variance_threshold_config,
version_history,
wbes,
)
from app.core.config import settings
Expand All @@ -38,6 +44,8 @@
api_router.include_router(app_configuration.router)
api_router.include_router(utils.router)
api_router.include_router(projects.router)
api_router.include_router(change_orders.router)
api_router.include_router(change_order_line_items.router)
api_router.include_router(wbes.router)
api_router.include_router(cost_elements.router)
api_router.include_router(cost_element_schedules.router)
Expand All @@ -55,9 +63,13 @@
api_router.include_router(evm_indices.router)
api_router.include_router(evm_aggregation.router)
api_router.include_router(baseline_logs.router)
api_router.include_router(branch_comparison.router)
api_router.include_router(cost_performance_report.router)
api_router.include_router(variance_analysis_report.router)
api_router.include_router(variance_threshold_config.router)
api_router.include_router(restore.router)
api_router.include_router(hard_delete.router)
api_router.include_router(version_history.router)


if settings.ENVIRONMENT == "local":
Expand Down
78 changes: 54 additions & 24 deletions backend/app/api/routes/app_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import Annotated, Any

from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session, select
from sqlmodel import Session, func, select

from app.api.deps import SessionDep, get_current_active_admin
from app.models import (
Expand All @@ -15,13 +15,24 @@
AppConfigurationUpdate,
Message,
)
from app.services.branch_filtering import apply_status_filters
from app.services.entity_versioning import (
create_entity_with_version,
soft_delete_entity,
update_entity_with_version,
)

router = APIRouter(prefix="/app-configuration", tags=["admin"])


def _get_app_configuration(session: Session, config_id: uuid.UUID) -> AppConfiguration:
def _get_app_configuration(
session: Session, config_id: uuid.UUID, include_inactive: bool = False
) -> AppConfiguration:
"""Get app configuration by ID."""
config = session.get(AppConfiguration, config_id)
statement = select(AppConfiguration).where(AppConfiguration.config_id == config_id)
if not include_inactive:
statement = apply_status_filters(statement, AppConfiguration, status="active")
config = session.exec(statement).first()
if not config:
raise HTTPException(status_code=404, detail="App configuration not found")
return config
Expand All @@ -33,12 +44,19 @@ def list_app_configurations(
_current_user: Annotated[Any, Depends(get_current_active_admin)],
) -> Any:
"""List all app configurations (admin only)."""
count_statement = select(func.count()).select_from(AppConfiguration)
count_statement = apply_status_filters(
count_statement, AppConfiguration, status="active"
)
count = session.exec(count_statement).one()

statement = select(AppConfiguration).order_by(
AppConfiguration.config_key, AppConfiguration.is_active.desc()
AppConfiguration.config_key, AppConfiguration.created_at.desc()
)
statement = apply_status_filters(statement, AppConfiguration, status="active")
configs = session.exec(statement).all()

return AppConfigurationsPublic(data=configs, count=len(configs))
return AppConfigurationsPublic(data=configs, count=count)


@router.get("/{config_id}", response_model=AppConfigurationPublic)
Expand All @@ -60,11 +78,11 @@ def create_app_configuration(
) -> Any:
"""Create a new app configuration (admin only)."""
# Check if config_key already exists
existing = session.exec(
select(AppConfiguration).where(
AppConfiguration.config_key == config_in.config_key
)
).first()
statement = select(AppConfiguration).where(
AppConfiguration.config_key == config_in.config_key
)
statement = apply_status_filters(statement, AppConfiguration, status="active")
existing = session.exec(statement).first()
if existing:
raise HTTPException(
status_code=400,
Expand All @@ -73,7 +91,12 @@ def create_app_configuration(

# Create new configuration
config = AppConfiguration.model_validate(config_in)
session.add(config)
config = create_entity_with_version(
session=session,
entity=config,
entity_type="app_configuration",
entity_id=str(config.config_id),
)
session.commit()
session.refresh(config)

Expand All @@ -95,24 +118,26 @@ def update_app_configuration(
config_update.config_key is not None
and config_update.config_key != config.config_key
):
existing = session.exec(
select(AppConfiguration).where(
AppConfiguration.config_key == config_update.config_key,
AppConfiguration.app_configuration_id != config_id,
)
).first()
statement = select(AppConfiguration).where(
AppConfiguration.config_key == config_update.config_key,
AppConfiguration.config_id != config_id,
)
statement = apply_status_filters(statement, AppConfiguration, status="active")
existing = session.exec(statement).first()
if existing:
raise HTTPException(
status_code=400,
detail=f"Configuration with key '{config_update.config_key}' already exists",
)

# Update configuration
update_data = config_update.model_dump(exclude_unset=True)
for field, value in update_data.items():
setattr(config, field, value)

session.add(config)
config = update_entity_with_version(
session=session,
entity_class=AppConfiguration,
entity_id=config_id,
update_data=update_data,
entity_type="app_configuration",
)
session.commit()
session.refresh(config)

Expand All @@ -126,8 +151,13 @@ def delete_app_configuration(
_current_user: Annotated[Any, Depends(get_current_active_admin)],
) -> Any:
"""Delete an app configuration (admin only)."""
config = _get_app_configuration(session, config_id)
session.delete(config)
_get_app_configuration(session, config_id)
soft_delete_entity(
session=session,
entity_class=AppConfiguration,
entity_id=config_id,
entity_type="app_configuration",
)
session.commit()

return Message(message="App configuration deleted successfully")
Loading
Loading