mirror of
https://github.com/ArchiveBox/ArchiveBox.git
synced 2025-05-27 21:24:16 -04:00
fix plugin loading order, admin, abx-pkg
Some checks failed
Run linters / lint (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Build Debian package / build (push) Has been cancelled
Build Docker image / buildx (push) Has been cancelled
Deploy static content to Pages / deploy (push) Has been cancelled
Build Homebrew package / build (push) Has been cancelled
Build GitHub Pages website / build (push) Has been cancelled
Build Pip package / build (push) Has been cancelled
Run tests / python_tests (ubuntu-22.04, 3.11) (push) Has been cancelled
Run tests / docker_tests (push) Has been cancelled
Build GitHub Pages website / deploy (push) Has been cancelled
Some checks failed
Run linters / lint (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Build Debian package / build (push) Has been cancelled
Build Docker image / buildx (push) Has been cancelled
Deploy static content to Pages / deploy (push) Has been cancelled
Build Homebrew package / build (push) Has been cancelled
Build GitHub Pages website / build (push) Has been cancelled
Build Pip package / build (push) Has been cancelled
Run tests / python_tests (ubuntu-22.04, 3.11) (push) Has been cancelled
Run tests / docker_tests (push) Has been cancelled
Build GitHub Pages website / deploy (push) Has been cancelled
This commit is contained in:
parent
210fd935d7
commit
c8e186f21b
78 changed files with 801 additions and 987 deletions
|
@ -1,5 +1,5 @@
|
|||
__package__ = 'archivebox.core'
|
||||
|
||||
__order__ = 100
|
||||
import abx
|
||||
|
||||
@abx.hookimpl
|
||||
|
|
|
@ -21,7 +21,7 @@ class SnapshotActor(ActorType[Snapshot]):
|
|||
FINAL_STATES: ClassVar[list[State]] = SnapshotMachine.final_states # ['sealed']
|
||||
STATE_FIELD_NAME: ClassVar[str] = Snapshot.state_field_name # status
|
||||
|
||||
MAX_CONCURRENT_ACTORS: ClassVar[int] = 3
|
||||
MAX_CONCURRENT_ACTORS: ClassVar[int] = 1 # 3
|
||||
MAX_TICK_TIME: ClassVar[int] = 10
|
||||
CLAIM_FROM_TOP_N: ClassVar[int] = MAX_CONCURRENT_ACTORS * 10
|
||||
|
||||
|
@ -39,7 +39,7 @@ class ArchiveResultActor(ActorType[ArchiveResult]):
|
|||
FINAL_STATES: ClassVar[list[State]] = ArchiveResultMachine.final_states # ['succeeded', 'failed', 'skipped']
|
||||
STATE_FIELD_NAME: ClassVar[str] = ArchiveResult.state_field_name # status
|
||||
|
||||
MAX_CONCURRENT_ACTORS: ClassVar[int] = 6
|
||||
MAX_CONCURRENT_ACTORS: ClassVar[int] = 1 # 6
|
||||
MAX_TICK_TIME: ClassVar[int] = 60
|
||||
CLAIM_FROM_TOP_N: ClassVar[int] = MAX_CONCURRENT_ACTORS * 10
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ class ArchiveResultInline(admin.TabularInline):
|
|||
extra = 0
|
||||
sort_fields = ('end_ts', 'extractor', 'output', 'status', 'cmd_version')
|
||||
readonly_fields = ('id', 'result_id', 'completed', 'command', 'version')
|
||||
fields = ('start_ts', 'end_ts', *readonly_fields, 'extractor', 'cmd', 'cmd_version', 'pwd', 'created_by', 'status', 'output')
|
||||
fields = ('start_ts', 'end_ts', *readonly_fields, 'extractor', 'cmd', 'cmd_version', 'pwd', 'created_by', 'status', 'retry_at', 'output')
|
||||
# exclude = ('id',)
|
||||
ordering = ('end_ts',)
|
||||
show_change_link = True
|
||||
|
@ -105,11 +105,11 @@ class ArchiveResultInline(admin.TabularInline):
|
|||
|
||||
|
||||
class ArchiveResultAdmin(ABIDModelAdmin):
|
||||
list_display = ('start_ts', 'snapshot_info', 'tags_str', 'extractor', 'cmd_str', 'status', 'output_str')
|
||||
sort_fields = ('start_ts', 'extractor', 'status')
|
||||
list_display = ('abid', 'created_by', 'created_at', 'snapshot_info', 'tags_str', 'status', 'extractor', 'cmd_str', 'output_str')
|
||||
sort_fields = ('abid', 'created_by', 'created_at', 'extractor', 'status')
|
||||
readonly_fields = ('cmd_str', 'snapshot_info', 'tags_str', 'created_at', 'modified_at', 'abid_info', 'output_summary')
|
||||
search_fields = ('id', 'abid', 'snapshot__url', 'extractor', 'output', 'cmd_version', 'cmd', 'snapshot__timestamp')
|
||||
fields = ('snapshot', 'extractor', 'status', 'output', 'pwd', 'start_ts', 'end_ts', 'created_by', 'cmd_version', 'cmd', *readonly_fields)
|
||||
fields = ('snapshot', 'extractor', 'status', 'retry_at', 'start_ts', 'end_ts', 'created_by', 'pwd', 'cmd_version', 'cmd', 'output', *readonly_fields)
|
||||
autocomplete_fields = ['snapshot']
|
||||
|
||||
list_filter = ('status', 'extractor', 'start_ts', 'cmd_version')
|
||||
|
@ -169,7 +169,7 @@ class ArchiveResultAdmin(ABIDModelAdmin):
|
|||
result.output,
|
||||
)
|
||||
output_str += format_html('<a href="/archive/{}/index.html#all">See result files ...</a><br/><pre><code>', str(result.snapshot.timestamp))
|
||||
path_from_output_str = (snapshot_dir / result.output)
|
||||
path_from_output_str = (snapshot_dir / (result.output or ''))
|
||||
output_str += format_html('<i style="padding: 1px">{}</i><b style="padding-right: 20px">/</b><i>{}</i><br/><hr/>', str(snapshot_dir), str(result.output))
|
||||
if os.access(path_from_output_str, os.R_OK):
|
||||
root_dir = str(path_from_output_str)
|
||||
|
|
|
@ -56,12 +56,12 @@ class SnapshotActionForm(ActionForm):
|
|||
|
||||
|
||||
class SnapshotAdmin(SearchResultsAdminMixin, ABIDModelAdmin):
|
||||
list_display = ('created_at', 'title_str', 'files', 'size', 'url_str', 'crawl')
|
||||
sort_fields = ('title_str', 'url_str', 'created_at', 'crawl')
|
||||
list_display = ('created_at', 'title_str', 'status', 'files', 'size', 'url_str')
|
||||
sort_fields = ('title_str', 'url_str', 'created_at', 'status', 'crawl')
|
||||
readonly_fields = ('admin_actions', 'status_info', 'tags_str', 'imported_timestamp', 'created_at', 'modified_at', 'downloaded_at', 'abid_info', 'link_dir')
|
||||
search_fields = ('id', 'url', 'abid', 'timestamp', 'title', 'tags__name')
|
||||
list_filter = ('created_at', 'downloaded_at', 'archiveresult__status', 'created_by', 'tags__name')
|
||||
fields = ('url', 'title', 'created_by', 'bookmarked_at', 'crawl', *readonly_fields)
|
||||
fields = ('url', 'title', 'created_by', 'bookmarked_at', 'status', 'retry_at', 'crawl', *readonly_fields)
|
||||
ordering = ['-created_at']
|
||||
actions = ['add_tags', 'remove_tags', 'update_titles', 'update_snapshots', 'resnapshot_snapshot', 'overwrite_snapshots', 'delete_snapshots']
|
||||
inlines = [TagInline, ArchiveResultInline]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
__package__ = 'archivebox.core'
|
||||
|
||||
|
||||
from typing import Optional, Dict, Iterable
|
||||
from typing import Optional, Dict, Iterable, Any
|
||||
from django_stubs_ext.db.models import TypedModelMeta
|
||||
|
||||
import os
|
||||
|
@ -20,20 +20,22 @@ from django.db.models import Case, When, Value, IntegerField
|
|||
from django.contrib import admin
|
||||
from django.conf import settings
|
||||
|
||||
from actors.models import ModelWithStateMachine
|
||||
|
||||
import abx
|
||||
|
||||
from archivebox.config import CONSTANTS
|
||||
|
||||
from abid_utils.models import ABIDModel, ABIDField, AutoDateTimeField
|
||||
from actors.models import ModelWithStateMachine
|
||||
from queues.tasks import bg_archive_snapshot
|
||||
from crawls.models import Crawl
|
||||
# from machine.models import Machine, NetworkInterface
|
||||
|
||||
from archivebox.misc.system import get_dir_size
|
||||
from archivebox.misc.util import parse_date, base_url
|
||||
from ..index.schema import Link
|
||||
from ..index.html import snapshot_icons
|
||||
from ..extractors import ARCHIVE_METHODS_INDEXING_PRECEDENCE, EXTRACTORS
|
||||
from archivebox.index.schema import Link
|
||||
from archivebox.index.html import snapshot_icons
|
||||
from archivebox.extractors import ARCHIVE_METHODS_INDEXING_PRECEDENCE
|
||||
|
||||
|
||||
# class BaseModel(models.Model):
|
||||
|
@ -195,13 +197,21 @@ class Snapshot(ABIDModel, ModelWithStateMachine):
|
|||
tags = models.ManyToManyField(Tag, blank=True, through=SnapshotTag, related_name='snapshot_set', through_fields=('snapshot', 'tag'))
|
||||
title = models.CharField(max_length=512, null=True, blank=True, db_index=True)
|
||||
|
||||
keys = ('url', 'timestamp', 'title', 'tags', 'downloaded_at')
|
||||
# config = models.JSONField(default=dict, null=False, blank=False, editable=True)
|
||||
|
||||
keys = ('url', 'timestamp', 'title', 'tags', 'downloaded_at', 'created_at', 'status', 'retry_at', 'abid', 'id')
|
||||
|
||||
archiveresult_set: models.Manager['ArchiveResult']
|
||||
|
||||
objects = SnapshotManager()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.pk:
|
||||
existing_snapshot = self.__class__.objects.filter(pk=self.pk).first()
|
||||
if existing_snapshot and existing_snapshot.status == self.StatusChoices.SEALED:
|
||||
if self.as_json() != existing_snapshot.as_json():
|
||||
raise Exception(f'Snapshot {self.pk} is already sealed, it cannot be modified any further. NEW: {self.as_json()} != Existing: {existing_snapshot.as_json()}')
|
||||
|
||||
if not self.bookmarked_at:
|
||||
self.bookmarked_at = self.created_at or self._init_timestamp
|
||||
|
||||
|
@ -427,7 +437,7 @@ class Snapshot(ABIDModel, ModelWithStateMachine):
|
|||
ALL_EXTRACTORS = ['favicon', 'title', 'screenshot', 'headers', 'singlefile', 'dom', 'git', 'archive_org', 'readability', 'mercury', 'pdf', 'wget']
|
||||
|
||||
# config = get_scope_config(snapshot=self)
|
||||
config = {'EXTRACTORS': ''}
|
||||
config = {'EXTRACTORS': ','.join(ALL_EXTRACTORS)}
|
||||
|
||||
if config.get('EXTRACTORS', 'auto') == 'auto':
|
||||
EXTRACTORS = ALL_EXTRACTORS
|
||||
|
@ -438,10 +448,13 @@ class Snapshot(ABIDModel, ModelWithStateMachine):
|
|||
for extractor in EXTRACTORS:
|
||||
if not extractor:
|
||||
continue
|
||||
archiveresult, _created = ArchiveResult.objects.get_or_create(
|
||||
archiveresult = ArchiveResult.objects.update_or_create(
|
||||
snapshot=self,
|
||||
extractor=extractor,
|
||||
status=ArchiveResult.INITIAL_STATE,
|
||||
defaults={
|
||||
'retry_at': timezone.now(),
|
||||
},
|
||||
)
|
||||
archiveresults.append(archiveresult)
|
||||
return archiveresults
|
||||
|
@ -560,6 +573,8 @@ class ArchiveResult(ABIDModel, ModelWithStateMachine):
|
|||
# uplink = models.ForeignKey(NetworkInterface, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='Network Interface Used')
|
||||
|
||||
objects = ArchiveResultManager()
|
||||
|
||||
keys = ('snapshot_id', 'extractor', 'cmd', 'pwd', 'cmd_version', 'output', 'start_ts', 'end_ts', 'created_at', 'status', 'retry_at', 'abid', 'id')
|
||||
|
||||
class Meta(TypedModelMeta):
|
||||
verbose_name = 'Archive Result'
|
||||
|
@ -576,6 +591,16 @@ class ArchiveResult(ABIDModel, ModelWithStateMachine):
|
|||
|
||||
def __str__(self):
|
||||
return repr(self)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# if (self.pk and self.__class__.objects.filter(pk=self.pk).values_list('status', flat=True)[0] in [self.StatusChoices.FAILED, self.StatusChoices.SUCCEEDED, self.StatusChoices.SKIPPED]):
|
||||
# raise Exception(f'ArchiveResult {self.pk} is in a final state, it cannot be modified any further.')
|
||||
if self.pk:
|
||||
existing_archiveresult = self.__class__.objects.filter(pk=self.pk).first()
|
||||
if existing_archiveresult and existing_archiveresult.status in [self.StatusChoices.FAILED, self.StatusChoices.SUCCEEDED, self.StatusChoices.SKIPPED]:
|
||||
if self.as_json() != existing_archiveresult.as_json():
|
||||
raise Exception(f'ArchiveResult {self.pk} is in a final state, it cannot be modified any further. NEW: {self.as_json()} != Existing: {existing_archiveresult.as_json()}')
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
# TODO: finish connecting machine.models
|
||||
# @cached_property
|
||||
|
@ -603,36 +628,53 @@ class ArchiveResult(ABIDModel, ModelWithStateMachine):
|
|||
return f'/{self.snapshot.archive_path}/{self.output_path()}'
|
||||
|
||||
@property
|
||||
def extractor_module(self):
|
||||
return EXTRACTORS[self.extractor]
|
||||
def extractor_module(self) -> Any | None:
|
||||
return abx.as_dict(abx.pm.hook.get_EXTRACTORS()).get(self.extractor, None)
|
||||
|
||||
def output_path(self) -> str:
|
||||
def output_path(self) -> str | None:
|
||||
"""return the canonical output filename or directory name within the snapshot dir"""
|
||||
return self.extractor_module.get_output_path()
|
||||
try:
|
||||
return self.extractor_module.get_output_path(self.snapshot)
|
||||
except Exception as e:
|
||||
print(f'Error getting output path for {self.extractor} extractor: {e}')
|
||||
return None
|
||||
|
||||
def embed_path(self) -> str:
|
||||
def embed_path(self) -> str | None:
|
||||
"""
|
||||
return the actual runtime-calculated path to the file on-disk that
|
||||
should be used for user-facing iframe embeds of this result
|
||||
"""
|
||||
|
||||
if get_embed_path_func := getattr(self.extractor_module, 'get_embed_path', None):
|
||||
return get_embed_path_func(self)
|
||||
|
||||
return self.extractor_module.get_output_path()
|
||||
try:
|
||||
return self.extractor_module.get_embed_path(self)
|
||||
except Exception as e:
|
||||
print(f'Error getting embed path for {self.extractor} extractor: {e}')
|
||||
return None
|
||||
|
||||
def legacy_output_path(self):
|
||||
link = self.snapshot.as_link()
|
||||
return link.canonical_outputs().get(f'{self.extractor}_path')
|
||||
|
||||
def output_exists(self) -> bool:
|
||||
return os.path.exists(self.output_path())
|
||||
|
||||
output_path = self.output_path()
|
||||
return bool(output_path and os.path.exists(output_path))
|
||||
|
||||
def create_output_dir(self):
|
||||
snap_dir = self.snapshot_dir
|
||||
snap_dir = Path(self.snapshot_dir)
|
||||
snap_dir.mkdir(parents=True, exist_ok=True)
|
||||
return snap_dir / self.output_path()
|
||||
output_path = self.output_path()
|
||||
if output_path:
|
||||
(snap_dir / output_path).mkdir(parents=True, exist_ok=True)
|
||||
else:
|
||||
raise ValueError(f'Not able to calculate output path for {self.extractor} extractor in {snap_dir}')
|
||||
return snap_dir / output_path
|
||||
|
||||
def as_json(self, *args) -> dict:
|
||||
args = args or self.keys
|
||||
return {
|
||||
key: getattr(self, key)
|
||||
for key in args
|
||||
}
|
||||
|
||||
# def get_storage_dir(self, create=True, symlink=True):
|
||||
# date_str = self.snapshot.bookmarked_at.strftime('%Y%m%d')
|
||||
|
|
|
@ -37,25 +37,44 @@ class SnapshotMachine(StateMachine, strict_states=True):
|
|||
super().__init__(snapshot, *args, **kwargs)
|
||||
|
||||
def can_start(self) -> bool:
|
||||
return self.snapshot.url
|
||||
can_start = bool(self.snapshot.url and (self.snapshot.retry_at < timezone.now()))
|
||||
if not can_start:
|
||||
print(f'SnapshotMachine[{self.snapshot.ABID}].can_start() False: {self.snapshot.url} {self.snapshot.retry_at} {timezone.now()}')
|
||||
return can_start
|
||||
|
||||
def is_finished(self) -> bool:
|
||||
# if no archiveresults exist yet, it's not finished
|
||||
if not self.snapshot.archiveresult_set.exists():
|
||||
return False
|
||||
# if archiveresults exist but are still pending, it's not finished
|
||||
if self.snapshot.pending_archiveresults().exists():
|
||||
return False
|
||||
|
||||
# otherwise archiveresults exist and are all finished, so it's finished
|
||||
return True
|
||||
|
||||
@started.enter
|
||||
def on_started(self):
|
||||
print(f'SnapshotMachine[{self.snapshot.ABID}].on_started(): snapshot.create_pending_archiveresults() + snapshot.bump_retry_at(+60s)')
|
||||
self.snapshot.create_pending_archiveresults()
|
||||
self.snapshot.bump_retry_at(seconds=60)
|
||||
def on_transition(self, event, state):
|
||||
print(f'SnapshotMachine[{self.snapshot.ABID}].on_transition() {event} -> {state}')
|
||||
|
||||
@queued.enter
|
||||
def enter_queued(self):
|
||||
print(f'SnapshotMachine[{self.snapshot.ABID}].on_queued(): snapshot.retry_at = now()')
|
||||
self.snapshot.status = Snapshot.StatusChoices.QUEUED
|
||||
self.snapshot.retry_at = timezone.now()
|
||||
self.snapshot.save()
|
||||
|
||||
@started.enter
|
||||
def enter_started(self):
|
||||
print(f'SnapshotMachine[{self.snapshot.ABID}].on_started(): snapshot.create_pending_archiveresults() + snapshot.bump_retry_at(+60s)')
|
||||
self.snapshot.status = Snapshot.StatusChoices.STARTED
|
||||
self.snapshot.bump_retry_at(seconds=60)
|
||||
self.snapshot.save()
|
||||
self.snapshot.create_pending_archiveresults()
|
||||
|
||||
@sealed.enter
|
||||
def on_sealed(self):
|
||||
def enter_sealed(self):
|
||||
print(f'SnapshotMachine[{self.snapshot.ABID}].on_sealed(): snapshot.retry_at=None')
|
||||
self.snapshot.status = Snapshot.StatusChoices.SEALED
|
||||
self.snapshot.retry_at = None
|
||||
self.snapshot.save()
|
||||
|
||||
|
@ -95,7 +114,7 @@ class ArchiveResultMachine(StateMachine, strict_states=True):
|
|||
super().__init__(archiveresult, *args, **kwargs)
|
||||
|
||||
def can_start(self) -> bool:
|
||||
return self.archiveresult.snapshot and self.archiveresult.snapshot.STATE == Snapshot.active_state
|
||||
return self.archiveresult.snapshot and (self.archiveresult.retry_at < timezone.now())
|
||||
|
||||
def is_succeeded(self) -> bool:
|
||||
return self.archiveresult.output_exists()
|
||||
|
@ -109,29 +128,45 @@ class ArchiveResultMachine(StateMachine, strict_states=True):
|
|||
def is_finished(self) -> bool:
|
||||
return self.is_failed() or self.is_succeeded()
|
||||
|
||||
|
||||
@queued.enter
|
||||
def enter_queued(self):
|
||||
print(f'ArchiveResultMachine[{self.archiveresult.ABID}].on_queued(): archiveresult.retry_at = now()')
|
||||
self.archiveresult.status = ArchiveResult.StatusChoices.QUEUED
|
||||
self.archiveresult.retry_at = timezone.now()
|
||||
self.archiveresult.save()
|
||||
|
||||
@started.enter
|
||||
def on_started(self):
|
||||
def enter_started(self):
|
||||
print(f'ArchiveResultMachine[{self.archiveresult.ABID}].on_started(): archiveresult.start_ts + create_output_dir() + bump_retry_at(+60s)')
|
||||
self.archiveresult.status = ArchiveResult.StatusChoices.STARTED
|
||||
self.archiveresult.start_ts = timezone.now()
|
||||
self.archiveresult.create_output_dir()
|
||||
self.archiveresult.bump_retry_at(seconds=60)
|
||||
self.archiveresult.save()
|
||||
self.archiveresult.create_output_dir()
|
||||
|
||||
@backoff.enter
|
||||
def on_backoff(self):
|
||||
print(f'ArchiveResultMachine[{self.archiveresult.ABID}].on_backoff(): archiveresult.bump_retry_at(+60s)')
|
||||
def enter_backoff(self):
|
||||
print(f'ArchiveResultMachine[{self.archiveresult.ABID}].on_backoff(): archiveresult.retries += 1, archiveresult.bump_retry_at(+60s), archiveresult.end_ts = None')
|
||||
self.archiveresult.status = ArchiveResult.StatusChoices.BACKOFF
|
||||
self.archiveresult.retries = getattr(self.archiveresult, 'retries', 0) + 1
|
||||
self.archiveresult.bump_retry_at(seconds=60)
|
||||
self.archiveresult.end_ts = None
|
||||
self.archiveresult.save()
|
||||
|
||||
@succeeded.enter
|
||||
def on_succeeded(self):
|
||||
print(f'ArchiveResultMachine[{self.archiveresult.ABID}].on_succeeded(): archiveresult.end_ts')
|
||||
def enter_succeeded(self):
|
||||
print(f'ArchiveResultMachine[{self.archiveresult.ABID}].on_succeeded(): archiveresult.retry_at = None, archiveresult.end_ts = now()')
|
||||
self.archiveresult.status = ArchiveResult.StatusChoices.SUCCEEDED
|
||||
self.archiveresult.retry_at = None
|
||||
self.archiveresult.end_ts = timezone.now()
|
||||
self.archiveresult.save()
|
||||
|
||||
@failed.enter
|
||||
def on_failed(self):
|
||||
print(f'ArchiveResultMachine[{self.archiveresult.ABID}].on_failed(): archiveresult.end_ts')
|
||||
def enter_failed(self):
|
||||
print(f'ArchiveResultMachine[{self.archiveresult.ABID}].on_failed(): archivebox.retry_at = None, archiveresult.end_ts = now()')
|
||||
self.archiveresult.status = ArchiveResult.StatusChoices.FAILED
|
||||
self.archiveresult.retry_at = None
|
||||
self.archiveresult.end_ts = timezone.now()
|
||||
self.archiveresult.save()
|
||||
|
||||
|
|
|
@ -102,7 +102,8 @@ class SnapshotView(View):
|
|||
|
||||
# iterate through all the files in the snapshot dir and add the biggest ones to1 the result list
|
||||
snap_dir = Path(snapshot.link_dir)
|
||||
assert os.path.isdir(snap_dir) and os.access(snap_dir, os.R_OK)
|
||||
if not os.path.isdir(snap_dir) and os.access(snap_dir, os.R_OK):
|
||||
return {}
|
||||
|
||||
for result_file in (*snap_dir.glob('*'), *snap_dir.glob('*/*')):
|
||||
extension = result_file.suffix.lstrip('.').lower()
|
||||
|
@ -504,7 +505,7 @@ def find_config_section(key: str) -> str:
|
|||
if key in CONSTANTS_CONFIG:
|
||||
return 'CONSTANT'
|
||||
matching_sections = [
|
||||
section_id for section_id, section in CONFIGS.items() if key in section.model_fields
|
||||
section_id for section_id, section in CONFIGS.items() if key in dict(section)
|
||||
]
|
||||
section = matching_sections[0] if matching_sections else 'DYNAMIC'
|
||||
return section
|
||||
|
@ -518,8 +519,9 @@ def find_config_default(key: str) -> str:
|
|||
default_val = None
|
||||
|
||||
for config in CONFIGS.values():
|
||||
if key in config.model_fields:
|
||||
default_val = config.model_fields[key].default
|
||||
if key in dict(config):
|
||||
default_field = getattr(config, 'model_fields', dict(config))[key]
|
||||
default_val = default_field.default if hasattr(default_field, 'default') else default_field
|
||||
break
|
||||
|
||||
if isinstance(default_val, Callable):
|
||||
|
@ -529,7 +531,6 @@ def find_config_default(key: str) -> str:
|
|||
else:
|
||||
default_val = str(default_val)
|
||||
|
||||
|
||||
return default_val
|
||||
|
||||
def find_config_type(key: str) -> str:
|
||||
|
@ -567,7 +568,7 @@ def live_config_list_view(request: HttpRequest, **kwargs) -> TableContext:
|
|||
}
|
||||
|
||||
for section_id, section in reversed(list(CONFIGS.items())):
|
||||
for key, field in section.model_fields.items():
|
||||
for key in dict(section).keys():
|
||||
rows['Section'].append(section_id) # section.replace('_', ' ').title().replace(' Config', '')
|
||||
rows['Key'].append(ItemLink(key, key=key))
|
||||
rows['Type'].append(format_html('<code>{}</code>', find_config_type(key)))
|
||||
|
@ -580,7 +581,7 @@ def live_config_list_view(request: HttpRequest, **kwargs) -> TableContext:
|
|||
for key in CONSTANTS_CONFIG.keys():
|
||||
rows['Section'].append(section) # section.replace('_', ' ').title().replace(' Config', '')
|
||||
rows['Key'].append(ItemLink(key, key=key))
|
||||
rows['Type'].append(format_html('<code>{}</code>', getattr(type(CONSTANTS_CONFIG[key]), '__name__', repr(CONSTANTS_CONFIG[key]))))
|
||||
rows['Type'].append(format_html('<code>{}</code>', getattr(type(CONSTANTS_CONFIG[key]), '__name__', str(CONSTANTS_CONFIG[key]))))
|
||||
rows['Value'].append(format_html('<code>{}</code>', CONSTANTS_CONFIG[key]) if key_is_safe(key) else '******** (redacted)')
|
||||
rows['Default'].append(mark_safe(f'<a href="https://github.com/search?q=repo%3AArchiveBox%2FArchiveBox+path%3Aconfig+{key}&type=code"><code style="text-decoration: underline">{find_config_default(key) or "See here..."}</code></a>'))
|
||||
# rows['Documentation'].append(mark_safe(f'Wiki: <a href="https://github.com/ArchiveBox/ArchiveBox/wiki/Configuration#{key.lower()}">{key}</a>'))
|
||||
|
@ -642,13 +643,13 @@ def live_config_value_view(request: HttpRequest, key: str, **kwargs) -> ItemCont
|
|||
<code>{find_config_default(key) or '↗️ See in ArchiveBox source code...'}</code>
|
||||
</a>
|
||||
<br/><br/>
|
||||
<p style="display: {"block" if key in FLAT_CONFIG else "none"}">
|
||||
<p style="display: {"block" if key in FLAT_CONFIG and key not in CONSTANTS_CONFIG else "none"}">
|
||||
<i>To change this value, edit <code>data/ArchiveBox.conf</code> or run:</i>
|
||||
<br/><br/>
|
||||
<code>archivebox config --set {key}="{
|
||||
val.strip("'")
|
||||
if (val := find_config_default(key)) else
|
||||
(repr(FLAT_CONFIG[key] if key_is_safe(key) else '********')).strip("'")
|
||||
(str(FLAT_CONFIG[key] if key_is_safe(key) else '********')).strip("'")
|
||||
}"</code>
|
||||
</p>
|
||||
'''),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue