Skip to content

Commit

Permalink
Merge pull request #345 from FJNR-inc/develop
Browse files Browse the repository at this point in the history
new release
  • Loading branch information
RignonNoel authored Jan 16, 2020
2 parents 08b3738 + 5f0ad0d commit 3dff5c1
Show file tree
Hide file tree
Showing 37 changed files with 1,110 additions and 1,002 deletions.
60 changes: 60 additions & 0 deletions blitz_api/cron_manager_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import json
import traceback
from django.utils import timezone

import requests
from django.core.mail import mail_admins
from django.urls import reverse

from blitz_api import settings
from cron_manager.models import Task


class CronManager:

def __init__(self):
self.url_to_call = settings.EXTERNAL_SCHEDULER['URL_TO_CALL']

def create_task(self, data):
Task.objects.create(**data)

def create_wait_queue_place_notification(self, wait_queue_place_id):
wait_queue_place_url = self.url_to_call + reverse(
'retreat:waitqueueplace-notify',
args=[wait_queue_place_id]
)

data = {
"execution_datetime": timezone.now(),
"execution_interval": 1000 * 60 * 60 * 24,
"url": wait_queue_place_url,
"description": "Retreat wait queue notification"
}

self.create_task(data)

def create_remind_user(self, retreat_id, reminder_date):
remind_users_url = self.url_to_call + reverse(
'retreat:retreat-detail',
args=[retreat_id]
) + "/remind_users"
data = {
"execution_datetime": reminder_date,
"url": remind_users_url,
"description": "Retreat 7-days reminder notification"
}

self.create_task(data)

def create_recap(self, retreat_id, throwback_date):
remind_users_url = self.url_to_call + reverse(
'retreat:retreat-detail',
args=[retreat_id]
) + "/recap"
data = {
"execution_datetime": throwback_date,
"url": remind_users_url,
"description": "Retreat post-event notification"
}

self.create_task(data)
2 changes: 0 additions & 2 deletions blitz_api/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,6 @@ class Meta:
timezone = factory.Faker('timezone')
details = factory.Faker('text', max_nb_chars=1000)
seats = factory.fuzzy.FuzzyInteger(0)
reserved_seats = 0
next_user_notified = 0
notification_interval = timedelta(hours=24)
activity_language = factory.fuzzy.FuzzyChoice(Retreat.ACTIVITY_LANGUAGE)
price = factory.fuzzy.FuzzyDecimal(0, 9999, 2)
Expand Down
2 changes: 2 additions & 0 deletions blitz_api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
'store',
'retirement',
'log_management',
'cron_manager',
'storages',
'anymail',
'simple_history',
Expand Down Expand Up @@ -376,4 +377,5 @@
'URL': config('EXTERNAL_SCHEDULER_URL', default='http://example.com'),
'USER': config('EXTERNAL_SCHEDULER_USER', default='user'),
'PASSWORD': config('EXTERNAL_SCHEDULER_PASSWORD', default='password'),
'URL_TO_CALL': config('URL_TO_CALL', default='http://example.com'),
}
2 changes: 2 additions & 0 deletions blitz_api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from workplace.urls import router as workplace_router
from store.urls import router as store_router
from retirement.urls import router as retirement_router
from cron_manager.urls import router as cron_manager_router

from . import views

Expand All @@ -44,6 +45,7 @@ def __init__(self, *args, **kwargs):
# it is included separately at the bottom of this file.
router.registry.extend(workplace_router.registry)
router.registry.extend(store_router.registry)
router.registry.extend(cron_manager_router.registry)
# router.registry.extend(retirement_router.registry)

router.register('users', views.UserViewSet)
Expand Down
Empty file added cron_manager/__init__.py
Empty file.
53 changes: 53 additions & 0 deletions cron_manager/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from django.contrib import admin

from . import models
from django.utils.translation import ugettext_lazy as _


class ExecutionInline(admin.StackedInline):
model = models.Execution
can_delete = True
show_change_link = True
verbose_name_plural = _('Executions')
fk_name = 'task'


class TaskAdmin(admin.ModelAdmin):
inlines = (ExecutionInline,)
list_display = (
'id',
'description',
'execution_datetime',
'execution_interval',
'active',
'created_at'
)
list_filter = (
'description',
'active'
)
search_fields = (
'description',
'id',
)


class ExecutionAdmin(admin.ModelAdmin):
list_display = (
'id',
'task',
'created_at',
'executed_at',
'success',
'http_code'
)
list_filter = (
'success',
'task',
'http_code'
)
autocomplete_fields = ('task',)


admin.site.register(models.Task, TaskAdmin)
admin.site.register(models.Execution, ExecutionAdmin)
5 changes: 5 additions & 0 deletions cron_manager/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class CronManagerConfig(AppConfig):
name = 'cron_manager'
12 changes: 12 additions & 0 deletions cron_manager/cron_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from cron_manager.models import Task


