├── .coveragerc ├── .editorconfig ├── .github └── workflows │ ├── release.yml │ └── tests.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── README_IMAGES ├── django-celerybeat-status-admin.png └── django-celerybeat-status-tasks.png ├── celerybeat_status ├── __init__.py ├── admin.py ├── apps.py ├── helpers.py ├── templates │ └── celerybeat_status │ │ ├── custom_admin │ │ ├── index.html │ │ ├── sidebar.html │ │ └── sidebar_widgets │ │ │ ├── actions.html │ │ │ └── statuscheck.html │ │ └── periodic_tasks_status_list.html ├── tests │ ├── __init__.py │ ├── test_views.py │ └── utils.py ├── urls.py └── views.py ├── manage.py ├── requirements_test.txt ├── runtests.py ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── settings.py └── urls.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = true 3 | 4 | [report] 5 | omit = 6 | *site-packages* 7 | *tests* 8 | *migrations* 9 | *.tox 10 | celerybeat_status/urls.py 11 | celerybeat_status/admin.py 12 | celerybeat_status/apps.py 13 | show_missing = True 14 | exclude_lines = 15 | raise NotImplementedError -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | end_of_line = lf 12 | 13 | [*.{json,html,md,yml,yaml}] 14 | indent_size = 2 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | 19 | [*.ini] 20 | indent_style = tab 21 | 22 | [Makefile] 23 | indent_style = tab 24 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release - Publish to PyPI 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v4 14 | - name: Set up Python 15 | uses: actions/setup-python@v5 16 | with: 17 | python-version: "3.x" 18 | - name: Install dependencies 19 | run: | 20 | python -m pip install --upgrade pip 21 | pip install build twine 22 | - name: Build package 23 | run: | 24 | python -m build 25 | twine check dist/* 26 | - name: Publish package 27 | run: twine upload dist/* 28 | env: 29 | TWINE_USERNAME: __token__ 30 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 31 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Test (Python ${{ matrix.python-version }}, Django ${{ matrix.django-version }}) 8 | 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | matrix: 13 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 14 | django-version: ["4.2", "5.0"] 15 | 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v4 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install -r requirements_test.txt 27 | - name: Run tests with coverage 28 | run: tox -- --keepdb --parallel 29 | env: 30 | COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.db 3 | *~ 4 | 5 | 6 | /site/ 7 | /htmlcov/ 8 | /coverage/ 9 | /build/ 10 | /dist/ 11 | /*.egg-info/ 12 | /env/ 13 | MANIFEST 14 | coverage.* 15 | .coverage 16 | .tox 17 | .vscode/ 18 | .python-version 19 | celerybeat-schedule 20 | celerybeat-schedule.* 21 | *.sqlite 22 | *.sqlite3 23 | 24 | !.gitignore 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Vinta Serviços e Soluções Tecnológicas Ltda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django Celery Beat Status 2 | 3 | ![PyPI - Version](https://img.shields.io/pypi/v/django-celerybeat-status) 4 | ![Supported Python Versions](https://img.shields.io/pypi/pyversions/django-celerybeat-status.svg) 5 | ![Supported Django Versions](https://img.shields.io/pypi/frameworkversions/django/django-celerybeat-status.svg) 6 | [![Build Status](https://github.com/vintasoftware/django-celerybeat-status/actions/workflows/tests.yml/badge.svg)](https://github.com/vintasoftware/django-celerybeat-status/actions/workflows/tests.yml) 7 | [![Coverage Status](https://coveralls.io/repos/github/vintasoftware/django-celerybeat-status/badge.svg?branch=main)](https://coveralls.io/github/vintasoftware/django-celerybeat-status?branch=main) 8 | 9 | A library that integrates with django admin and shows in a simple GUI when your periodic are going to run next. 10 | 11 | ## Instalation 12 | 13 | ```bash 14 | pip install django-celerybeat-status 15 | ``` 16 | 17 | ## Configuration 18 | 19 | 1. Add `"celerybeat_status"` to your `INSTALLED_APPS` variable in django settings 20 | 21 | ```python 22 | INSTALLED_APPS = [ 23 | ... 24 | "celerybeat_status", 25 | ] 26 | ``` 27 | 28 | 2. Create a url for the status check view 29 | 30 | ```python 31 | from django.urls import include, path 32 | 33 | urlpatterns = [ 34 | # other urls... 35 | path("admin/statuscheck/", include("celerybeat_status.urls")), # celerybeat_status admin 36 | path("admin/", admin.site.urls), # django admin 37 | ] 38 | ``` 39 | 40 | ## Usage 41 | 42 | Check your tasks under `/admin/statuscheck/periodic-tasks/` (if you configured your urls the way we suggested in this docs). 43 | 44 | You can also find a link in `/admin` sidebar. 45 | 46 | How you admin page will look like: 47 | 48 | ![admin-page](https://raw.githubusercontent.com/vintasoftware/django-celerybeat-status/master/README_IMAGES/django-celerybeat-status-admin.png) 49 | 50 | How your tasks will be shown: 51 | 52 | ![tasks-page](https://raw.githubusercontent.com/vintasoftware/django-celerybeat-status/master/README_IMAGES/django-celerybeat-status-tasks.png) 53 | 54 | ## Contributing 55 | 56 | ### Setting up the development environment 57 | 58 | 1. Clone the repository. 59 | 60 | 2. Create a virtual environment. 61 | 62 | 3. Install the dependencies. 63 | 64 | ```bash 65 | pip install -r requirements_test.txt 66 | ``` 67 | 68 | 4. Run the project. Relevant to check UI changes. 69 | 70 | ```bash 71 | # Create the database and run the migrations. 72 | python manage.py migrate 73 | # Create a superuser. This will allow you to access the admin interface. 74 | python manage.py createsuperuser 75 | # Start the development server. You can view the application by navigating to the URL provided in the terminal. 76 | python manage.py runserver 77 | ``` 78 | 79 | 5. Run the tests. This package uses `tox` to run tests on multiple evironments, please make sure they are passing before submitting a pull request. 80 | 81 | ```bash 82 | tox 83 | ``` 84 | 85 | ## Commercial Support 86 | 87 | This project, as other Vinta open-source projects, is used in products of Vinta clients. We are always looking for exciting work, so if you need any commercial support, feel free to get in touch: contact@vinta.com.br 88 | 89 | Copyright (c) 2017 Vinta Serviços e Soluções Tecnológicas Ltda 90 | -------------------------------------------------------------------------------- /README_IMAGES/django-celerybeat-status-admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vintasoftware/django-celerybeat-status/c75c219ea85656030c266d665a36c4e1ff2251b3/README_IMAGES/django-celerybeat-status-admin.png -------------------------------------------------------------------------------- /README_IMAGES/django-celerybeat-status-tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vintasoftware/django-celerybeat-status/c75c219ea85656030c266d665a36c4e1ff2251b3/README_IMAGES/django-celerybeat-status-tasks.png -------------------------------------------------------------------------------- /celerybeat_status/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 2 | __title__ = "CeleryBeat Status" 3 | __version__ = "1.0.1" 4 | __author__ = "Vinta Software" 5 | __license__ = "MIT License" 6 | __copyright__ = "Copyright 2017 Vinta Serviços e Soluções Tecnológicas Ltda" 7 | 8 | # Version synonym 9 | VERSION = __version__ 10 | -------------------------------------------------------------------------------- /celerybeat_status/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib.admin.sites import AdminSite 2 | 3 | AdminSite.index_template = "celerybeat_status/custom_admin/index.html" 4 | -------------------------------------------------------------------------------- /celerybeat_status/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CeleryBeatStatusConfig(AppConfig): 5 | name = "celerybeat_status" 6 | -------------------------------------------------------------------------------- /celerybeat_status/helpers.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import json 3 | 4 | from celery.beat import Service 5 | from django.utils import timezone 6 | 7 | 8 | def get_periodic_tasks_info(): 9 | from celery import current_app 10 | 11 | schedule = Service(current_app).get_scheduler().get_schedule() 12 | tasks = [] 13 | for key, entry in schedule.items(): 14 | # entry.is_due() returns (is_due, time_in_seconds_for_next_execution) 15 | is_due_tpl = entry.is_due() 16 | 17 | next_execution = timezone.now() + datetime.timedelta(seconds=is_due_tpl[1]) 18 | 19 | # remove delay between the timezone.now and the schedule entry due date 20 | next_execution = next_execution.replace(microsecond=0) 21 | 22 | tasks.append( 23 | { 24 | "name": key, 25 | "task": entry.task, 26 | "args": "(" + ", ".join([json.dumps(arg) for arg in entry.args]) + ")", 27 | "kwargs": json.dumps(entry.kwargs), 28 | "is_due": is_due_tpl[0], 29 | "next_execution": next_execution, 30 | } 31 | ) 32 | 33 | return tasks 34 | -------------------------------------------------------------------------------- /celerybeat_status/templates/celerybeat_status/custom_admin/index.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/index.html" %} 2 | {% load i18n static %} 3 | 4 | {% block sidebar %} 5 | {% include "celerybeat_status/custom_admin/sidebar.html" %} 6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /celerybeat_status/templates/celerybeat_status/custom_admin/sidebar.html: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /celerybeat_status/templates/celerybeat_status/custom_admin/sidebar_widgets/actions.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 |
4 |

{% trans 'Recent actions' %}

5 |

{% trans 'My actions' %}

6 | {% load log %} 7 | {% get_admin_log 10 as admin_log for_user user %} 8 | {% if not admin_log %} 9 |

{% trans 'None available' %}

10 | {% else %} 11 | 28 | {% endif %} 29 |
30 | -------------------------------------------------------------------------------- /celerybeat_status/templates/celerybeat_status/custom_admin/sidebar_widgets/statuscheck.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 9 | -------------------------------------------------------------------------------- /celerybeat_status/templates/celerybeat_status/periodic_tasks_status_list.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/index.html" %} 2 | {% load i18n static %} 3 | 4 | {% block extrastyle %}{{ block.super }}{% endblock %} 5 | 6 | {% block coltype %}colMS{% endblock %} 7 | 8 | {% block bodyclass %}{{ block.super }} dashboard{% endblock %} 9 | 10 | {% block breadcrumbs %} 11 | 15 | {% endblock %} 16 | 17 | {% block content %} 18 |
19 | 20 | 34 | 35 | 36 |
37 | {% endblock %} 38 | 39 | 40 | {% block sidebar %}{% endblock %} 41 | -------------------------------------------------------------------------------- /celerybeat_status/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vintasoftware/django-celerybeat-status/c75c219ea85656030c266d665a36c4e1ff2251b3/celerybeat_status/tests/__init__.py -------------------------------------------------------------------------------- /celerybeat_status/tests/test_views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.urls import reverse 3 | 4 | from celerybeat_status.tests.utils import BaseTestCase, TestRequiresAuthenticatedUser 5 | 6 | User = get_user_model() 7 | 8 | 9 | class PeriodicTasksStatusListViewTests(TestRequiresAuthenticatedUser, BaseTestCase): 10 | view_name = "celerybeat_status:periodic-tasks-status" 11 | 12 | def setUp(self): 13 | super().setUp() 14 | self.view_url = reverse(self.view_name) 15 | 16 | def test_non_authenticated_user_is_redirected(self): 17 | response = self.client.get(self.view_url) 18 | self.assertRedirects(response, "/admin/login/?next=" + self.view_url) 19 | 20 | def test_regular_user_access_returns_forbidden(self): 21 | self.client.force_login(self.regular_user) 22 | response = self.client.get(self.view_url) 23 | self.assertEqual(response.status_code, 403) 24 | 25 | def test_staff_user_access_returns_forbidden(self): 26 | self.client.force_login(self.staff_user) 27 | response = self.client.get(self.view_url) 28 | self.assertEqual(response.status_code, 403) 29 | 30 | def test_superuser_access_returns_success(self): 31 | self.client.force_login(self.superuser) 32 | response = self.client.get(self.view_url) 33 | self.assertEqual(response.status_code, 200) 34 | -------------------------------------------------------------------------------- /celerybeat_status/tests/utils.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.test import Client, TestCase 3 | 4 | User = get_user_model() 5 | 6 | 7 | class BaseTestCase(TestCase): 8 | 9 | def setUp(self): 10 | super().setUp() 11 | self.regular_user = User.objects.create_user( 12 | username="regular", 13 | email="regular@email.com", 14 | password="123", 15 | is_staff=False, 16 | is_superuser=False, 17 | ) 18 | self.staff_user = User.objects.create_user( 19 | username="staff", 20 | email="staff@email.com", 21 | password="123", 22 | is_staff=True, 23 | is_superuser=False, 24 | ) 25 | self.superuser = User.objects.create_user( 26 | username="super", 27 | email="superuser@gmail.com", 28 | password="123", 29 | is_staff=True, 30 | is_superuser=True, 31 | ) 32 | 33 | 34 | class TestRequiresAuthenticatedUser(object): 35 | 36 | def test_requires_authenticated_user(self): 37 | response = self.client.get(self.view_url) 38 | self.assertEqual(response.status_code, 302) 39 | -------------------------------------------------------------------------------- /celerybeat_status/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from celerybeat_status.views import PeriodicTasksStatusListView 4 | 5 | app_name = "celerybeat_status" 6 | 7 | urlpatterns = [ 8 | path( 9 | "periodic-tasks/", 10 | PeriodicTasksStatusListView.as_view(), 11 | name="periodic-tasks-status", 12 | ), 13 | ] 14 | -------------------------------------------------------------------------------- /celerybeat_status/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.mixins import UserPassesTestMixin 2 | from django.utils.translation import gettext_lazy 3 | from django.views.generic.base import TemplateView 4 | 5 | from celerybeat_status.helpers import get_periodic_tasks_info 6 | 7 | 8 | class PeriodicTasksStatusListView(UserPassesTestMixin, TemplateView): 9 | template_name = "celerybeat_status/periodic_tasks_status_list.html" 10 | site_url = "/" 11 | login_url = "admin:login" 12 | 13 | def test_func(self): 14 | return self.request.user.is_staff and self.request.user.is_superuser 15 | 16 | def get_context_data(self, **kwargs): 17 | context = super().get_context_data(**kwargs) 18 | context["title"] = gettext_lazy("Periodic tasks status") 19 | context["user"] = self.request.user 20 | context["site_url"] = self.site_url 21 | context["has_permission"] = self.request.user.is_superuser 22 | context["tasks"] = get_periodic_tasks_info() 23 | 24 | return context 25 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /requirements_test.txt: -------------------------------------------------------------------------------- 1 | celery==5.4.0 2 | coverage==7.5.1 3 | mock>=5.1.0 4 | flake8>=7.0.0 5 | tox>=4.15.0 6 | tox-gh>=1.3.1 7 | django-model-utils>=4.5.1 8 | djangorestframework 9 | model_mommy 10 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 3 | from __future__ import absolute_import, unicode_literals 4 | 5 | import os 6 | import sys 7 | 8 | import django 9 | from django.conf import settings 10 | from django.test.utils import get_runner 11 | 12 | 13 | def run_tests(*args, **kwargs): 14 | os.environ["DJANGO_SETTINGS_MODULE"] = "tests.settings" 15 | django.setup() 16 | TestRunner = get_runner(settings) 17 | test_runner = TestRunner(**kwargs) 18 | failures = test_runner.run_tests(args) 19 | try: 20 | os.remove("./celerybeat-schedule.bak") 21 | except Exception: 22 | pass 23 | 24 | try: 25 | os.remove("./celerybeat-schedule.dat") 26 | except Exception: 27 | pass 28 | 29 | try: 30 | os.remove("./celerybeat-schedule.dir") 31 | except Exception: 32 | pass 33 | 34 | sys.exit(bool(failures)) 35 | 36 | 37 | def process_kwargs(kwarg): 38 | if len(kwarg.split("=")) == 2: 39 | return (kwarg.split("=")[0], kwarg.split("=")[1]) 40 | 41 | else: 42 | return (kwarg, True) 43 | 44 | 45 | if __name__ == "__main__": 46 | 47 | args = [a for a in sys.argv[1:] if not a.startswith("--")] 48 | kwargs = dict(process_kwargs(a[2:]) for a in sys.argv[1:] if a.startswith("--")) 49 | 50 | run_tests(*args, **kwargs) 51 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | 4 | [flake8] 5 | ignore = E501 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import os 4 | import re 5 | import shutil 6 | import sys 7 | from io import open 8 | 9 | from setuptools import setup 10 | 11 | long_description = "" 12 | try: 13 | with open("README.md") as f: 14 | long_description = f.read() 15 | except FileNotFoundError: 16 | print("warning: README.md not found, could not set long_description") 17 | 18 | 19 | def get_version(package): 20 | """ 21 | Return package version as listed in `__version__` in `init.py`. 22 | """ 23 | init_py = open(os.path.join(package, "__init__.py")).read() 24 | return re.search("__version__ = ['\"]([^'\"]+)['\"]", init_py).group(1) 25 | 26 | 27 | def get_packages(package): 28 | """ 29 | Return root package and all sub-packages. 30 | """ 31 | return [ 32 | dirpath 33 | for dirpath, dirnames, filenames in os.walk(package) 34 | if os.path.exists(os.path.join(dirpath, "__init__.py")) 35 | ] 36 | 37 | 38 | def get_package_data(package): 39 | """ 40 | Return all files under the root package, that are not in a 41 | package themselves. 42 | """ 43 | walk = [ 44 | (dirpath.replace(package + os.sep, "", 1), filenames) 45 | for dirpath, dirnames, filenames in os.walk(package) 46 | if not os.path.exists(os.path.join(dirpath, "__init__.py")) 47 | ] 48 | 49 | filepaths = [] 50 | for base, filenames in walk: 51 | filepaths.extend([os.path.join(base, filename) for filename in filenames]) 52 | return {package: filepaths} 53 | 54 | 55 | version = get_version("celerybeat_status") 56 | 57 | 58 | if sys.argv[-1] == "publish": 59 | if os.system("pip freeze | grep twine"): 60 | print("twine not installed.\nUse `pip install twine`.\nExiting.") 61 | sys.exit() 62 | os.system("python setup.py sdist bdist_wheel") 63 | os.system("twine upload dist/*") 64 | print("You probably want to also tag the version now:") 65 | print(" git tag -a %s -m 'version %s'" % (version, version)) 66 | print(" git push --tags") 67 | shutil.rmtree("dist") 68 | shutil.rmtree("build") 69 | shutil.rmtree("django_celerybeat_status.egg-info") 70 | sys.exit() 71 | 72 | 73 | setup( 74 | name="django-celerybeat-status", 75 | version=version, 76 | license="MIT", 77 | description="A simple django admin extension that shows when your periodic are going to run next", 78 | long_description=long_description, 79 | long_description_content_type="text/markdown", 80 | author="Vinta Software", 81 | author_email="contact@vinta.com.br", 82 | packages=get_packages("celerybeat_status"), 83 | package_data=get_package_data("celerybeat_status"), 84 | install_requires=[], 85 | zip_safe=False, 86 | classifiers=[ 87 | "Development Status :: 5 - Production/Stable", 88 | "Intended Audience :: Developers", 89 | "Framework :: Django", 90 | "Framework :: Django :: 4.2", 91 | "Framework :: Django :: 5.0", 92 | "Programming Language :: Python :: 3.8", 93 | "Programming Language :: Python :: 3.9", 94 | "Programming Language :: Python :: 3.10", 95 | "Programming Language :: Python :: 3.11", 96 | "Programming Language :: Python :: 3.12", 97 | ], 98 | ) 99 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vintasoftware/django-celerybeat-status/c75c219ea85656030c266d665a36c4e1ff2251b3/tests/__init__.py -------------------------------------------------------------------------------- /tests/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for tests project. 3 | 4 | Generated by 'django-admin startproject' using Django 5.0.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/5.0/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = "django-insecure-2#mnq%u++zzqhlftquaky2y=)ida=5759jg=ck&n7@ez+sqs#1" 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ["*"] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | "django.contrib.admin", 35 | "django.contrib.auth", 36 | "django.contrib.contenttypes", 37 | "django.contrib.sessions", 38 | "django.contrib.messages", 39 | "django.contrib.staticfiles", 40 | "celerybeat_status", 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | "django.middleware.security.SecurityMiddleware", 45 | "django.contrib.sessions.middleware.SessionMiddleware", 46 | "django.middleware.common.CommonMiddleware", 47 | "django.middleware.csrf.CsrfViewMiddleware", 48 | "django.contrib.auth.middleware.AuthenticationMiddleware", 49 | "django.contrib.messages.middleware.MessageMiddleware", 50 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 51 | ] 52 | 53 | ROOT_URLCONF = "tests.urls" 54 | 55 | TEMPLATES = [ 56 | { 57 | "BACKEND": "django.template.backends.django.DjangoTemplates", 58 | # "DIRS": [os.path.join(BASE_DIR, "templates")], 59 | "DIRS": [], 60 | "APP_DIRS": True, 61 | "OPTIONS": { 62 | "context_processors": [ 63 | "django.template.context_processors.debug", 64 | "django.template.context_processors.request", 65 | "django.contrib.auth.context_processors.auth", 66 | "django.contrib.messages.context_processors.messages", 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | 73 | # Database 74 | # https://docs.djangoproject.com/en/5.0/ref/settings/#databases 75 | 76 | DATABASES = { 77 | "default": { 78 | "ENGINE": "django.db.backends.sqlite3", 79 | "NAME": BASE_DIR / "db.sqlite3", 80 | } 81 | } 82 | 83 | 84 | # Internationalization 85 | # https://docs.djangoproject.com/en/5.0/topics/i18n/ 86 | 87 | LANGUAGE_CODE = "en-us" 88 | 89 | TIME_ZONE = "UTC" 90 | 91 | USE_I18N = True 92 | 93 | USE_TZ = True 94 | 95 | 96 | # Static files (CSS, JavaScript, Images) 97 | # https://docs.djangoproject.com/en/5.0/howto/static-files/ 98 | 99 | STATIC_URL = "static/" 100 | -------------------------------------------------------------------------------- /tests/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import include, path 3 | 4 | urlpatterns = [ 5 | # The new admin catch-all view will break URL patterns routed after the admin URLs and matching 6 | # the admin URL prefix. Source: https://docs.djangoproject.com/en/5.0/releases/3.2/#id1 7 | path("admin/statuscheck/", include("celerybeat_status.urls")), 8 | path("admin/", admin.site.urls), 9 | ] 10 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | requires = 3 | tox>=4 4 | envlist = 5 | # Django official Python support 6 | # Source: https://docs.djangoproject.com/en/5.0/faq/install/#what-python-version-can-i-use-with-django 7 | {py38,py39,py310,py311,py312}-django42 8 | {py310,py311,py312}-django50 9 | coverage 10 | 11 | [gh] 12 | python = 13 | 3.12 = py312-django42, py312-django50, coverage 14 | 3.11 = py311-django42, py311-django50 15 | 3.10 = py310-django42, py310-django50 16 | 3.9 = py39-django42 17 | 3.8 = py38-django42 18 | 19 | [testenv] 20 | description = run tests 21 | setenv = 22 | PYTHONPATH = {toxinidir}:{toxinidir}/celerybeat_status:{toxinidir} 23 | commands = coverage run --source celerybeat_status runtests.py {posargs} 24 | deps = 25 | django42: Django>=4.2,<5 26 | django50: Django>=5.0,<5.1 27 | -r{toxinidir}/requirements_test.txt 28 | basepython = 29 | py312: python3.12 30 | py311: python3.11 31 | py310: python3.10 32 | py39: python3.9 33 | py38: python3.8 34 | 35 | [testenv:coverage] 36 | description = run coveralls 37 | passenv = COVERALLS_REPO_TOKEN 38 | allowlist_externals = coverage 39 | basepython = python3.12 40 | deps = 41 | {[testenv]deps} 42 | coveralls 43 | commands = 44 | coverage run --source celerybeat_status runtests.py {posargs} 45 | coveralls 46 | --------------------------------------------------------------------------------