reformat everything

This commit is contained in:
Jakub Kropáček 2025-03-03 21:52:20 +01:00
parent 98b1705d81
commit 1bd01f9652
25 changed files with 276 additions and 121 deletions

View file

@ -27,14 +27,18 @@ class Migration(migrations.Migration):
),
),
(
'password', models.CharField(
max_length=128, verbose_name='password',
'password',
models.CharField(
max_length=128,
verbose_name='password',
),
),
(
'last_login',
models.DateTimeField(
blank=True, null=True, verbose_name='last login',
blank=True,
null=True,
verbose_name='last login',
),
),
(
@ -63,19 +67,25 @@ class Migration(migrations.Migration):
(
'first_name',
models.CharField(
blank=True, max_length=150, verbose_name='first name',
blank=True,
max_length=150,
verbose_name='first name',
),
),
(
'last_name',
models.CharField(
blank=True, max_length=150, verbose_name='last name',
blank=True,
max_length=150,
verbose_name='last name',
),
),
(
'email',
models.EmailField(
blank=True, max_length=254, verbose_name='email address',
blank=True,
max_length=254,
verbose_name='email address',
),
),
(
@ -97,7 +107,8 @@ class Migration(migrations.Migration):
(
'date_joined',
models.DateTimeField(
default=django.utils.timezone.now, verbose_name='date joined',
default=django.utils.timezone.now,
verbose_name='date joined',
),
),
(

View file

@ -13,7 +13,8 @@ class Migration(migrations.Migration):
model_name='user',
name='email',
field=models.EmailField(
max_length=254, verbose_name='email address',
max_length=254,
verbose_name='email address',
),
),
migrations.AlterField(

View file

@ -18,14 +18,18 @@ class Migration(migrations.Migration):
model_name='user',
name='customers',
field=models.ManyToManyField(
blank=True, related_name='+', to='subjects.subject',
blank=True,
related_name='+',
to='subjects.subject',
),
),
migrations.AddField(
model_name='user',
name='suppliers',
field=models.ManyToManyField(
blank=True, related_name='+', to='subjects.subject',
blank=True,
related_name='+',
to='subjects.subject',
),
),
]

View file

@ -30,7 +30,9 @@ class Migration(migrations.Migration):
model_name='user',
name='customers',
field=models.ManyToManyField(
blank=True, to='subjects.subject', verbose_name='customers',
blank=True,
to='subjects.subject',
verbose_name='customers',
),
),
]

View file

@ -13,15 +13,21 @@ class User(AbstractUser):
email = models.EmailField(_('email address'))
supplier = models.ForeignKey(
Subject, models.PROTECT, _('supplier'), blank=True, null=True,
Subject,
models.PROTECT,
_('supplier'),
blank=True,
null=True,
)
customers = models.ManyToManyField(
Subject, blank=True, verbose_name=_('customers'),
Subject,
blank=True,
verbose_name=_('customers'),
)
class Meta:
verbose_name = _("user")
verbose_name_plural = _("users")
verbose_name = _('user')
verbose_name_plural = _('users')
def _get_m2m_ids(self, field: str):
return list(getattr(self, field).values_list('id', flat=True))

View file

@ -22,8 +22,8 @@ def auth_login(req: HttpRequest) -> HttpResponse:
login(req, user)
user.email_user(
subject="You have just logged in!",
message="You have just logged in",
subject='You have just logged in!',
message='You have just logged in',
)
redirect_url = getattr(req.GET, 'next', settings.LOGIN_REDIRECT_URL)

View file