def execute_tasks():

task_actives = Task.objects.filter(
active=True
)

for task in task_actives:
if task.can_be_execute:
task.execute()
39 changes: 39 additions & 0 deletions cron_manager/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Generated by Django 2.2.7 on 2020-01-08 19:49

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Task',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('url', models.URLField(verbose_name='URL to execute')),
('description', models.CharField(max_length=150, verbose_name='Description')),
('execution_datetime', models.DateTimeField(verbose_name='Execution datetime')),
('execution_interval', models.BigIntegerField(blank=True, null=True, verbose_name='Execution intervals ms')),
('active', models.BooleanField(default=True, verbose_name='Active')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created_at')),
],
),
migrations.CreateModel(
name='Execution',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created at')),
('executed_at', models.DateTimeField(verbose_name='Executed at')),
('success', models.BooleanField(blank=True, default=False, null=True, verbose_name='Succeded ?')),
('http_code', models.CharField(blank=True, max_length=100, null=True, verbose_name='HTTP code')),
('http_response', models.TextField(blank=True, null=True, verbose_name='HTTP response')),
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='executions', to='cron_manager.Task', verbose_name='Task')),
],
),
]
Empty file.
142 changes: 142 additions & 0 deletions cron_manager/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import requests
from django.db import models
from django.utils import timezone

from django.utils.translation import ugettext_lazy as _


class Task(models.Model):
"""Model for tasks"""

url = models.URLField(
verbose_name=_("URL to execute"),
)

description = models.CharField(
max_length=150,
verbose_name=_("Description"),
)

execution_datetime = models.DateTimeField(
verbose_name=_("Execution datetime"),
)

execution_interval = models.BigIntegerField(
blank=True,
null=True,
verbose_name=_("Execution intervals ms"),
)

active = models.BooleanField(
default=True,
verbose_name=_("Active"),
)

created_at = models.DateTimeField(
auto_now_add=True,
verbose_name=_("Created_at"),
)

def __str__(self):
return self.description

@property
def last_execution(self):
return self.executions.order_by('-executed_at').first()

@property
def can_be_execute(self):

if self.active:
if self.execution_interval:
now_less_intervals = timezone.now() - timezone.timedelta(
milliseconds=self.execution_interval)
else:
now_less_intervals = timezone.now()

last_execution = self.last_execution

if last_execution:
return now_less_intervals > last_execution.executed_at
else:
return now_less_intervals > self.execution_datetime
else:
return False

def execute(self):

last_execution = self.last_execution
if last_execution:
if self.execution_interval:
executed_at = last_execution.executed_at - timezone.timedelta(
milliseconds=self.execution_interval)
else:
executed_at = last_execution.executed_at
else:
executed_at = self.execution_datetime

execution = Execution.objects.create(
task=self,
executed_at=executed_at
)

response = requests.get(self.url)

success = False
if 200 <= response.status_code < 300:
success = True
try:
content = response.json()
stop_cron_task = content.get('stop', False)
if stop_cron_task or not self.execution_interval:
self.active = False
self.save()
except Exception:
success = False
execution.success = success
execution.http_code = response.status_code
execution.http_response = response.text
execution.save()


class Execution(models.Model):
"""Model to log execution of tasks"""

task = models.ForeignKey(
'Task',
related_name='executions',
on_delete=models.CASCADE,
verbose_name="Task"
)

created_at = models.DateTimeField(
auto_now_add=True,
verbose_name=_("Created at"),
)

executed_at = models.DateTimeField(
verbose_name=_("Executed at"),
)

success = models.BooleanField(
default=False,
blank=True,
null=True,
verbose_name=_("Succeded ?"),
)

http_code = models.CharField(
max_length=100,
blank=True,
null=True,
verbose_name=_("HTTP code"),
)

http_response = models.TextField(
verbose_name=_("HTTP response"),
blank=True,
null=True,
)

def __str__(self):
return f'{self.task} - {self.success}'
13 changes: 13 additions & 0 deletions cron_manager/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from rest_framework import serializers

from .models import (
Task
)


class TaskSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField()

class Meta:
model = Task
fields = '__all__'
24 changes: 24 additions & 0 deletions cron_manager/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

from rest_framework.routers import SimpleRouter
from django.urls import path
from django.conf.urls import include

from . import views


class OptionalSlashSimpleRouter(SimpleRouter):
""" Subclass of SimpleRouter to make the trailing slash optional """

def __init__(self, *args, **kwargs):
super(SimpleRouter, self).__init__(*args, **kwargs)
self.trailing_slash = '/?'


app_name = "cron_manager"

router = OptionalSlashSimpleRouter()
router.register('tasks', views.TaskViewSet)

urlpatterns = [
path('', include(router.urls)), # includes router generated URL
]
Loading

0 comments on commit 3dff5c1

Please sign in to comment.