Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fef07cd4d | ||
|
|
9b2e7cd33a | ||
|
|
122bd0cc2e |
No files matched your search
+15
-21
@@ -1,26 +1,20 @@
|
||||
# Read the Docs config for https://docs.archivebox.io
|
||||
# https://docs.readthedocs.io/en/stable/config-file/v2.html
|
||||
|
||||
version: 2
|
||||
|
||||
submodules:
|
||||
include: all
|
||||
recursive: true
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-24.04
|
||||
tools:
|
||||
python: "3.12"
|
||||
#nodejs: "20" # not needed unless we need the full archivebox to run while building docs for some reason
|
||||
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python: "3.13"
|
||||
jobs:
|
||||
build:
|
||||
html:
|
||||
- uv run --no-project --with-requirements .github/docs/requirements.txt sphinx-build -W --keep-going -b html -c .github/docs docs "$READTHEDOCS_OUTPUT/html"
|
||||
submodules:
|
||||
include:
|
||||
- docs
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
- method: uv
|
||||
command: pip
|
||||
requirements: .github/docs/requirements.txt
|
||||
sphinx:
|
||||
configuration: .github/docs/conf.py
|
||||
fail_on_warning: true
|
||||
@@ -0,0 +1,33 @@
|
||||
# Building the 0.8.6rc1 documentation
|
||||
|
||||
The `docs` submodule remains pinned to the wiki revision shipped with 0.8.6rc1.
|
||||
This configuration renders that historical user guide without installing
|
||||
ArchiveBox, importing Django, or updating the wiki checkout.
|
||||
|
||||
Prerequisites: [uv](https://docs.astral.sh/uv/getting-started/installation/) 0.11.3 and Python 3.13, matching CI and Read the Docs. `uv` can install Python when needed.
|
||||
|
||||
```sh
|
||||
git submodule update --init docs
|
||||
uv run --no-project --python 3.13 --with-requirements .github/docs/requirements.txt \
|
||||
sphinx-build -W --keep-going -b html -c .github/docs docs /tmp/archivebox-docs
|
||||
```
|
||||
|
||||
`conf.py` adapts GitHub wiki links, heading levels, and obsolete local anchors
|
||||
in memory. Search options without a prose section link to their exact
|
||||
0.8.6rc1 source definition; the removed USE_CURL option points to CURL_BINARY. The original user documentation is not rewritten.
|
||||
Generated Python autodoc scaffolding is outside this standalone user guide;
|
||||
its legacy Django-dependent builder is not loaded.
|
||||
|
||||
Read the Docs uses `.github/.readthedocs.yaml` and the same strict HTML command.
|
||||
Keep its project configuration-file setting pointed at that path.
|
||||
|
||||
The release label follows the immutable `v0.8.6rc1` tag; its original
|
||||
`pyproject.toml` still declares `0.8.6rc0` and is left unchanged.
|
||||
|
||||
`requirements.txt` pins the complete documentation dependency set. To update it,
|
||||
edit `requirements.in` and run:
|
||||
|
||||
```sh
|
||||
uv pip compile --universal --python-version 3.13 .github/docs/requirements.in \
|
||||
--output-file .github/docs/requirements.txt
|
||||
```
|
||||
@@ -0,0 +1,199 @@
|
||||
"""Build the pinned 0.8.6rc1 user guide without importing ArchiveBox or Django."""
|
||||
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from urllib.parse import unquote
|
||||
|
||||
from markdown_it import MarkdownIt
|
||||
from myst_parser.mdit_to_docutils.base import default_slugify
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
DOCS = ROOT / "docs"
|
||||
project = "ArchiveBox"
|
||||
author = "ArchiveBox contributors"
|
||||
copyright = "ArchiveBox contributors"
|
||||
html_show_sphinx = False
|
||||
# The release tag is authoritative: its pyproject still declares 0.8.6rc0.
|
||||
release = "0.8.6rc1"
|
||||
version = release
|
||||
extensions = ["myst_parser", "sphinx_rtd_theme", "sphinxcontrib.mermaid"]
|
||||
source_suffix = {".rst": "restructuredtext", ".md": "markdown"}
|
||||
root_doc = "index"
|
||||
# These are generated autodoc scaffolding, not standalone user documentation.
|
||||
exclude_patterns = [
|
||||
"archivebox*.rst",
|
||||
"modules.rst",
|
||||
"apidocs/**",
|
||||
"_Sidebar.md",
|
||||
"_Footer.md",
|
||||
".git",
|
||||
]
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
html_theme_options = {
|
||||
"navigation_depth": 3,
|
||||
"collapse_navigation": False,
|
||||
"version_selector": True,
|
||||
"language_selector": False,
|
||||
}
|
||||
html_context = {"current_version": os.environ.get("READTHEDOCS_VERSION", release)}
|
||||
html_logo = str(DOCS / "logo.png")
|
||||
html_static_path = [str(DOCS / "_static")]
|
||||
html_title = f"ArchiveBox {release} documentation"
|
||||
myst_heading_anchors = 6
|
||||
myst_fence_as_directive = ["mermaid"]
|
||||
|
||||
|
||||
def rewrite_prose(text, transform):
|
||||
"""Rewrite navigation outside fenced and indented code blocks."""
|
||||
lines = text.splitlines(keepends=True)
|
||||
output = []
|
||||
start = 0
|
||||
for token in MarkdownIt().parse(text):
|
||||
if token.type not in {"fence", "code_block"}:
|
||||
continue
|
||||
first, last = token.map
|
||||
output.append(transform("".join(lines[start:first])))
|
||||
output.append("".join(lines[first:last]))
|
||||
start = last
|
||||
output.append(transform("".join(lines[start:])))
|
||||
return "".join(output)
|
||||
|
||||
|
||||
def prepare_source(app, docname, source):
|
||||
"""Adapt GitHub wiki navigation in memory, preserving the pinned sources."""
|
||||
text = source[0]
|
||||
if docname == "Contents":
|
||||
text = re.sub(r"API Reference\n#############.*?(?=Meta\n####)", "", text, flags=re.DOTALL)
|
||||
text += "\n.. toctree::\n :hidden:\n\n Home\n ArchiveBox-Architecture-Diagrams\n Upgrading-or-Merging-Archives\n"
|
||||
if docname == "index":
|
||||
text = text.replace("archivebox info", "archivebox status")
|
||||
text = text.replace("pip install archivebox", f"pip install archivebox=={release}")
|
||||
text = text.replace("ArchiveBox/ArchiveBox/tree/master", f"ArchiveBox/ArchiveBox/tree/v{release}")
|
||||
text = text.replace(
|
||||
"`Github <https://github.com/ArchiveBox/ArchiveBox/issues>`_",
|
||||
"`GitHub issues <https://github.com/ArchiveBox/ArchiveBox/issues>`_",
|
||||
)
|
||||
text = text.replace(
|
||||
"==========\nArchiveBox\n==========",
|
||||
f"ArchiveBox {release}\n" + "=" * (11 + len(release)),
|
||||
)
|
||||
if Path(app.env.doc2path(docname)).suffix == ".md":
|
||||
|
||||
def wiki_link(match):
|
||||
parts = match.group(1).split("|", 1)
|
||||
label, target = (parts[0], parts[-1])
|
||||
page, separator, anchor = unquote(target).partition("#")
|
||||
page = page.replace(" ", "-")
|
||||
page = {"Setting-Up-Authentication": "Setting-up-Authentication"}.get(page, page)
|
||||
suffix = f"#{anchor}" if separator else ""
|
||||
return f"[{label}]({page}.md{suffix})"
|
||||
|
||||
text = rewrite_prose(text, lambda prose: re.sub(r"\[\[([^\]\n]+)\]\]", wiki_link, prose))
|
||||
source[0] = text
|
||||
|
||||
|
||||
# GitHub wiki headings allow skipped levels; normalize their hierarchy before MyST
|
||||
# parses them, without changing fenced code blocks or the historical source files.
|
||||
def normalize_markdown(app, docname, source):
|
||||
if Path(app.env.doc2path(docname)).suffix != ".md":
|
||||
return
|
||||
text = source[0]
|
||||
if docname == "Upgrading":
|
||||
text = text.replace("---\n---", "---")
|
||||
if docname == "Scheduled-Archiving":
|
||||
text = text.replace("```bash\n$ cd", "```console\n$ cd", 1)
|
||||
if docname == "Setting-up-Authentication":
|
||||
text = text.replace(
|
||||
"[Wiki: Configuration (`PUBLIC_ADD_VIEW`, `PUBLIC_SNAPSHOTS`, `PUBLIC_INDEX`)]()",
|
||||
"[Wiki: Configuration (`PUBLIC_ADD_VIEW`, `PUBLIC_SNAPSHOTS`, `PUBLIC_INDEX`)](Configuration.md#public_index--public_snapshots--public_add_view)",
|
||||
)
|
||||
if docname in {"Home", "README", "Donations", "Upgrading-or-Merging-Archives"}:
|
||||
text = "# " + ("ArchiveBox overview" if docname == "README" else docname.replace("-", " ")) + "\n\n" + text
|
||||
lines = text.splitlines()
|
||||
stack = []
|
||||
for token in MarkdownIt().parse(text):
|
||||
if token.type != "heading_open":
|
||||
continue
|
||||
original = int(token.tag[1])
|
||||
while stack and stack[-1] >= original:
|
||||
stack.pop()
|
||||
stack.append(original)
|
||||
line = token.map[0]
|
||||
lines[line] = re.sub(r"^#{1,6}(?=\s)", "#" * len(stack), lines[line]).replace("\ufe0f", "")
|
||||
text = "\n".join(lines) + "\n"
|
||||
|
||||
def link(match):
|
||||
target = match.group(1)
|
||||
if "://" in target or target.startswith("mailto:"):
|
||||
return match.group(0)
|
||||
page, sep, anchor = target.partition("#")
|
||||
if page and (DOCS / (page + ".md")).exists():
|
||||
page += ".md"
|
||||
anchor = unquote(anchor).lower()
|
||||
if docname == "Install" and not page:
|
||||
anchor = {
|
||||
"option-a-docker--docker-compose-setup-️": "option-a-docker--docker-compose-setup-",
|
||||
"upgrading-archivebox-to-a-new-version": "next-steps-upgrading-archivebox-to-a-new-version",
|
||||
"next-steps": "next-steps-add-some-urls-to-archive-and-try-out-cli--web-ui",
|
||||
"ubuntudebian": "ubuntudebian-based-systems",
|
||||
"bsd": "freebsd",
|
||||
"manual-setup": "option-c-bare-metal-setup",
|
||||
}.get(anchor, anchor)
|
||||
if docname == "README" and not page:
|
||||
anchor = {
|
||||
"input-formats": "input-formats-how-to-pass-urls-into-archivebox-for-saving",
|
||||
"output-formats": "output-formats-what-archivebox-saves-for-each-url",
|
||||
}.get(anchor, anchor)
|
||||
if (docname == "Configuration" and not page) or page == "Configuration.md":
|
||||
other = (DOCS / "Configuration.md").read_text()
|
||||
for heading in re.findall(r"^#+\s+(.+)$", other, re.MULTILINE):
|
||||
if anchor in [word.lower() for word in re.findall(r"`([^`]+)`", heading)]:
|
||||
anchor = default_slugify(heading.replace("`", ""))
|
||||
break
|
||||
aliases = {"templates_dir": "custom_templates_dir"}
|
||||
anchor = aliases.get(anchor, anchor)
|
||||
if anchor in {
|
||||
"search_backend_timeout",
|
||||
"search_backend_engine",
|
||||
"use_curl",
|
||||
}:
|
||||
if anchor == "use_curl":
|
||||
# This obsolete wiki option no longer exists in the 0.8 config.
|
||||
return "](Configuration.md#curl_binary)"
|
||||
code = (ROOT / "archivebox/config/common.py").read_text().splitlines()
|
||||
number = next(i + 1 for i, line in enumerate(code) if anchor.upper() in line)
|
||||
return "](https://github.com/ArchiveBox/ArchiveBox/blob/v0.8.6rc1/archivebox/config/common.py#L" + str(number) + ")"
|
||||
if docname == "README" and anchor == "saves-lots-of-useful-stuff-for-each-imported-link":
|
||||
anchor = "output-formats-what-archivebox-saves-for-each-url"
|
||||
if docname == "Web-Archiving-Community":
|
||||
anchor = {
|
||||
"blogs": "blogs-friends-of-archivebox",
|
||||
"articles": "articles-we-like-about-internet-archiving",
|
||||
}.get(anchor, anchor)
|
||||
return "](" + page + ("#" + anchor if sep else "") + ")"
|
||||
|
||||
def rewrite_links(prose):
|
||||
prose = re.sub(r"\]\(([^)\s]+)\)", link, prose)
|
||||
# Raw HTML links are outside MyST's cross-reference resolver.
|
||||
html_anchors = {
|
||||
"background--motivation": "background-motivation",
|
||||
"Caveats": "caveats",
|
||||
"contents": "web-archiving-community",
|
||||
"%EF%B8%8F-cli-usage": "cli-usage",
|
||||
"url_whitelist": "url-allowlist",
|
||||
"url_blacklist": "url-denylist",
|
||||
"input-formats": "input-formats-how-to-pass-urls-into-archivebox-for-saving",
|
||||
}
|
||||
for old, new in html_anchors.items():
|
||||
prose = prose.replace(f'href="#{old}"', f'href="#{new}"')
|
||||
prose = prose.replace("#️-cli-usage", "#cli-usage")
|
||||
return prose
|
||||
|
||||
text = rewrite_prose(text, rewrite_links)
|
||||
source[0] = text
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.connect("source-read", prepare_source)
|
||||
app.connect("source-read", normalize_markdown)
|
||||
@@ -0,0 +1,4 @@
|
||||
Sphinx==8.2.3
|
||||
myst-parser==4.0.1
|
||||
sphinx-rtd-theme==3.1.0
|
||||
sphinxcontrib-mermaid==2.1.1
|
||||
@@ -0,0 +1,81 @@
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile --universal --python-version 3.13 .github/docs/requirements.in --output-file .github/docs/requirements.txt
|
||||
alabaster==1.0.0
|
||||
# via sphinx
|
||||
babel==2.18.0
|
||||
# via sphinx
|
||||
certifi==2026.7.22
|
||||
# via requests
|
||||
charset-normalizer==3.5.1
|
||||
# via requests
|
||||
colorama==0.4.6 ; sys_platform == 'win32'
|
||||
# via sphinx
|
||||
docutils==0.21.2
|
||||
# via
|
||||
# myst-parser
|
||||
# sphinx
|
||||
# sphinx-rtd-theme
|
||||
idna==3.20
|
||||
# via requests
|
||||
imagesize==2.0.1
|
||||
# via sphinx
|
||||
jinja2==3.1.6
|
||||
# via
|
||||
# myst-parser
|
||||
# sphinx
|
||||
# sphinxcontrib-mermaid
|
||||
markdown-it-py==3.0.0
|
||||
# via
|
||||
# mdit-py-plugins
|
||||
# myst-parser
|
||||
markupsafe==3.0.3
|
||||
# via jinja2
|
||||
mdit-py-plugins==0.6.1
|
||||
# via myst-parser
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
myst-parser==4.0.1
|
||||
# via -r .github/docs/requirements.in
|
||||
packaging==26.3
|
||||
# via sphinx
|
||||
pygments==2.21.0
|
||||
# via sphinx
|
||||
pyyaml==6.0.3
|
||||
# via
|
||||
# myst-parser
|
||||
# sphinxcontrib-mermaid
|
||||
requests==2.34.2
|
||||
# via sphinx
|
||||
roman-numerals==4.1.0
|
||||
# via roman-numerals-py
|
||||
roman-numerals-py==4.1.0
|
||||
# via sphinx
|
||||
snowballstemmer==3.1.1
|
||||
# via sphinx
|
||||
sphinx==8.2.3
|
||||
# via
|
||||
# -r .github/docs/requirements.in
|
||||
# myst-parser
|
||||
# sphinx-rtd-theme
|
||||
# sphinxcontrib-jquery
|
||||
# sphinxcontrib-mermaid
|
||||
sphinx-rtd-theme==3.1.0
|
||||
# via -r .github/docs/requirements.in
|
||||
sphinxcontrib-applehelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-devhelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-htmlhelp==2.1.0
|
||||
# via sphinx
|
||||
sphinxcontrib-jquery==4.1
|
||||
# via sphinx-rtd-theme
|
||||
sphinxcontrib-jsmath==1.0.1
|
||||
# via sphinx
|
||||
sphinxcontrib-mermaid==2.1.1
|
||||
# via -r .github/docs/requirements.in
|
||||
sphinxcontrib-qthelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-serializinghtml==2.0.0
|
||||
# via sphinx
|
||||
urllib3==2.8.0
|
||||
# via requests
|
||||
@@ -3,8 +3,9 @@ name: Build Docker image
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
branches-ignore:
|
||||
- docs
|
||||
- 'docs/**'
|
||||
tags:
|
||||
- 'v*'
|
||||
# pull_request:
|
||||
@@ -14,6 +15,8 @@ env:
|
||||
|
||||
jobs:
|
||||
buildx:
|
||||
# Also prevent a manual dispatch on a docs repair branch from publishing.
|
||||
if: github.ref != 'refs/heads/docs' && !startsWith(github.ref, 'refs/heads/docs/')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Historical Sphinx documentation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [docs/restore-08-sphinx]
|
||||
paths: ['.github/docs/**', '.github/.readthedocs.yaml', '.github/workflows/docs.yml', 'docs', 'README.md']
|
||||
push:
|
||||
branches: [docs/restore-08-sphinx]
|
||||
paths: ['.github/docs/**', '.github/.readthedocs.yaml', '.github/workflows/docs.yml', 'docs', 'README.md']
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||
- run: git submodule update --init docs
|
||||
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: '0.11.3'
|
||||
python-version: '3.13'
|
||||
- name: Build the pinned historical user guide
|
||||
env:
|
||||
READTHEDOCS: 'True'
|
||||
READTHEDOCS_VERSION: v0.8.6rc1
|
||||
run: uv run --no-project --with-requirements .github/docs/requirements.txt sphinx-build -W --keep-going -b html -c .github/docs docs "$RUNNER_TEMP/docs-html"
|
||||
- name: Verify the version selector
|
||||
run: |
|
||||
grep -q 'class="version-switch"' "$RUNNER_TEMP/docs-html/index.html"
|
||||
grep -q 'readthedocs-addons-data-ready' "$RUNNER_TEMP/docs-html/_static/js/versions.js"
|
||||
@@ -164,7 +164,7 @@ class BaseModelWithStateMachine(models.Model, MachineMixin):
|
||||
|
||||
@classproperty
|
||||
def ACTIVE_STATE(cls) -> str:
|
||||
return cls._state_to_str(cls.StateMachineClass.active_state)
|
||||
return cls._state_to_str(cls.active_state)
|
||||
|
||||
@classproperty
|
||||
def INITIAL_STATE(cls) -> str:
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.1.2 on 2025-12-29 10:16
|
||||
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0074_alter_snapshot_downloaded_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='archiveresult',
|
||||
name='retry_at',
|
||||
field=models.DateTimeField(db_index=True, default=django.utils.timezone.now),
|
||||
),
|
||||
]
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
# Generated by Django 5.1.2 on 2025-12-29 10:16
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0075_archiveresult_retry_at'),
|
||||
('crawls', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='snapshot',
|
||||
name='crawl',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='snapshot_set', to='crawls.crawl'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='snapshot',
|
||||
name='retry_at',
|
||||
field=models.DateTimeField(db_index=True, default=django.utils.timezone.now),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='snapshot',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('queued', 'Queued'), ('started', 'Started'), ('sealed', 'Sealed')], db_index=True, default='queued', max_length=15),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='archiveresult',
|
||||
name='cmd',
|
||||
field=models.JSONField(blank=True, default=None, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='archiveresult',
|
||||
name='end_ts',
|
||||
field=models.DateTimeField(blank=True, default=None, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='archiveresult',
|
||||
name='extractor',
|
||||
field=models.CharField(choices=[('htmltotext', 'htmltotext'), ('git', 'git'), ('singlefile', 'singlefile'), ('media', 'media'), ('archive_org', 'archive_org'), ('readability', 'readability'), ('mercury', 'mercury'), ('favicon', 'favicon'), ('pdf', 'pdf'), ('headers', 'headers'), ('screenshot', 'screenshot'), ('dom', 'dom'), ('title', 'title'), ('wget', 'wget')], db_index=True, max_length=32),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='archiveresult',
|
||||
name='output',
|
||||
field=models.CharField(blank=True, default=None, max_length=1024, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='archiveresult',
|
||||
name='pwd',
|
||||
field=models.CharField(blank=True, default=None, max_length=256, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='archiveresult',
|
||||
name='snapshot',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.snapshot'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='archiveresult',
|
||||
name='start_ts',
|
||||
field=models.DateTimeField(blank=True, default=None, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='archiveresult',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('queued', 'Queued'), ('started', 'Started'), ('backoff', 'Waiting to retry'), ('succeeded', 'Succeeded'), ('failed', 'Failed'), ('skipped', 'Skipped')], db_index=True, default='queued', max_length=15),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,78 @@
|
||||
# Generated by Django 5.1.2 on 2025-12-29 10:16
|
||||
|
||||
import abid_utils.models
|
||||
import charidfield.fields
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import statemachine.mixins
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('core', '0075_archiveresult_retry_at'),
|
||||
('seeds', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CrawlSchedule',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('num_uses_failed', models.PositiveIntegerField(default=0)),
|
||||
('num_uses_succeeded', models.PositiveIntegerField(default=0)),
|
||||
('schedule', models.CharField(max_length=64)),
|
||||
('is_enabled', models.BooleanField(default=True)),
|
||||
('created_at', abid_utils.models.AutoDateTimeField(db_index=True, default=None)),
|
||||
('modified_at', models.DateTimeField(auto_now=True)),
|
||||
('created_by', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Crawl',
|
||||
fields=[
|
||||
('num_uses_failed', models.PositiveIntegerField(default=0)),
|
||||
('num_uses_succeeded', models.PositiveIntegerField(default=0)),
|
||||
('id', models.UUIDField(default=None, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='ID')),
|
||||
('abid', charidfield.fields.CharIDField(blank=True, db_index=True, default=None, help_text='ABID-format identifier for this entity (e.g. snp_01BJQMF54D093DXEAWZ6JYRPAQ)', max_length=30, null=True, prefix='crl_', unique=True)),
|
||||
('created_at', abid_utils.models.AutoDateTimeField(db_index=True, default=None)),
|
||||
('modified_at', models.DateTimeField(auto_now=True)),
|
||||
('status', models.CharField(choices=[('queued', 'Queued'), ('started', 'Started'), ('sealed', 'Sealed')], db_index=True, default='queued', max_length=15)),
|
||||
('retry_at', models.DateTimeField(db_index=True, default=django.utils.timezone.now)),
|
||||
('max_depth', models.PositiveSmallIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(4)])),
|
||||
('tags_str', models.CharField(blank=True, default='', max_length=1024)),
|
||||
('persona', models.CharField(blank=True, default='auto', max_length=32)),
|
||||
('config', models.JSONField(default=dict)),
|
||||
('created_by', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='crawl_set', to=settings.AUTH_USER_MODEL)),
|
||||
('seed', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='crawl_set', to='seeds.seed')),
|
||||
('schedule', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='crawls.crawlschedule')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Crawl',
|
||||
'verbose_name_plural': 'Crawls',
|
||||
},
|
||||
bases=(models.Model, statemachine.mixins.MachineMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Outlink',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=None, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='ID')),
|
||||
('src', models.URLField()),
|
||||
('dst', models.URLField()),
|
||||
('crawl', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='outlink_set', to='crawls.crawl')),
|
||||
('via', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='outlink_set', to='core.archiveresult')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('src', 'dst', 'via')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -80,8 +80,8 @@ def write_html_link_details(link: Link, out_dir: Optional[str]=None) -> None:
|
||||
|
||||
@enforce_types
|
||||
def link_details_template(link: Link) -> str:
|
||||
|
||||
from abx_plugin_wget_extractor.wget import wget_output_path
|
||||
|
||||
from abx_plugin_wget.wget import wget_output_path
|
||||
|
||||
SAVE_ARCHIVE_DOT_ORG = abx.pm.hook.get_FLAT_CONFIG().SAVE_ARCHIVE_DOT_ORG
|
||||
|
||||
|
||||
@@ -427,8 +427,7 @@ class Link:
|
||||
"""predict the expected output paths that should be present after archiving"""
|
||||
|
||||
from abx_plugin_wget.wget import wget_output_path
|
||||
|
||||
FAVICON_CONFIG = abx.pm.hook.get_CONFIGS().favicon
|
||||
from abx_plugin_favicon.config import FAVICON_CONFIG
|
||||
|
||||
# TODO: banish this awful duplication from the codebase and import these
|
||||
# from their respective extractor files
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# Generated by Django 5.1.2 on 2025-12-29 10:16
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('core', '0074_alter_snapshot_downloaded_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SinglefileResult',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('core.archiveresult',),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,40 @@
|
||||
# Generated by Django 5.1.2 on 2025-12-29 10:16
|
||||
|
||||
import abid_utils.models
|
||||
import charidfield.fields
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Seed',
|
||||
fields=[
|
||||
('num_uses_failed', models.PositiveIntegerField(default=0)),
|
||||
('num_uses_succeeded', models.PositiveIntegerField(default=0)),
|
||||
('id', models.UUIDField(default=None, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='ID')),
|
||||
('abid', charidfield.fields.CharIDField(blank=True, db_index=True, default=None, help_text='ABID-format identifier for this entity (e.g. snp_01BJQMF54D093DXEAWZ6JYRPAQ)', max_length=30, null=True, prefix='src_', unique=True)),
|
||||
('uri', models.URLField(max_length=2000)),
|
||||
('extractor', models.CharField(default='auto', max_length=32)),
|
||||
('tags_str', models.CharField(blank=True, default='', max_length=255)),
|
||||
('config', models.JSONField(default=dict)),
|
||||
('created_at', abid_utils.models.AutoDateTimeField(db_index=True, default=None)),
|
||||
('modified_at', models.DateTimeField(auto_now=True)),
|
||||
('created_by', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Seed',
|
||||
'verbose_name_plural': 'Seeds',
|
||||
'unique_together': {('created_by', 'uri', 'extractor')},
|
||||
},
|
||||
),
|
||||
]
|
||||
Reference in new issue
Block a user