mirror of
https://github.com/ArchiveBox/ArchiveBox.git
synced 2026-04-04 23:07:56 +10:00
wip major changes
This commit is contained in:
@@ -3,9 +3,77 @@
|
||||
__package__ = 'archivebox.base_models'
|
||||
|
||||
from django.contrib import admin
|
||||
from django.utils.html import format_html, mark_safe
|
||||
from django_object_actions import DjangoObjectActions
|
||||
|
||||
|
||||
class ConfigEditorMixin:
|
||||
"""
|
||||
Mixin for admin classes with a config JSON field.
|
||||
|
||||
Provides a readonly field that shows available config options
|
||||
from all discovered plugin schemas.
|
||||
"""
|
||||
|
||||
@admin.display(description='Available Config Options')
|
||||
def available_config_options(self, obj):
|
||||
"""Show documentation for available config keys."""
|
||||
try:
|
||||
from archivebox.hooks import discover_plugin_configs
|
||||
plugin_configs = discover_plugin_configs()
|
||||
except ImportError:
|
||||
return format_html('<i>Plugin config system not available</i>')
|
||||
|
||||
html_parts = [
|
||||
'<details>',
|
||||
'<summary style="cursor: pointer; font-weight: bold; padding: 4px;">',
|
||||
'Click to see available config keys ({})</summary>'.format(
|
||||
sum(len(s.get('properties', {})) for s in plugin_configs.values())
|
||||
),
|
||||
'<div style="max-height: 400px; overflow-y: auto; padding: 8px; background: #f8f8f8; border-radius: 4px; font-family: monospace; font-size: 11px;">',
|
||||
]
|
||||
|
||||
for plugin_name, schema in sorted(plugin_configs.items()):
|
||||
properties = schema.get('properties', {})
|
||||
if not properties:
|
||||
continue
|
||||
|
||||
html_parts.append(f'<div style="margin: 8px 0;"><strong style="color: #333;">{plugin_name}</strong></div>')
|
||||
html_parts.append('<table style="width: 100%; border-collapse: collapse; margin-bottom: 12px;">')
|
||||
html_parts.append('<tr style="background: #eee;"><th style="text-align: left; padding: 4px;">Key</th><th style="text-align: left; padding: 4px;">Type</th><th style="text-align: left; padding: 4px;">Default</th><th style="text-align: left; padding: 4px;">Description</th></tr>')
|
||||
|
||||
for key, prop in sorted(properties.items()):
|
||||
prop_type = prop.get('type', 'string')
|
||||
default = prop.get('default', '')
|
||||
description = prop.get('description', '')
|
||||
|
||||
# Truncate long defaults
|
||||
default_str = str(default)
|
||||
if len(default_str) > 30:
|
||||
default_str = default_str[:27] + '...'
|
||||
|
||||
html_parts.append(
|
||||
f'<tr style="border-bottom: 1px solid #ddd;">'
|
||||
f'<td style="padding: 4px; font-weight: bold;">{key}</td>'
|
||||
f'<td style="padding: 4px; color: #666;">{prop_type}</td>'
|
||||
f'<td style="padding: 4px; color: #666;">{default_str}</td>'
|
||||
f'<td style="padding: 4px;">{description}</td>'
|
||||
f'</tr>'
|
||||
)
|
||||
|
||||
html_parts.append('</table>')
|
||||
|
||||
html_parts.append('</div></details>')
|
||||
html_parts.append(
|
||||
'<p style="margin-top: 8px; color: #666; font-size: 11px;">'
|
||||
'<strong>Usage:</strong> Add key-value pairs in JSON format, e.g., '
|
||||
'<code>{"SAVE_WGET": false, "WGET_TIMEOUT": 120}</code>'
|
||||
'</p>'
|
||||
)
|
||||
|
||||
return mark_safe(''.join(html_parts))
|
||||
|
||||
|
||||
class BaseModelAdmin(DjangoObjectActions, admin.ModelAdmin):
|
||||
list_display = ('id', 'created_at', 'created_by')
|
||||
readonly_fields = ('id', 'created_at', 'modified_at')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# from django.apps import AppConfig
|
||||
|
||||
|
||||
# class AbidUtilsConfig(AppConfig):
|
||||
# class BaseModelsConfig(AppConfig):
|
||||
# default_auto_field = 'django.db.models.BigAutoField'
|
||||
|
||||
|
||||
# name = 'base_models'
|
||||
|
||||
@@ -19,7 +19,7 @@ from django.conf import settings
|
||||
from django_stubs_ext.db.models import TypedModelMeta
|
||||
|
||||
from archivebox import DATA_DIR
|
||||
from archivebox.index.json import to_json
|
||||
from archivebox.misc.util import to_json
|
||||
from archivebox.misc.hashing import get_dir_info
|
||||
|
||||
|
||||
@@ -31,6 +31,16 @@ def get_or_create_system_user_pk(username='system'):
|
||||
return user.pk
|
||||
|
||||
|
||||
class AutoDateTimeField(models.DateTimeField):
|
||||
"""DateTimeField that automatically updates on save (legacy compatibility)."""
|
||||
def pre_save(self, model_instance, add):
|
||||
if add or not getattr(model_instance, self.attname):
|
||||
value = timezone.now()
|
||||
setattr(model_instance, self.attname, value)
|
||||
return value
|
||||
return super().pre_save(model_instance, add)
|
||||
|
||||
|
||||
class ModelWithUUID(models.Model):
|
||||
id = models.UUIDField(primary_key=True, default=uuid7, editable=False, unique=True)
|
||||
created_at = models.DateTimeField(default=timezone.now, db_index=True)
|
||||
@@ -74,6 +84,7 @@ class ModelWithSerializers(ModelWithUUID):
|
||||
|
||||
|
||||
class ModelWithNotes(models.Model):
|
||||
"""Mixin for models with a notes field."""
|
||||
notes = models.TextField(blank=True, null=False, default='')
|
||||
|
||||
class Meta:
|
||||
@@ -81,6 +92,7 @@ class ModelWithNotes(models.Model):
|
||||
|
||||
|
||||
class ModelWithHealthStats(models.Model):
|
||||
"""Mixin for models with health tracking fields."""
|
||||
num_uses_failed = models.PositiveIntegerField(default=0)
|
||||
num_uses_succeeded = models.PositiveIntegerField(default=0)
|
||||
|
||||
@@ -94,6 +106,7 @@ class ModelWithHealthStats(models.Model):
|
||||
|
||||
|
||||
class ModelWithConfig(models.Model):
|
||||
"""Mixin for models with a JSON config field."""
|
||||
config = models.JSONField(default=dict, null=False, blank=False, editable=True)
|
||||
|
||||
class Meta:
|
||||
@@ -113,7 +126,7 @@ class ModelWithOutputDir(ModelWithSerializers):
|
||||
|
||||
@property
|
||||
def output_dir_parent(self) -> str:
|
||||
return getattr(self, 'output_dir_parent', f'{self._meta.model_name}s')
|
||||
return f'{self._meta.model_name}s'
|
||||
|
||||
@property
|
||||
def output_dir_name(self) -> str:
|
||||
|
||||
Reference in New Issue
Block a user