@ -24,7 +24,8 @@ BASE_DIR = Path(__file__).resolve().parent.parent.parent
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
SECRET_KEY = env(
'SECRET_KEY', default='django-insecure-+l+g3)q1zz2bz7=mz4ys6lhu5uj+=ucj34flm^clo4vb3(wmdp',
'SECRET_KEY',
default='django-insecure-+l+g3)q1zz2bz7=mz4ys6lhu5uj+=ucj34flm^clo4vb3(wmdp',
)
DEBUG = env.bool('DEBUG', default=True)
@ -156,32 +157,32 @@ CRISPY_TEMPLATE_PACK = 'bootstrap5'
# Template to PDF rendering
GOTENBERG_API_URL = env.str('GOTENBERG_API_URL', 'http://gotenberg:3000')
INTERNAL_API_URL = env.str('INTERNAL_API_URL', "http://backend:8000")
INTERNAL_API_URL = env.str('INTERNAL_API_URL', 'http://backend:8000')
# Dramatiq
DRAMATIQ_BROKER = {
"BROKER": "dramatiq.brokers.redis.RedisBroker",
"OPTIONS": {
"url": "redis://redis:6379",
'BROKER': 'dramatiq.brokers.redis.RedisBroker',
'OPTIONS': {
'url': 'redis://redis:6379',
},
"MIDDLEWARE": [
"dramatiq.middleware.AgeLimit",
"dramatiq.middleware.TimeLimit",
"dramatiq.middleware.Callbacks",
"dramatiq.middleware.Retries",
"django_dramatiq.middleware.DbConnectionsMiddleware",
"django_dramatiq.middleware.AdminMiddleware",
'MIDDLEWARE': [
'dramatiq.middleware.AgeLimit',
'dramatiq.middleware.TimeLimit',
'dramatiq.middleware.Callbacks',
'dramatiq.middleware.Retries',
'django_dramatiq.middleware.DbConnectionsMiddleware',
'django_dramatiq.middleware.AdminMiddleware',
],
}
# Email config
EMAIL_BACKEND = 'post_office.EmailBackend'
EMAIL_HOST = env.str("EMAIL_HOST", "smtp.seznam.cz")
EMAIL_PORT = env.int("EMAIL_PORT", 465)
EMAIL_HOST_USER = env.str("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = env.str("EMAIL_HOST_PASSWORD")
EMAIL_USE_SSL = env.bool("EMAIL_USE_SSL", True)
EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", False)
EMAIL_HOST = env.str('EMAIL_HOST', 'smtp.seznam.cz')
EMAIL_PORT = env.int('EMAIL_PORT', 465)
EMAIL_HOST_USER = env.str('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = env.str('EMAIL_HOST_PASSWORD')
EMAIL_USE_SSL = env.bool('EMAIL_USE_SSL', True)
EMAIL_USE_TLS = env.bool('EMAIL_USE_TLS', False)
DEFAULT_FROM_EMAIL = env.str("EMAIL_DEFAULT_FROM", "Facturio <no-reply@kropcloud.net>")
DEFAULT_FROM_EMAIL = env.str('EMAIL_DEFAULT_FROM', 'Facturio <no-reply@kropcloud.net>')

View file

@ -2,6 +2,7 @@ from django.contrib import admin
from . import models
class InvoiceFileInline(admin.TabularInline):
model = models.InvoiceFile
extra = 0
@ -11,6 +12,7 @@ class InvoiceFileInline(admin.TabularInline):
]
can_delete = False
class InvoiceItemInline(admin.TabularInline):
model = models.InvoiceItem
extra = 0

View file

@ -10,7 +10,7 @@ from subjects import models as subject_models
class InvoiceItemForm(forms.ModelForm):
class Meta:
model = models.InvoiceItem
fields = ["amount", "amount_unit", "description", "price_for_amount"]
fields = ['amount', 'amount_unit', 'description', 'price_for_amount']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -19,9 +19,14 @@ class InvoiceItemForm(forms.ModelForm):
InvoiceItemFormSet = forms.inlineformset_factory(
models.Invoice, models.InvoiceItem, form=InvoiceItemForm,
fields=["description", "amount", "amount_unit", "price_for_amount"],
extra=3, can_delete=False, validate_min=True, min_num=1,
models.Invoice,
models.InvoiceItem,
form=InvoiceItemForm,
fields=['description', 'amount', 'amount_unit', 'price_for_amount'],
extra=3,
can_delete=False,
validate_min=True,
min_num=1,
)
@ -39,12 +44,12 @@ class CreateInvoiceForm(forms.ModelForm):
super().__init__(*args, **kwargs)
if self._user.supplier:
self.fields["supplier"].queryset = subject_models.Subject.objects.filter(
self.fields['supplier'].queryset = subject_models.Subject.objects.filter(
id=self._user.supplier.id,
)
else:
self.fields["supplier"].queryset = subject_models.Subject.objects.none()
self.fields["customer"].queryset = self._user.customers
self.fields['supplier'].queryset = subject_models.Subject.objects.none()
self.fields['customer'].queryset = self._user.customers
self.helper = helper.FormHelper()
self.helper.form_method = 'post'

View file

@ -88,25 +88,31 @@ class Migration(migrations.Migration):
(
'amount',
models.DecimalField(
decimal_places=3, max_digits=8, verbose_name='Amount',
decimal_places=3,
max_digits=8,
verbose_name='Amount',
),
),
(
'amount_unit',
models.CharField(
max_length=32, verbose_name='Amount unit',
max_length=32,
verbose_name='Amount unit',
),
),
(
'description',
models.CharField(
max_length=64, verbose_name='Description',
max_length=64,
verbose_name='Description',
),
),
(
'price_for_amount',
models.DecimalField(
decimal_places=3, max_digits=8, verbose_name='Price for amount',
decimal_places=3,
max_digits=8,
verbose_name='Price for amount',
),
),
(

View file

@ -5,17 +5,17 @@ from django.db import models
class Migration(migrations.Migration):
dependencies = [
("invoices", "0001_initial"),
('invoices', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name="invoice",
name="invoice_date",
model_name='invoice',
name='invoice_date',
field=models.DateField(
default=django.utils.timezone.now, verbose_name="Invoice date",
default=django.utils.timezone.now,
verbose_name='Invoice date',
),
),
]

View file

@ -5,7 +5,6 @@ from django.db import models
class Migration(migrations.Migration):
dependencies = [
('invoices', '0002_alter_invoice_invoice_date'),
('subjects', '0005_alter_subjectdata_subject'),
@ -15,21 +14,41 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='invoice',
name='customer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='subjects.subject', verbose_name='Customer'),
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='+',
to='subjects.subject',
verbose_name='Customer',
),
),
migrations.AlterField(
model_name='invoice',
name='customer_data',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='subjects.subjectdata', verbose_name='Customer data'),
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='+',
to='subjects.subjectdata',
verbose_name='Customer data',
),
),
migrations.AlterField(
model_name='invoice',
name='supplier',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='subjects.subject', verbose_name='Supplier'),
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='+',
to='subjects.subject',
verbose_name='Supplier',
),
),
migrations.AlterField(
model_name='invoice',
name='supplier_data',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='subjects.subjectdata', verbose_name='Supplier data'),
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='+',
to='subjects.subjectdata',
verbose_name='Supplier data',
),
),
]

