abstract-block-dumper 0.0.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. abstract_block_dumper-0.0.5/.cruft.json +20 -0
  2. abstract_block_dumper-0.0.5/.github/dependabot.yml +12 -0
  3. abstract_block_dumper-0.0.5/.github/workflows/ci.yml +61 -0
  4. abstract_block_dumper-0.0.5/.github/workflows/publish.yml +122 -0
  5. abstract_block_dumper-0.0.5/.gitignore +17 -0
  6. abstract_block_dumper-0.0.5/.pre-commit-config.yaml +10 -0
  7. abstract_block_dumper-0.0.5/.shellcheckrc +2 -0
  8. abstract_block_dumper-0.0.5/CHANGELOG.md +34 -0
  9. abstract_block_dumper-0.0.5/PKG-INFO +311 -0
  10. abstract_block_dumper-0.0.5/README.md +289 -0
  11. abstract_block_dumper-0.0.5/SECURITY.md +52 -0
  12. abstract_block_dumper-0.0.5/docs/3rd_party/cookiecutter-rt-pkg/CHANGELOG.md +15 -0
  13. abstract_block_dumper-0.0.5/example_project/.dockerignore +31 -0
  14. abstract_block_dumper-0.0.5/example_project/.gitignore +1 -0
  15. abstract_block_dumper-0.0.5/example_project/Dockerfile +43 -0
  16. abstract_block_dumper-0.0.5/example_project/README.md +19 -0
  17. abstract_block_dumper-0.0.5/example_project/block_explorer/__init__.py +0 -0
  18. abstract_block_dumper-0.0.5/example_project/block_explorer/admin.py +1 -0
  19. abstract_block_dumper-0.0.5/example_project/block_explorer/apps.py +6 -0
  20. abstract_block_dumper-0.0.5/example_project/block_explorer/management/__init__.py +0 -0
  21. abstract_block_dumper-0.0.5/example_project/block_explorer/management/commands/__init__.py +0 -0
  22. abstract_block_dumper-0.0.5/example_project/block_explorer/management/commands/create_admin.py +24 -0
  23. abstract_block_dumper-0.0.5/example_project/block_explorer/migrations/__init__.py +0 -0
  24. abstract_block_dumper-0.0.5/example_project/block_explorer/models.py +1 -0
  25. abstract_block_dumper-0.0.5/example_project/block_explorer/tasks.py +27 -0
  26. abstract_block_dumper-0.0.5/example_project/block_explorer/tests.py +1 -0
  27. abstract_block_dumper-0.0.5/example_project/block_explorer/views.py +1 -0
  28. abstract_block_dumper-0.0.5/example_project/docker-compose.yml +123 -0
  29. abstract_block_dumper-0.0.5/example_project/example_project/__init__.py +3 -0
  30. abstract_block_dumper-0.0.5/example_project/example_project/asgi.py +16 -0
  31. abstract_block_dumper-0.0.5/example_project/example_project/celery.py +12 -0
  32. abstract_block_dumper-0.0.5/example_project/example_project/settings.py +135 -0
  33. abstract_block_dumper-0.0.5/example_project/example_project/urls.py +25 -0
  34. abstract_block_dumper-0.0.5/example_project/example_project/wsgi.py +16 -0
  35. abstract_block_dumper-0.0.5/example_project/main.py +6 -0
  36. abstract_block_dumper-0.0.5/example_project/manage.py +23 -0
  37. abstract_block_dumper-0.0.5/example_project/pyproject.toml +33 -0
  38. abstract_block_dumper-0.0.5/example_project/pytest.ini +4 -0
  39. abstract_block_dumper-0.0.5/example_project/uv.lock +1781 -0
  40. abstract_block_dumper-0.0.5/noxfile.py +208 -0
  41. abstract_block_dumper-0.0.5/pyproject.toml +166 -0
  42. abstract_block_dumper-0.0.5/src/abstract_block_dumper/__init__.py +0 -0
  43. abstract_block_dumper-0.0.5/src/abstract_block_dumper/_version.py +34 -0
  44. abstract_block_dumper-0.0.5/src/abstract_block_dumper/admin.py +73 -0
  45. abstract_block_dumper-0.0.5/src/abstract_block_dumper/apps.py +7 -0
  46. abstract_block_dumper-0.0.5/src/abstract_block_dumper/dal/__init__.py +0 -0
  47. abstract_block_dumper-0.0.5/src/abstract_block_dumper/dal/django_dal.py +150 -0
  48. abstract_block_dumper-0.0.5/src/abstract_block_dumper/dal/memory_registry.py +105 -0
  49. abstract_block_dumper-0.0.5/src/abstract_block_dumper/decorators.py +211 -0
  50. abstract_block_dumper-0.0.5/src/abstract_block_dumper/discovery.py +24 -0
  51. abstract_block_dumper-0.0.5/src/abstract_block_dumper/exceptions.py +16 -0
  52. abstract_block_dumper-0.0.5/src/abstract_block_dumper/management/__init__.py +0 -0
  53. abstract_block_dumper-0.0.5/src/abstract_block_dumper/management/commands/block_tasks.py +19 -0
  54. abstract_block_dumper-0.0.5/src/abstract_block_dumper/migrations/0001_initial.py +54 -0
  55. abstract_block_dumper-0.0.5/src/abstract_block_dumper/migrations/__init__.py +0 -0
  56. abstract_block_dumper-0.0.5/src/abstract_block_dumper/models.py +59 -0
  57. abstract_block_dumper-0.0.5/src/abstract_block_dumper/py.typed +0 -0
  58. abstract_block_dumper-0.0.5/src/abstract_block_dumper/services/__init__.py +0 -0
  59. abstract_block_dumper-0.0.5/src/abstract_block_dumper/services/block_processor.py +190 -0
  60. abstract_block_dumper-0.0.5/src/abstract_block_dumper/services/executor.py +54 -0
  61. abstract_block_dumper-0.0.5/src/abstract_block_dumper/services/scheduler.py +92 -0
  62. abstract_block_dumper-0.0.5/src/abstract_block_dumper/services/utils.py +51 -0
  63. abstract_block_dumper-0.0.5/src/abstract_block_dumper/tasks.py +75 -0
  64. abstract_block_dumper-0.0.5/tests/__init__.py +0 -0
  65. abstract_block_dumper-0.0.5/tests/conftest.py +65 -0
  66. abstract_block_dumper-0.0.5/tests/django_fixtures.py +16 -0
  67. abstract_block_dumper-0.0.5/tests/fatories.py +16 -0
  68. abstract_block_dumper-0.0.5/tests/integration/__init__.py +0 -0
  69. abstract_block_dumper-0.0.5/tests/integration/test_block_processor.py +55 -0
  70. abstract_block_dumper-0.0.5/tests/integration/test_concurrent_processing.py +52 -0
  71. abstract_block_dumper-0.0.5/tests/integration/test_multi_arguments_tasks.py +52 -0
  72. abstract_block_dumper-0.0.5/tests/integration/test_registered_celery_tasks.py +95 -0
  73. abstract_block_dumper-0.0.5/tests/integration/test_scheduler.py +153 -0
  74. abstract_block_dumper-0.0.5/tests/integration/test_task_registration.py +24 -0
  75. abstract_block_dumper-0.0.5/tests/settings.py +53 -0
  76. abstract_block_dumper-0.0.5/tests/unit/test_decorator.py +0 -0
  77. abstract_block_dumper-0.0.5/uv.lock +1993 -0
