This commit is contained in:
Iliyan Angelov
2025-11-26 22:32:20 +02:00
commit ed94dd22dd
150 changed files with 14058 additions and 0 deletions

246
osint/admin.py Normal file
View File

@@ -0,0 +1,246 @@
"""
Admin configuration for osint app.
"""
from django.contrib import admin
from django.utils.html import format_html
from django.urls import reverse
from django.utils import timezone
from datetime import timedelta
from .models import (
OSINTTask, OSINTResult, OSINTConfiguration,
SeedWebsite, OSINTKeyword, CrawledContent, AutoGeneratedReport
)
@admin.register(OSINTTask)
class OSINTTaskAdmin(admin.ModelAdmin):
"""OSINT task admin."""
list_display = ('report', 'task_type', 'status', 'created_at', 'completed_at')
list_filter = ('task_type', 'status', 'created_at')
search_fields = ('report__title', 'error_message')
readonly_fields = ('created_at', 'started_at', 'completed_at')
date_hierarchy = 'created_at'
@admin.register(OSINTResult)
class OSINTResultAdmin(admin.ModelAdmin):
"""OSINT result admin."""
list_display = ('report', 'source', 'data_type', 'confidence_level', 'is_verified', 'collected_at')
list_filter = ('data_type', 'is_verified', 'collected_at')
search_fields = ('report__title', 'source')
readonly_fields = ('collected_at', 'updated_at')
date_hierarchy = 'collected_at'
@admin.register(OSINTConfiguration)
class OSINTConfigurationAdmin(admin.ModelAdmin):
"""OSINT configuration admin."""
list_display = ('service_name', 'is_active', 'rate_limit', 'updated_at')
list_filter = ('is_active',)
search_fields = ('service_name',)
@admin.register(SeedWebsite)
class SeedWebsiteAdmin(admin.ModelAdmin):
"""Seed website admin."""
list_display = ('name', 'url', 'is_active', 'priority', 'last_crawled_at', 'pages_crawled', 'matches_found', 'status_indicator')
list_filter = ('is_active', 'priority', 'created_at')
search_fields = ('name', 'url', 'description')
readonly_fields = ('last_crawled_at', 'pages_crawled', 'matches_found', 'created_at', 'updated_at')
fieldsets = (
('Basic Information', {
'fields': ('name', 'url', 'description', 'is_active', 'priority', 'created_by')
}),
('Crawling Configuration', {
'fields': ('crawl_depth', 'crawl_interval_hours', 'allowed_domains', 'user_agent')
}),
('Statistics', {
'fields': ('last_crawled_at', 'pages_crawled', 'matches_found'),
'classes': ('collapse',)
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
date_hierarchy = 'created_at'
def status_indicator(self, obj):
"""Show visual status indicator."""
if not obj.is_active:
return format_html('<span style="color: red;">●</span> Inactive')
if not obj.last_crawled_at:
return format_html('<span style="color: orange;">●</span> Never Crawled')
hours_since = (timezone.now() - obj.last_crawled_at).total_seconds() / 3600
if hours_since > obj.crawl_interval_hours * 2:
return format_html('<span style="color: orange;">●</span> Overdue')
elif hours_since > obj.crawl_interval_hours:
return format_html('<span style="color: yellow;">●</span> Due Soon')
else:
return format_html('<span style="color: green;">●</span> Up to Date')
status_indicator.short_description = 'Status'
def save_model(self, request, obj, form, change):
if not change: # New object
obj.created_by = request.user
super().save_model(request, obj, form, change)
@admin.register(OSINTKeyword)
class OSINTKeywordAdmin(admin.ModelAdmin):
"""OSINT keyword admin."""
list_display = ('name', 'keyword', 'keyword_type', 'is_active', 'confidence_score', 'auto_approve', 'match_count')
list_filter = ('is_active', 'keyword_type', 'auto_approve', 'created_at')
search_fields = ('name', 'keyword', 'description')
readonly_fields = ('created_at', 'updated_at', 'match_count')
fieldsets = (
('Basic Information', {
'fields': ('name', 'keyword', 'description', 'keyword_type', 'is_active', 'created_by')
}),
('Matching Configuration', {
'fields': ('case_sensitive', 'confidence_score', 'auto_approve')
}),
('Statistics', {
'fields': ('match_count',),
'classes': ('collapse',)
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
date_hierarchy = 'created_at'
def match_count(self, obj):
"""Count how many times this keyword has matched."""
return obj.matched_contents.count()
match_count.short_description = 'Total Matches'
def save_model(self, request, obj, form, change):
if not change: # New object
obj.created_by = request.user
super().save_model(request, obj, form, change)
@admin.register(CrawledContent)
class CrawledContentAdmin(admin.ModelAdmin):
"""Crawled content admin."""
list_display = ('title', 'url', 'seed_website', 'match_count', 'confidence_score', 'has_potential_scam', 'crawled_at')
list_filter = ('has_potential_scam', 'seed_website', 'crawled_at', 'http_status')
search_fields = ('title', 'url', 'content')
readonly_fields = ('crawled_at', 'content_hash', 'http_status')
fieldsets = (
('Content Information', {
'fields': ('seed_website', 'url', 'title', 'content', 'html_content')
}),
('Analysis', {
'fields': ('matched_keywords', 'match_count', 'confidence_score', 'has_potential_scam')
}),
('Metadata', {
'fields': ('http_status', 'content_hash', 'crawled_at'),
'classes': ('collapse',)
}),
)
date_hierarchy = 'crawled_at'
filter_horizontal = ('matched_keywords',)
def get_queryset(self, request):
return super().get_queryset(request).select_related('seed_website').prefetch_related('matched_keywords')
@admin.register(AutoGeneratedReport)
class AutoGeneratedReportAdmin(admin.ModelAdmin):
"""Auto-generated report admin."""
list_display = ('title', 'source_url', 'status', 'confidence_score', 'reviewed_by', 'reviewed_at', 'view_report_link')
list_filter = ('status', 'confidence_score', 'created_at', 'reviewed_at')
search_fields = ('title', 'description', 'source_url')
readonly_fields = ('crawled_content', 'created_at', 'updated_at', 'published_at')
fieldsets = (
('Report Information', {
'fields': ('crawled_content', 'title', 'description', 'source_url')
}),
('Analysis', {
'fields': ('matched_keywords', 'confidence_score')
}),
('Review', {
'fields': ('status', 'review_notes', 'reviewed_by', 'reviewed_at', 'report')
}),
('Publication', {
'fields': ('published_at',),
'classes': ('collapse',)
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
date_hierarchy = 'created_at'
filter_horizontal = ('matched_keywords',)
actions = ['approve_reports', 'reject_reports', 'publish_reports']
def view_report_link(self, obj):
"""Link to the generated report if exists."""
if obj.report:
url = reverse('admin:reports_scamreport_change', args=[obj.report.pk])
return format_html('<a href="{}">View Report #{}</a>', url, obj.report.pk)
return '-'
view_report_link.short_description = 'Linked Report'
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'crawled_content', 'reviewed_by', 'report'
).prefetch_related('matched_keywords')
@admin.action(description='Approve selected reports')
def approve_reports(self, request, queryset):
"""Approve selected auto-generated reports."""
from django.utils import timezone
updated = queryset.filter(status='pending').update(
status='approved',
reviewed_by=request.user,
reviewed_at=timezone.now()
)
self.message_user(request, f'{updated} reports approved.')
@admin.action(description='Reject selected reports')
def reject_reports(self, request, queryset):
"""Reject selected auto-generated reports."""
from django.utils import timezone
updated = queryset.filter(status='pending').update(
status='rejected',
reviewed_by=request.user,
reviewed_at=timezone.now()
)
self.message_user(request, f'{updated} reports rejected.')
@admin.action(description='Publish selected reports')
def publish_reports(self, request, queryset):
"""Publish approved reports."""
from django.utils import timezone
from reports.models import ScamReport
from reports.models import ScamTag
published = 0
for auto_report in queryset.filter(status='approved'):
if not auto_report.report:
# Create the actual scam report
report = ScamReport.objects.create(
title=auto_report.title,
description=auto_report.description,
reported_url=auto_report.source_url,
scam_type='other', # Default type
status='verified', # Auto-verified since reviewed
verification_score=auto_report.confidence_score,
is_public=True,
is_anonymous=True, # System-generated
is_auto_discovered=True, # Mark as auto-discovered
reporter_ip=None, # System-generated
)
auto_report.report = report
auto_report.status = 'published'
auto_report.published_at = timezone.now()
auto_report.save()
published += 1
self.message_user(request, f'{published} reports published.')