View file

@ -5,19 +5,47 @@ from django.db import models
class Migration(migrations.Migration):
dependencies = [
('invoices', '0003_alter_invoice_customer_alter_invoice_customer_data_and_more'),
(
'invoices',
'0003_alter_invoice_customer_alter_invoice_customer_data_and_more',
),
]
operations = [
migrations.CreateModel(
name='InvoiceFile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('file', models.FileField(blank=True, null=True, upload_to='invoice-files', verbose_name='File')),
('created_date', models.DateTimeField(auto_now=True, verbose_name='Created Date')),
('invoice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='invoices.invoice')),
(
'id',
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID',
),
),
(
'file',
models.FileField(
blank=True,
null=True,
upload_to='invoice-files',
verbose_name='File',
),
),
(
'created_date',
models.DateTimeField(auto_now=True, verbose_name='Created Date'),
),
(
'invoice',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='files',
to='invoices.invoice',
),
),
],
options={
'verbose_name': 'Invoice File',

View file

@ -4,7 +4,6 @@ from django.db import models
class Migration(migrations.Migration):
dependencies = [
('invoices', '0004_invoicefile'),
]
@ -13,6 +12,11 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='invoicefile',
name='file',
field=models.FileField(blank=True, null=True, upload_to='invoice-files/%Y/%m/%d/', verbose_name='File'),
field=models.FileField(
blank=True,
null=True,
upload_to='invoice-files/%Y/%m/%d/',
verbose_name='File',
),
),
]

View file

