You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
160 lines
6.5 KiB
160 lines
6.5 KiB
from django.contrib import admin
|
|
from django.db import models
|
|
from django.shortcuts import redirect
|
|
from django.urls import reverse
|
|
# 1. Change TabularInline to StackedInline
|
|
from unfold.admin import ModelAdmin, TabularInline
|
|
from utils.admin import dovoodi_admin_site, project_admin_site
|
|
from .models import AgentSettings, AgentPrompt, EmbeddingSession
|
|
from django.utils.html import format_html
|
|
import requests
|
|
import threading
|
|
|
|
class AgentPromptInline(TabularInline):
|
|
model = AgentPrompt
|
|
extra = 0
|
|
fields = ('is_active', 'content')
|
|
|
|
formfield_overrides = {
|
|
models.TextField: {
|
|
'widget': admin.widgets.AdminTextareaWidget(attrs={
|
|
# 1. HEIGHT: Set to 1 row to keep it compact (it will expand if they type)
|
|
'rows': 2,
|
|
|
|
'class': (
|
|
'border border-gray-300 rounded-md shadow-sm '
|
|
'w-full block sm:text-sm '
|
|
'bg-white text-gray-900 '
|
|
'dark:bg-gray-900 dark:text-white dark:border-gray-700 '
|
|
'focus:ring-primary-500 focus:border-primary-500'
|
|
),
|
|
|
|
# 2. SIZE FIX:
|
|
# - background/color: inherit to keep your color fix
|
|
# - min-width: 600px to force the table column to be wide
|
|
# - width: 100% to fill that 600px+ space
|
|
'style': (
|
|
'background-color: inherit; '
|
|
'color: inherit; '
|
|
'width: 100%; '
|
|
'min-width: 700px; ' # Increased slightly for even better UX
|
|
'resize: vertical;'
|
|
)
|
|
})
|
|
},
|
|
}
|
|
class AgentSettingsAdmin(ModelAdmin):
|
|
# ... keep your existing permission and redirect logic ...
|
|
def has_add_permission(self, request): return False
|
|
def has_delete_permission(self, request, obj=None): return False
|
|
|
|
def changelist_view(self, request, extra_context=None):
|
|
obj, created = self.model.objects.get_or_create(pk=1)
|
|
url = reverse(
|
|
f"{self.admin_site.name}:{self.model._meta.app_label}_{self.model._meta.model_name}_change",
|
|
args=[obj.pk]
|
|
)
|
|
return redirect(url)
|
|
|
|
# 2. Add the Stacked Inline
|
|
inlines = [AgentPromptInline]
|
|
|
|
|
|
|
|
class EmbeddingSessionAdmin(ModelAdmin):
|
|
# What to show in the table view
|
|
list_display = ['id', 'status_badge', 'progress_bar', 'created_at']
|
|
|
|
# We make everything read-only so admins can't fake the progress
|
|
readonly_fields = ['status', 'progress', 'processed_items', 'total_items', 'error_message']
|
|
|
|
# Optional: If you want to customize how the error message text area looks in Unfold
|
|
formfield_overrides = {
|
|
models.TextField: {
|
|
'widget': admin.widgets.AdminTextareaWidget(attrs={
|
|
'rows': 4,
|
|
'class': (
|
|
'border border-gray-300 rounded-md shadow-sm '
|
|
'w-full block sm:text-sm '
|
|
'bg-white text-gray-900 '
|
|
'dark:bg-gray-900 dark:text-white dark:border-gray-700 '
|
|
'focus:ring-primary-500 focus:border-primary-500'
|
|
),
|
|
'style': 'background-color: inherit; color: inherit; width: 100%;'
|
|
})
|
|
},
|
|
}
|
|
|
|
@admin.display(description="Status")
|
|
def status_badge(self, obj):
|
|
"""Creates a beautiful Unfold/Tailwind badge based on the status."""
|
|
colors = {
|
|
'PENDING': 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300',
|
|
'PROCESSING': 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300',
|
|
'COMPLETED': 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300',
|
|
'FAILED': 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300',
|
|
}
|
|
color_classes = colors.get(obj.status, colors['PENDING'])
|
|
|
|
return format_html(
|
|
'<span class="px-2.5 py-1 inline-flex text-xs leading-5 font-semibold rounded-full {}">'
|
|
'{}'
|
|
'</span>',
|
|
color_classes,
|
|
obj.status
|
|
)
|
|
|
|
@admin.display(description="Progress")
|
|
def progress_bar(self, obj):
|
|
"""Creates a Tailwind progress bar to show sync status with percentages."""
|
|
if obj.status == 'PENDING':
|
|
return format_html('<span class="text-sm text-gray-500 dark:text-gray-400">Waiting to start...</span>')
|
|
|
|
return format_html(
|
|
'''
|
|
<div class="flex flex-col gap-1 w-64">
|
|
<div class="w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700">
|
|
<div class="bg-primary-600 h-2 rounded-full transition-all duration-500" style="width: {}%"></div>
|
|
</div>
|
|
|
|
<div class="flex justify-between text-xs text-gray-500 dark:text-gray-400">
|
|
<span class="font-bold text-gray-700 dark:text-gray-300">{}%</span>
|
|
<span>{} / {} items</span>
|
|
</div>
|
|
</div>
|
|
''',
|
|
obj.progress, # For the CSS width inside the style="" tag
|
|
obj.progress, # For the text percentage display
|
|
obj.processed_items, # For the items count
|
|
obj.total_items # For the total count
|
|
)
|
|
|
|
def save_model(self, request, obj, form, change):
|
|
"""
|
|
Intercept the save. If it's a new record, tell the FastAPI agent to start working.
|
|
"""
|
|
is_new = obj.pk is None
|
|
super().save_model(request, obj, form, change)
|
|
|
|
if is_new:
|
|
# 🟢 Trigger the FastAPI Agent in the background
|
|
def fire_and_forget():
|
|
try:
|
|
# Point this to your FastAPI Agent URL
|
|
# Make sure host.docker.internal works, or use the agent's container name
|
|
requests.post(
|
|
"http://127.0.0.1:8081/api/sync-knowledge",
|
|
json={"session_id": obj.id},
|
|
timeout=5
|
|
)
|
|
except Exception as e:
|
|
print(f"Failed to trigger agent: {e}")
|
|
|
|
threading.Thread(target=fire_and_forget).start()
|
|
|
|
# Register to your custom Unfold admin sites
|
|
dovoodi_admin_site.register(EmbeddingSession, EmbeddingSessionAdmin)
|
|
project_admin_site.register(EmbeddingSession, EmbeddingSessionAdmin)
|
|
|
|
dovoodi_admin_site.register(AgentSettings, AgentSettingsAdmin)
|
|
project_admin_site.register(AgentSettings, AgentSettingsAdmin)
|