@@ -0,0 +1,20 @@
1
+ {
2
+ "template": "https://github.com/reef-technologies/cookiecutter-rt-pkg",
3
+ "commit": "f65b5ff3a36e7ba0e828e290b90e9bf1a92179fd",
4
+ "checkout": null,
5
+ "context": {
6
+ "cookiecutter": {
7
+ "package_name": "abstract_block_dumper",
8
+ "repository_github_url": "https://github.com/bactensor/abstract-block-dumper",
9
+ "is_django_package": "y",
10
+ "build_docker_image": "n",
11
+ "_jinja2_env_vars": {
12
+ "block_start_string": "# COOKIECUTTER{%",
13
+ "block_end_string": "%}"
14
+ },
15
+ "_template": "https://github.com/reef-technologies/cookiecutter-rt-pkg",
16
+ "_commit": "f65b5ff3a36e7ba0e828e290b90e9bf1a92179fd"
17
+ }
18
+ },
19
+ "directory": null
20
+ }
@@ -0,0 +1,12 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/" # specify the directory to scan for dependency files, e.g., "/"
5
+ schedule:
6
+ interval: "daily"
7
+ open-pull-requests-limit: 0 # Only security updates will be opened as PRs
8
+ - package-ecosystem: "docker"
9
+ directory: "/" # specify the directory to scan for dependency files, e.g., "/"
10
+ schedule:
11
+ interval: "weekly"
12
+ open-pull-requests-limit: 0 # Only security updates will be opened as PRs
@@ -0,0 +1,61 @@
1
+ name: Continuous Integration
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+ branches: [master, main]
8
+
9
+ env:
10
+ PYTHON_DEFAULT_VERSION: "3.12"
11
+
12
+ jobs:
13
+ linter:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ with:
18
+ fetch-depth: 0
19
+ - name: Set up Python ${{ env.PYTHON_DEFAULT_VERSION }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
23
+ cache: "pip"
24
+ cache-dependency-path: 'uv.lock'
25
+ - uses: actions/cache@v4
26
+ with:
27
+ path: ~/.cache/uv
28
+ key: ${{ env.PYTHON_DEFAULT_VERSION }}-uv-${{ hashFiles('uv.lock') }}
29
+ restore-keys: ${{ env.PYTHON_DEFAULT_VERSION }}-uv-
30
+ - name: Install dependencies
31
+ run: python -m pip install --upgrade nox uv
32
+ - name: Run linters
33
+ run: nox -vs lint
34
+ test:
35
+ timeout-minutes: 10
36
+ runs-on: ${{ matrix.os }}
37
+ strategy:
38
+ fail-fast: false
39
+ matrix:
40
+ os: ["ubuntu-latest"]
41
+ python-version: ["3.11", "3.12"]
42
+ django-version: ["3.2", "4.2"]
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+ with:
46
+ fetch-depth: 0
47
+ - name: Set up Python ${{ matrix.python-version }}
48
+ uses: actions/setup-python@v5
49
+ with:
50
+ python-version: ${{ matrix.python-version }}
51
+ cache: 'pip'
52
+ cache-dependency-path: 'uv.lock'
53
+ - uses: actions/cache@v4
54
+ with:
55
+ path: ~/.cache/uv
56
+ key: ${{ matrix.python-version }}-uv-${{ hashFiles('uv.lock') }}
57
+ restore-keys: ${{ matrix.python-version }}-uv-
58
+ - name: Install dependencies
59
+ run: python -m pip install --upgrade 'nox==2024.3.2' uv
60
+ - name: Run unit tests
61
+ run: nox -vs test
@@ -0,0 +1,122 @@
1
+ name: "Continuous Deployment"
2
+
3
+ # This workflow requires https://docs.pypi.org/trusted-publishers/ to be enabled for the repository.
4
+ # Follow instructions from this link to enable it.
5
+ # Use this workflow (`publish.yml`) in the configuration.
6
+ # Please note this process has to be repeated for Test PyPI and PyPI separately.
7
+
8
+ on:
9
+ push:
10
+ tags:
11
+ - 'v*' # push events to matching v*, i.e. v1.0, v20.15.10
12
+ - 'draft/v*'
13
+
14
+ env:
15
+ PYTHON_DEFAULT_VERSION: "3.12"
16
+
17
+ jobs:
18
+ # Job to get version from tag
19
+ get-version:
20
+ runs-on: ubuntu-latest
21
+ outputs:
22
+ version: ${{ steps.get-version.outputs.version }}
23
+ draft: ${{ steps.get-version.outputs.draft }}
24
+ prerelease: ${{ steps.get-version.outputs.prerelease }}
25
+ is_latest_version: ${{ steps.highest-version.outputs.is_latest_version }}
26
+ steps:
27
+ - name: Get version from tag
28
+ id: get-version
29
+ run: |
30
+ if [[ ${{ github.ref }} == refs/tags/v* ]]; then
31
+ echo "draft=false" >> "$GITHUB_OUTPUT"
32
+ echo "version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"
33
+ else
34
+ echo "draft=true" >> "$GITHUB_OUTPUT"
35
+ echo "version=${GITHUB_REF#refs/tags/draft/v}" >> "$GITHUB_OUTPUT"
36
+ fi
37
+ export IS_PRERELEASE=$([[ ${{ github.ref }} =~ [^0-9]$ ]] && echo true || echo false)
38
+ echo "prerelease=$IS_PRERELEASE" >> $GITHUB_OUTPUT
39
+
40
+ - uses: actions/checkout@v4
41
+ with:
42
+ fetch-depth: 0
43
+
44
+ - name: Get the highest version in the repository
45
+ id: highest-version
46
+ run: |
47
+ git fetch --tags
48
+ highest_version=$(git tag --sort=-v:refname | grep -E '^v[0-9]+' | head -n 1)
49
+ echo "highest_version=${highest_version#v}" >> "$GITHUB_OUTPUT"
50
+ if [[ ${{ steps.get-version.outputs.version }} == "${highest_version#v}" ]] && [ "${{ steps.get-version.outputs.draft }}" == "false" ]; then
51
+ echo "is_latest_version=true" >> "$GITHUB_OUTPUT"
52
+ else
53
+ echo "is_latest_version=false" >> "$GITHUB_OUTPUT"
54
+ fi
55
+
56
+ # Job for Python package publishing
57
+ publish-python:
58
+ needs: get-version
59
+ env:
60
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
61
+ permissions:
62
+ id-token: write # allows publishing to PyPI
63
+ contents: write # allows uploading a GitHub release
64
+ runs-on: ubuntu-latest
65
+ steps:
66
+ - uses: actions/checkout@v4
67
+ with:
68
+ fetch-depth: 0
69
+
70
+ - name: Set up Python ${{ env.PYTHON_DEFAULT_VERSION }}
71
+ uses: actions/setup-python@v5
72
+ with:
73
+ python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
74
+
75
+ - name: Install dependencies
76
+ run: python -m pip install --upgrade nox uv
77
+
78
+ - name: Read the Changelog
79
+ id: read-changelog
80
+ uses: mindsers/changelog-reader-action@v2
81
+ with:
82
+ version: ${{ needs.get-version.outputs.version }}
83
+ path: ./CHANGELOG.md
84
+ continue-on-error: ${{ fromJSON(needs.get-version.outputs.draft) }}
85
+
86
+ - name: Build
87
+ run: uv build
88
+
89
+ - name: Sign distribution
90
+ uses: sigstore/gh-action-sigstore-python@v3.1.0
91
+ with:
92
+ inputs: >-
93
+ dist/*.tar.gz
94
+ dist/*.whl
95
+
96
+ - name: Create GitHub release
97
+ id: create-release
98
+ uses: softprops/action-gh-release@v2
99
+ with:
100
+ name: ${{ needs.get-version.outputs.version }}
101
+ body: ${{ steps.read-changelog.outputs.changes }}
102
+ draft: ${{ fromJSON(needs.get-version.outputs.draft) }}
103
+ prerelease: ${{ fromJSON(needs.get-version.outputs.prerelease) }}
104
+ files: >-
105
+ dist/*.tar.gz
106
+ dist/*.whl
107
+ dist/*.sigstore
108
+
109
+ - name: Remove signature files as pypa/gh-action-pypi-publish does not support them
110
+ run: rm -f dist/*.sigstore dist/*.sigstore.json
111
+
112
+ - name: Publish distribution 📦 to TestPyPI
113
+ if: ${{ needs.get-version.outputs.draft == 'true' }}
114
+ uses: pypa/gh-action-pypi-publish@release/v1
115
+ with:
116
+ repository-url: https://test.pypi.org/legacy/
117
+
118
+ - name: Publish distribution 📦 to PyPI
119
+ if: ${{ needs.get-version.outputs.draft == 'false' }}
120
+ uses: pypa/gh-action-pypi-publish@release/v1
121
+
122
+
@@ -0,0 +1,17 @@
1
+ *.pyc
2
+ *.sqlite3
3
+ *~
4
+ *.egg-info/
5
+ /.idea/
6
+ .env
7
+ .venv
8
+ venv
9
+ media/
10
+ .backups/
11
+ .envrc
12
+ .python-version
13
+ .terraform.lock.hcl
14
+ .terraform/
15
+ .nox/
16
+ __pycache__
17
+ src/abstract_block_dumper/_version.py
@@ -0,0 +1,10 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ # Ruff version.
4
+ rev: v0.13.0
5
+ hooks:
6
+ # Run the linter with auto-fix
7
+ - id: ruff
8
+ args: [--fix]
9
+ # Run the formatter
10
+ - id: ruff-format
@@ -0,0 +1,2 @@
1
+ # disable errors related to cookiecutter templating:
2
+ disable=SC1054,SC1056,SC1072,SC1073,SC1083,SC1009
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the changes for the
8
+ upcoming release can be found in [changelog.d](changelog.d).
9
+
10
+ <!-- towncrier release notes start -->
11
+
12
+ ## [0.0.5](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.5) - 2025-10-21
13
+
14
+ No significant changes.
15
+
16
+
17
+ ## [0.0.4](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.4) - 2025-10-21
18
+
19
+ No significant changes.
20
+
21
+
22
+ ## [0.0.3](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.3) - 2025-10-21
23
+
24
+ No significant changes.
25
+
26
+
27
+ ## [0.0.2](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.2) - 2025-10-21
28
+
29
+ No significant changes.
30
+
31
+
32
+ ## [0.0.1](https://github.com/bactensor/abstract-block-dumper/releases/tag/v0.0.1) - 2025-10-21
33
+
34
+ No significant changes.
@@ -0,0 +1,311 @@
1
+ Metadata-Version: 2.4
2
+ Name: abstract-block-dumper
3
+ Version: 0.0.5
4
+ Project-URL: Source, https://github.com/bactensor/abstract-block-dumper
5
+ Project-URL: Issue Tracker, https://github.com/bactensor/abstract-block-dumper/issues
6
+ Author-email: Reef Technologies <opensource@reef.pl>
7
+ License: MIT
8
+ Classifier: Framework :: Django
9
+ Classifier: Framework :: Django :: 4.2
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Requires-Python: >=3.11
17
+ Requires-Dist: bittensor>=9.10.1
18
+ Requires-Dist: celery>=5.5.3
19
+ Requires-Dist: django<6.0,>=3.2
20
+ Requires-Dist: structlog>=25.4.0
21
+ Description-Content-Type: text/markdown
22
+
23
+ # Abstract Block Dumper
24
+ &nbsp;[![Continuous Integration](https://github.com/bactensor/abstract-block-dumper/workflows/Continuous%20Integration/badge.svg)](https://github.com/bactensor/abstract-block-dumper/actions?query=workflow%3A%22Continuous+Integration%22)&nbsp;[![License](https://img.shields.io/pypi/l/abstract_block_dumper.svg?label=License)](https://pypi.python.org/pypi/abstract_block_dumper)&nbsp;[![python versions](https://img.shields.io/pypi/pyversions/abstract_block_dumper.svg?label=python%20versions)](https://pypi.python.org/pypi/abstract_block_dumper)&nbsp;[![PyPI version](https://img.shields.io/pypi/v/abstract_block_dumper.svg?label=PyPI%20version)](https://pypi.python.org/pypi/abstract_block_dumper)
25
+
26
+ This package provides a simplified framework for creating block processing tasks in Django applications.
27
+ Define tasks with lambda conditions using the @block_task decorator and run them asynchronously with Celery.
28
+
29
+ ## Implementation Details
30
+
31
+ ### General Workflow:
32
+ Register functions -> detect new blocks -> evaluate conditions -> send to Celery -> execute -> track results -> handle retries.
33
+
34
+
35
+ ### WorkflowSteps
36
+ 1. Register
37
+ - Functions are automatically discovered when the scheduler starts
38
+ - Functions must be located in installed apps in tasks.py or block_tasks.py
39
+ - Functions marked with @block_task decorators are stored in memory registry
40
+
41
+ 2. Detect Blocks
42
+ - Scheduler is running by management command block_tasks
43
+ - Scheduler polls blockchain, finds new blocks, and batches them
44
+
45
+ 3. Plan Tasks
46
+ - For each block, lambda conditions are evaluated against registered functions
47
+ - Tasks are created for matching conditions (with optional multiple argument sets)
48
+
49
+ 4. Queue
50
+ Tasks are sent to Celery with queue and timeout settings from celery_kwargs
51
+
52
+ 5. Execute
53
+ Celery runs the function with block info, capturing results and errors
54
+
55
+ 6. Track
56
+ Task attempts are stored in TaskAttempt model with retry logic and state tracking
57
+
58
+
59
+ ## Prerequisites
60
+ - Django
61
+ - Celery
62
+ - Redis (for Celery broker and result backend)
63
+ - PostgreSQL (recommended for production)
64
+
65
+ ## Installation
66
+
67
+ 1. Install the package:
68
+ ```bash
69
+ pip install abstract_block_dumper
70
+ ```
71
+
72
+ 2. Add to your Django `INSTALLED_APPS`:
73
+ ```python
74
+ INSTALLED_APPS = [
75
+ # ... other apps
76
+ 'abstract_block_dumper',
77
+ ]
78
+ ```
79
+
80
+ 3. Run migrations:
81
+ ```bash
82
+ python manage.py migrate
83
+ ```
84
+
85
+ ## Usage
86
+
87
+ ### 1. Define Block Processing Tasks
88
+ Create block processing tasks in `tasks.py` or `block_tasks.py` file inside any of your installed Django apps.
89
+
90
+ ### 2. Use Decorators to Register Tasks
91
+ - Use `@block_task` with lambda conditions to create custom block processing tasks
92
+
93
+ ### 3. Start the Block Scheduler
94
+ Run the scheduler to start processing blocks:
95
+ ```bash
96
+ $ python manage.py block_tasks
97
+ ```
98
+
99
+ This command will:
100
+ - Automatically discover and register all decorated functions
101
+ - Start polling the blockchain for new blocks
102
+ - Schedule tasks based on your lambda conditions
103
+
104
+ ### 4. Start Celery Workers
105
+ In separate terminals, start Celery workers to execute tasks:
106
+ ```bash
107
+ $ celery -A your_project worker --loglevel=info
108
+ ```
109
+
110
+ See examples below:
111
+
112
+ Use the `@block_task` decorator with lambda conditions to create block processing tasks:
113
+
114
+ ```python
115
+ from abstract_block_dumper.decorators import block_task
116
+
117
+
118
+ # Process every block
119
+ @block_task(condition=lambda bn: True)
120
+ def process_every_block(block_number: int):
121
+ print(f"Processing every block: {block_number}")
122
+
123
+ # Process every 10 blocks
124
+ @block_task(condition=lambda bn: bn % 10 == 0)
125
+ def process_every_10_blocks(block_number: int):
126
+ print(f"Processing every 10 blocks: {block_number}")
127
+
128
+ # Process with multiple netuids
129
+ @block_task(
130
+ condition=lambda bn, netuid: bn % 100 == 0,
131
+ args=[{"netuid": 1}, {"netuid": 3}, {"netuid": 22}],
132
+ backfilling_lookback=300,
133
+ celery_kwargs={"queue": "high-priority"}
134
+ )
135
+ def process_multi_netuid_task(block_number: int, netuid: int):
136
+ print(f"Processing block {block_number} for netuid: {netuid}")
137
+ ```
138
+
139
+
140
+ ## Maintenance Tasks
141
+
142
+ ### Cleanup Old Task Attempts
143
+
144
+ The framework provides a maintenance task to clean up old task records and maintain database performance:
145
+
146
+ ```python
147
+ from abstract_block_dumper.tasks import cleanup_old_tasks
148
+
149
+ # Delete tasks older than 7 days (default)
150
+ cleanup_old_tasks.delay()
151
+
152
+ # Delete tasks older than 30 days
153
+ cleanup_old_tasks.delay(days=30)
154
+ ```
155
+
156
+ This task deletes all succeeded or unrecoverable failed tasks older than the specified number of days. It never deletes tasks with PENDING or RUNNING status to ensure ongoing work is preserved.
157
+
158
+ #### Running the Cleanup Task
159
+
160
+ **Option 1: Manual Execution**
161
+ ```bash
162
+ # Using Django shell
163
+ python manage.py shell -c "from abstract_block_dumper.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
164
+ ```
165
+
166
+ **Option 2: Cron Job (Recommended - once per day)**
167
+ ```bash
168
+ # Add to crontab (daily at 2 AM)
169
+ 0 2 * * * cd /path/to/your/project && python manage.py shell -c "from abstract_block_dumper.tasks import cleanup_old_tasks; cleanup_old_tasks.delay()"
170
+ ```
171
+
172
+ **Option 3: Celery Beat (Automated Scheduling)**
173
+
174
+ Add this to your Django `settings.py`:
175
+
176
+ ```python
177
+ from celery.schedules import crontab
178
+
179
+ CELERY_BEAT_SCHEDULE = {
180
+ 'cleanup-old-tasks': {
181
+ 'task': 'abstract_block_dumper.cleanup_old_tasks',
182
+ 'schedule': crontab(hour=2, minute=0), # Daily at 2 AM
183
+ 'kwargs': {'days': 7}, # Customize retention period
184
+ },
185
+ }
186
+ ```
187
+
188
+ Then start the Celery beat scheduler:
189
+ ```bash
190
+ celery -A your_project beat --loglevel=info
191
+ ```
192
+
193
+ ## Configuration
194
+
195
+ ### Required Django Settings
196
+
197
+ Add these settings to your Django `settings.py`:
198
+
199
+ ```python
200
+ # Celery Configuration
201
+ CELERY_BROKER_URL = 'redis://localhost:6379/0'
202
+ CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
203
+
204
+ # Abstract Block Dumper specific settings
205
+ BITTENSOR_NETWORK = 'finney' # Options: 'finney', 'local', 'testnet', 'mainnet'
206
+ BLOCK_DUMPER_START_FROM_BLOCK = 'current' # Options: None, 'current', or int
207
+ BLOCK_DUMPER_POLL_INTERVAL = 1 # seconds between polling for new blocks
208
+ BLOCK_TASK_RETRY_BACKOFF = 2 # minutes for retry backoff base
209
+ BLOCK_DUMPER_MAX_ATTEMPTS = 3 # maximum retry attempts
210
+ BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 1440 # maximum retry delay (24 hours)
211
+ ```
212
+
213
+ ### Configuration Options Reference
214
+
215
+ #### Core Settings
216
+
217
+ **BITTENSOR_NETWORK** (str, default: `'finney'`) Specifies which [Bittensor network](https://docs.learnbittensor.org/concepts/bittensor-networks) to connect to
218
+
219
+ **BLOCK_DUMPER_START_FROM_BLOCK** (str|int|None, default: `None`)
220
+ - **Purpose**: Determines the starting block for processing when the scheduler first runs
221
+ - **Valid Values**:
222
+ - `None`: Resume from the last processed block stored in database
223
+ - `'current'`: Start from the current blockchain block (skips historical blocks)
224
+ - `int`: Start from a specific block number (e.g., `1000000`)
225
+ - **Example**: `BLOCK_DUMPER_START_FROM_BLOCK = 'current'`
226
+ - **Performance Impact**: Starting from historical blocks may require significant processing time
227
+
228
+ #### Scheduler Settings
229
+
230
+ **BLOCK_DUMPER_POLL_INTERVAL** (int, default: `1`)
231
+ - **Purpose**: Seconds to wait between checking for new blocks
232
+ - **Valid Range**: `1` to `3600` (1 second to 1 hour)
233
+ - **Example**: `BLOCK_DUMPER_POLL_INTERVAL = 5`
234
+ - **Performance Impact**:
235
+ - Lower values (1-2s): Near real-time processing, higher CPU/network usage
236
+ - Higher values (10-60s): Reduced load but delayed processing
237
+ - Very low values (<1s) may cause rate limiting
238
+
239
+ #### Retry and Error Handling Settings
240
+
241
+ **BLOCK_DUMPER_MAX_ATTEMPTS** (int, default: `3`)
242
+ - **Purpose**: Maximum number of attempts to retry a failed task before giving up
243
+ - **Valid Range**: `1` to `10`
244
+ - **Example**: `BLOCK_DUMPER_MAX_ATTEMPTS = 5`
245
+ - **Performance Impact**: Higher values increase resilience but may delay failure detection
246
+
247
+ **BLOCK_TASK_RETRY_BACKOFF** (int, default: `1`)
248
+ - **Purpose**: Base number of minutes for exponential backoff retry delays
249
+ - **Valid Range**: `1` to `60`
250
+ - **Example**: `BLOCK_TASK_RETRY_BACKOFF = 2`
251
+ - **Calculation**: Actual delay = `backoff ** attempt_count` minutes
252
+ - Attempt 1: 2¹ = 2 minutes
253
+ - Attempt 2: 2² = 4 minutes
254
+ - Attempt 3: 2³ = 8 minutes
255
+ - **Performance Impact**: Lower values retry faster but may overwhelm failing services
256
+
257
+ **BLOCK_TASK_MAX_RETRY_DELAY_MINUTES** (int, default: `1440`)
258
+ - **Purpose**: Maximum delay (in minutes) between retry attempts, caps exponential backoff
259
+ - **Valid Range**: `1` to `10080` (1 minute to 1 week)
260
+ - **Example**: `BLOCK_TASK_MAX_RETRY_DELAY_MINUTES = 720` # 12 hours max
261
+ - **Performance Impact**: Prevents extremely long delays while maintaining backoff benefits
262
+
263
+
264
+ ## Example Project
265
+
266
+ The repository includes a complete working example in the `example_project/` directory that demonstrates:
267
+
268
+ - Django application setup with abstract-block-dumper
269
+ - Multiple task types (`@every_block`, `@every_n_blocks` with different configurations)
270
+ - Error handling with a randomly failing task
271
+ - Docker Compose setup with all required services
272
+ - Monitoring with Flower (Celery monitoring tool)
273
+
274
+ ### Running the Example
275
+
276
+ ```bash
277
+ cd example_project
278
+ docker-compose up --build
279
+ ```
280
+
281
+ This starts:
282
+ - **Django application** (http://localhost:8000) - Admin interface (user: `admin`, password: `admin`)
283
+ - **Celery workers** - Execute block processing tasks
284
+ - **Block scheduler** - Monitors blockchain and schedules tasks
285
+ - **Flower monitoring** (http://localhost:5555) - Monitor Celery tasks
286
+ - **Redis & PostgreSQL** - Required services
287
+
288
+
289
+ ## Development
290
+
291
+
292
+ Pre-requisites:
293
+ - [uv](https://docs.astral.sh/uv/)
294
+ - [nox](https://nox.thea.codes/en/stable/)
295
+ - [docker](https://www.docker.com/) and [docker compose plugin](https://docs.docker.com/compose/)
296
+
297
+
298
+ Ideally, you should run `nox -t format lint` before every commit to ensure that the code is properly formatted and linted.
299
+ Before submitting a PR, make sure that tests pass as well, you can do so using:
300
+ ```
301
+ nox -t check # equivalent to `nox -t format lint test`
302
+ ```
303
+
304
+ If you wish to install dependencies into `.venv` so your IDE can pick them up, you can do so using:
305
+ ```
306
+ uv sync --all-extras --dev
307
+ ```
308
+
309
+ ### Release process
310
+
311
+ Run `nox -s make_release -- X.Y.Z` where `X.Y.Z` is the version you're releasing and follow the printed instructions.