@ -17,17 +17,29 @@ class Invoice(models.Model):
user = models.ForeignKey(UserModel, models.CASCADE)
supplier = models.ForeignKey(
subject_models.Subject, on_delete=models.CASCADE, related_name='+', verbose_name=_('Supplier'),
subject_models.Subject,
on_delete=models.CASCADE,
related_name='+',
verbose_name=_('Supplier'),
)
supplier_data = models.ForeignKey(
subject_models.SubjectData, on_delete=models.CASCADE, related_name='+', verbose_name=_('Supplier data'),
subject_models.SubjectData,
on_delete=models.CASCADE,
related_name='+',
verbose_name=_('Supplier data'),
)
customer = models.ForeignKey(
subject_models.Subject, on_delete=models.CASCADE, related_name='+', verbose_name=_('Customer'),
subject_models.Subject,
on_delete=models.CASCADE,
related_name='+',
verbose_name=_('Customer'),
)
customer_data = models.ForeignKey(
subject_models.SubjectData, on_delete=models.CASCADE, related_name='+', verbose_name=_('Customer data'),
subject_models.SubjectData,
on_delete=models.CASCADE,
related_name='+',
verbose_name=_('Customer data'),
)
invoice_date = models.DateField(_('Invoice date'), default=timezone.now)
due_date = models.DateField(_('Due date'))
@ -42,7 +54,7 @@ class Invoice(models.Model):
return total
def custom_id(self) -> str:
return f"{self.invoice_date.year}-{self.id:04}"
return f'{self.invoice_date.year}-{self.id:04}'
class InvoiceItem(models.Model):
@ -51,13 +63,17 @@ class InvoiceItem(models.Model):
verbose_name_plural = _('Invoice Items')
invoice = models.ForeignKey(
Invoice, on_delete=models.CASCADE, related_name='items',
Invoice,
on_delete=models.CASCADE,
related_name='items',
)
amount = models.DecimalField(_('Amount'), decimal_places=3, max_digits=8)
amount_unit = models.CharField(_('Amount unit'), max_length=32)
description = models.CharField(_('Description'), max_length=64)
price_for_amount = models.DecimalField(
_('Price for amount'), decimal_places=3, max_digits=8,
_('Price for amount'),
decimal_places=3,
max_digits=8,
)
def total_price(self) -> decimal.Decimal:
@ -73,11 +89,13 @@ class InvoiceFile(models.Model):
verbose_name_plural = _('Invoice Files')
invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE, related_name='files')
file = models.FileField(_('File'), upload_to='invoice-files/%Y/%m/%d/', null=True, blank=True)
file = models.FileField(
_('File'), upload_to='invoice-files/%Y/%m/%d/', null=True, blank=True,
)
created_date = models.DateTimeField(_('Created Date'), auto_now=True)
def __str__(self):
return self.file.name
def get_file_name(self, ext: str) -> str:
return f"{self.invoice.id}-{self.created_date.strftime('%H%M%S')}.{ext}"
return f'{self.invoice.id}-{self.created_date.strftime("%H%M%S")}.{ext}'

View file

@ -10,11 +10,13 @@ from invoices.models import Invoice
@dramatiq.actor()
def generate_pdf(invoice_id: int):
invoice = Invoice.objects.get(pk=invoice_id)
print_url = f"{settings.INTERNAL_API_URL}{reverse('invoices:print_invoice', kwargs=dict(invoice_id=invoice.id))}"
print_url = f'{settings.INTERNAL_API_URL}{reverse("invoices:print_invoice", kwargs=dict(invoice_id=invoice.id))}'
invoice_file = invoice.files.create()
with GotenbergClient(settings.GOTENBERG_API_URL) as client:
with client.chromium.url_to_pdf() as task:
res = task.url(print_url).run()
invoice_file.file.save(invoice_file.get_file_name('pdf'), ContentFile(res.content))
invoice_file.file.save(
invoice_file.get_file_name('pdf'), ContentFile(res.content),
)

View file

@ -21,25 +21,36 @@ def home(req: HttpRequest) -> HttpResponse:
with transaction.atomic():
invoice = form.save()
formset = forms.InvoiceItemFormSet(
data=req.POST, instance=invoice,
data=req.POST,
instance=invoice,
)
if formset.is_valid():
formset.save()
tasks.generate_pdf.send(
invoice.id,
)
return redirect(reverse('invoices:invoice', kwargs=dict(invoice_id=invoice.id)))
return redirect(
reverse('invoices:invoice', kwargs=dict(invoice_id=invoice.id)),
)
else:
transaction.set_rollback(True)
else:
formset = forms.InvoiceItemFormSet(data=req.POST)
return render(req, 'invoices/index.html', dict(form=form, formset=formset, invoices=user_invoices))
return render(
req,
'invoices/index.html',
dict(form=form, formset=formset, invoices=user_invoices),
)
elif req.method == 'GET':
form = forms.CreateInvoiceForm(current_user=req.user)
formset = forms.InvoiceItemFormSet()
return render(req, 'invoices/index.html', dict(form=form, formset=formset, invoices=user_invoices))
return render(
req,
'invoices/index.html',
dict(form=form, formset=formset, invoices=user_invoices),
)
return HttpResponse(status=405)

