scripts.py 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. from django import forms
  2. from django.core.files.storage import storages
  3. from django.utils.translation import gettext_lazy as _
  4. from core.choices import JobIntervalChoices
  5. from core.forms import ManagedFileForm
  6. from extras.utils import validate_script_content
  7. from utilities.datetime import local_now
  8. from utilities.forms.widgets import DateTimePicker, NumberWithOptions
  9. __all__ = (
  10. 'ScriptFileForm',
  11. 'ScriptForm',
  12. )
  13. class ScriptForm(forms.Form):
  14. _commit = forms.BooleanField(
  15. required=False,
  16. initial=True,
  17. label=_("Commit changes"),
  18. help_text=_("Commit changes to the database (uncheck for a dry-run)")
  19. )
  20. _schedule_at = forms.DateTimeField(
  21. required=False,
  22. widget=DateTimePicker(),
  23. label=_("Schedule at"),
  24. help_text=_("Schedule execution of script to a set time"),
  25. )
  26. _interval = forms.IntegerField(
  27. required=False,
  28. min_value=1,
  29. label=_("Recurs every"),
  30. widget=NumberWithOptions(
  31. options=JobIntervalChoices
  32. ),
  33. help_text=_("Interval at which this script is re-run (in minutes)")
  34. )
  35. def __init__(self, *args, scheduling_enabled=True, **kwargs):
  36. super().__init__(*args, **kwargs)
  37. # Annotate the current system time for reference
  38. now = local_now().strftime('%Y-%m-%d %H:%M:%S %Z')
  39. self.fields['_schedule_at'].help_text += _(' (current time: <strong>{now}</strong>)').format(now=now)
  40. # Remove scheduling fields if scheduling is disabled
  41. if not scheduling_enabled:
  42. self.fields.pop('_schedule_at')
  43. self.fields.pop('_interval')
  44. def clean(self):
  45. scheduled_time = self.cleaned_data.get('_schedule_at')
  46. if scheduled_time and scheduled_time < local_now():
  47. raise forms.ValidationError(_('Scheduled time must be in the future.'))
  48. # When interval is used without schedule at, schedule for the current time
  49. if self.cleaned_data.get('_interval') and not scheduled_time:
  50. self.cleaned_data['_schedule_at'] = local_now()
  51. return self.cleaned_data
  52. class ScriptFileForm(ManagedFileForm):
  53. """
  54. ManagedFileForm with a custom save method to use django-storages.
  55. """
  56. def clean(self):
  57. super().clean()
  58. if upload_file := self.cleaned_data.get('upload_file'):
  59. # Validate that the uploaded script can be loaded as a Python module
  60. content = upload_file.read()
  61. upload_file.seek(0)
  62. try:
  63. validate_script_content(content, upload_file.name)
  64. except Exception as e:
  65. raise forms.ValidationError(
  66. _("Error loading script: {error}").format(error=e)
  67. )
  68. return self.cleaned_data
  69. def save(self, *args, **kwargs):
  70. # If a file was uploaded, save it to disk
  71. if self.cleaned_data['upload_file']:
  72. storage = storages.create_storage(storages.backends["scripts"])
  73. filename = self.cleaned_data['upload_file'].name
  74. self.instance.file_path = filename
  75. data = self.cleaned_data['upload_file']
  76. storage.save(filename, data)
  77. # need to skip ManagedFileForm save method
  78. return super(ManagedFileForm, self).save(*args, **kwargs)