View file

@ -56,7 +56,7 @@ class SelectSubjectForm(forms.ModelForm):
self._user = kwargs.pop('current_user', None)
super().__init__(*args, **kwargs)
self.fields["supplier"].queryset = models.Subject.objects.exclude(
self.fields['supplier'].queryset = models.Subject.objects.exclude(
id__in=self._user.get_customers(),
)

View file

@ -25,7 +25,10 @@ class Migration(migrations.Migration):
(
'vat_id',
models.CharField(
blank=True, max_length=12, null=True, verbose_name='vat_id',
blank=True,
max_length=12,
null=True,
verbose_name='vat_id',
),
),
('street', models.CharField(max_length=64, verbose_name='street')),
@ -34,7 +37,10 @@ class Migration(migrations.Migration):
(
'city_part',
models.CharField(
blank=True, max_length=64, null=True, verbose_name='city_part',
blank=True,
max_length=64,
null=True,
verbose_name='city_part',
),
),
],

View file

@ -18,14 +18,20 @@ class Migration(migrations.Migration):
model_name='subject',
name='city_part',
field=models.CharField(
blank=True, max_length=64, null=True, verbose_name='City part',
blank=True,
max_length=64,
null=True,
verbose_name='City part',
),
),
migrations.AlterField(
model_name='subject',
name='id',
field=models.CharField(
max_length=8, primary_key=True, serialize=False, verbose_name='CIN',
max_length=8,
primary_key=True,
serialize=False,
verbose_name='CIN',
),
),
migrations.AlterField(
@ -42,7 +48,10 @@ class Migration(migrations.Migration):
model_name='subject',
name='vat_id',
field=models.CharField(
blank=True, max_length=12, null=True, verbose_name='VAT ID',
blank=True,
max_length=12,
null=True,
verbose_name='VAT ID',
),
),
migrations.AlterField(

View file

@ -49,13 +49,17 @@ class Migration(migrations.Migration):
(
'city_part',
models.CharField(
blank=True, max_length=64, null=True, verbose_name='City part',
blank=True,
max_length=64,
null=True,
verbose_name='City part',
),
),
(
'created_date',
models.DateTimeField(
auto_now_add=True, verbose_name='Created date',
auto_now_add=True,
verbose_name='Created date',
),
),
(

View file

@ -5,19 +5,18 @@ from django.db import models
class Migration(migrations.Migration):
dependencies = [
("subjects", "0004_remove_subject_city_remove_subject_city_part_and_more"),
('subjects', '0004_remove_subject_city_remove_subject_city_part_and_more'),
]
operations = [
migrations.AlterField(
model_name="subjectdata",
name="subject",
model_name='subjectdata',
name='subject',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="subject_data",
to="subjects.subject",
related_name='subject_data',
to='subjects.subject',
),
),
]

View file

@ -9,13 +9,20 @@ class Subject(models.Model):
id = models.CharField(_('CIN'), max_length=8, primary_key=True)
vat_id = models.CharField(
_('VAT ID'), max_length=12, null=True, blank=True,
_('VAT ID'),
max_length=12,
null=True,
blank=True,
)
def get_latest_data(self):
return self.subject_data.filter(subject=self).order_by(
'-created_date',
).first()
return (
self.subject_data.filter(subject=self)
.order_by(
'-created_date',
)
.first()
)
def __str__(self):
return f'{self.id} - {self.get_latest_data().name}'
@ -27,14 +34,19 @@ class SubjectData(models.Model):
verbose_name_plural = _('Subject Datas')
subject = models.ForeignKey(
Subject, on_delete=models.CASCADE, related_name='subject_data',
Subject,
on_delete=models.CASCADE,
related_name='subject_data',
)
name = models.CharField(_('Name'), max_length=128)
street = models.CharField(_('Street'), max_length=64)
zip_code = models.CharField(_('Zip Code'), max_length=6)
city = models.CharField(_('City'), max_length=64)
city_part = models.CharField(
_('City part'), max_length=64, null=True, blank=True,
_('City part'),
max_length=64,
null=True,
blank=True,
)
created_date = models.DateTimeField(_('Created date'), auto_now_add=True)

View file

@ -18,7 +18,9 @@ def build_address(street: str, zip_code: int | str, city: str, city_part: str) -
def main_page(req: HttpRequest) -> HttpResponse:
if req.method == 'POST':
select_subject_form = forms.SelectSubjectForm(
data=req.POST, instance=req.user, current_user=req.user,
data=req.POST,
instance=req.user,
current_user=req.user,
)
if select_subject_form.is_valid():
select_subject_form.save()
@ -29,7 +31,8 @@ def main_page(req: HttpRequest) -> HttpResponse:
id=req.user.supplier.id if req.user.supplier else None,
)
select_subject_form = forms.SelectSubjectForm(
instance=req.user, current_user=req.user,
instance=req.user,
current_user=req.user,
)
return render(
req,
@ -54,7 +57,8 @@ def create_subject(req: HttpRequest) -> HttpResponse:
ares_legal_data = ares_data['legal']
subject = models.Subject.objects.create(
id=ares_legal_data['company_id'], vat_id=ares_legal_data['company_vat_id'],
id=ares_legal_data['company_id'],
vat_id=ares_legal_data['company_vat_id'],
)
models.SubjectData.objects.create(
subject=subject,

View file

@ -15,18 +15,17 @@ from django_dramatiq.models import Task
logger = logging.getLogger(__name__)
DEFAULT_JOB_KWARGS = {
"max_instances": 1,
"replace_existing": True,
'max_instances': 1,
'replace_existing': True,
}
DAY_SEC = 24*60*60
DAY_SEC = 24 * 60 * 60
PERIODIC_JOBS = [
{
"task": "utils.tasks:send_queued_mail_task.send",
"trigger": IntervalTrigger(seconds=30),
'task': 'utils.tasks:send_queued_mail_task.send',
'trigger': IntervalTrigger(seconds=30),
},
]
@ -48,21 +47,23 @@ def delete_old_job_executions(max_age: int = 7 * DAY_SEC) -> None:
class Command(BaseCommand):
help = "Runs APScheduler." # noqa: A003
help = 'Runs APScheduler.' # noqa: A003
scheduler: BlockingScheduler = None
def prepare_scheduler(self):
self.stdout.write(self.style.NOTICE("Preparing scheduler"))
self.stdout.write(self.style.NOTICE('Preparing scheduler'))
self.scheduler = BlockingScheduler(timezone=settings.TIME_ZONE)
self.scheduler.add_jobstore(DjangoJobStore(), "default")
self.scheduler.add_jobstore(DjangoJobStore(), 'default')
def add_jobs(self):
self.scheduler.add_job(
delete_old_job_executions,
trigger=CronTrigger(
day_of_week="mon", hour="00", minute="00",
day_of_week='mon',
hour='00',
minute='00',
), # Midnight on Monday, before the start of the next work week.
id="delete_old_job_executions",
id='delete_old_job_executions',
max_instances=1,
replace_existing=True,
)
@ -70,19 +71,19 @@ class Command(BaseCommand):
for job in PERIODIC_JOBS:
kwargs = DEFAULT_JOB_KWARGS | deepcopy(job)
task = kwargs.pop("task")
task = kwargs.pop('task')
if "id" not in kwargs:
kwargs["id"] = task
if 'id' not in kwargs:
kwargs['id'] = task
self.scheduler.add_job(task, **kwargs)
self.stdout.write(f"Added job: {task}")
self.stdout.write(f'Added job: {task}')
def handle_shutdown(self, *args, **kwargs): # noqa: ARG001
self.stdout.write(self.style.NOTICE("Stopping scheduler..."))
self.stdout.write(self.style.NOTICE('Stopping scheduler...'))
self.scheduler.shutdown()
self.stdout.write(self.style.NOTICE("Scheduler shut down successfully!"))
self.stdout.write(self.style.NOTICE('Scheduler shut down successfully!'))
def handle(self, *args, **options):
self.prepare_scheduler()
@ -91,7 +92,7 @@ class Command(BaseCommand):
signal.signal(signal.SIGTERM, self.handle_shutdown)
try:
self.stdout.write(self.style.NOTICE("Starting scheduler..."))
self.stdout.write(self.style.NOTICE('Starting scheduler...'))
self.scheduler.start()
except KeyboardInterrupt:
self.handle_shutdown()