Commit 1d2e7d56 authored by Elger Jonker's avatar Elger Jonker

List view of upcoming scans


Former-commit-id: 966283a7
parent dc3d6370
......@@ -7,6 +7,7 @@ from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import Group, User
from django.contrib.humanize.templatetags.humanize import naturaltime
# overwrites for period tasks, allowing import and export buttons to work.
from django.utils.safestring import mark_safe
from django_celery_beat.admin import PeriodicTaskAdmin, PeriodicTaskForm
from django_celery_beat.models import CrontabSchedule, IntervalSchedule, PeriodicTask, SolarSchedule
from import_export import resources
......@@ -54,13 +55,21 @@ class MyPeriodicTaskForm(PeriodicTaskForm):
class IEPeriodicTaskAdmin(PeriodicTaskAdmin, ImportExportModelAdmin):
# most / all time schedule functions in celery beat are moot. So the code below likely makes no sense.
list_display = ('name', 'enabled', 'interval', 'crontab', 'next', 'due',
'precise', 'queue', 'task', 'args', 'last_run', 'runs')
list_display = ('name_safe', 'enabled', 'interval', 'crontab', 'next', 'due',
'precise', 'last_run_at', 'queue', 'task', 'args', 'last_run', 'runs')
list_filter = ('enabled', 'queue', 'crontab')
search_fields = ('name', 'queue', 'args')
form = MyPeriodicTaskForm
save_as = True
@staticmethod
def name_safe(obj):
return mark_safe(obj.name)
@staticmethod
def last_run(obj):
return obj.last_run_at
......
......@@ -770,4 +770,9 @@ th.active .arrow {
width: 100%;
background-color: #e3d1cb33;
border-radius: 3px;
}
.schedule_row {
font-size: 1.2em;
margin-bottom: 6px;
}
\ No newline at end of file
......@@ -1591,6 +1591,30 @@ function views() {
},
});
window.vueSchedule = new Vue({
name: "schedule",
el: '#schedule',
data: {
next: Array(),
previous: Array(),
},
mounted: function () {
this.load()
},
methods: {
load: function() {
fetch('/data/upcoming_and_past_scans/').then(response => response.json()).then(data => {
this.next = data.next;
this.previous = data.previous;
}).catch((fail) => {
console.log('An error occurred: ' + fail)
});
},
},
});
window.vueInfo = new Vue({
name: 'infobox',
el: '#infobox',
......
......@@ -1180,6 +1180,32 @@
</section>
{% endif %}
{% if config.SHOW_SCAN_SCHEDULE %}
<section name="scan_schedule">
<div class="container">
<div id="schedule">
<div class="page-header">
<a href="#" class="backtomap">{% trans "back to map" %} ↑</a>
<a name="schedule" class="jumptonav"></a>
<h2>{% trans "Scan schedule" %}</h2>
{% trans "Below scans and tasks will be performed in the future. This can help you getting a better perspective on when updates will be hapening to your organization. Note that all times are approximate due to caching and rounding. The time listed is the time the scan starts, it might take a while before your organization is scanned." %}
<p></p>
</div>
<div class="row schedule_row" v-for="next_item in next">
{% verbatim %}
<div class="col-md-3 schedule_item">
{{ next_item.human_date }}
</div>
<div class="col-md-9 schedule_item" v-html="next_item.name">
</div>
{% endverbatim %}
</div>
</div>
</div>
</section>
{% endif %}
{% if config.SHOW_DATASETS %}
<section name="datasets">
<div class="container">
......
......@@ -75,6 +75,8 @@ urlpatterns = [
# this is not a single dataset, so building all kinds of exports was a bit harder, when needed we can build it.
path('data/export/explains/<c:country>/<slug:organization_type>/', views.export_explains),
path('data/upcoming_and_past_scans/', views.upcoming_and_past_scans),
# Proxy maptile requests,
# In production this can be done by caching proxy, this makes sure it works for dev. as well.
url(r'^proxy/(?P<url>https://api.mapbox.com/styles/v1/mapbox/.*./$)',
......
......@@ -17,9 +17,11 @@ from django.db.models import Count
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from django.utils import timezone
from django.utils.safestring import mark_safe
from django.utils.text import slugify
from django.utils.translation import ugettext as _
from django.views.decorators.cache import cache_page
from django_celery_beat.models import PeriodicTask
from import_export.resources import modelresource_factory
from failmap.map.models import (Configuration, MapDataCache, OrganizationRating, UrlRating,
......@@ -1962,3 +1964,47 @@ def organization_autcomplete(request, country: str = "NL", organization_type="mu
qs = qs.filter(name__icontains=parameter).values_list('name', flat=True)
return JsonResponse(list(qs), encoder=JSEncoder, json_dumps_params={'indent': 2}, safe=False)
def upcoming_and_past_scans(request):
def next(obj):
z, y = obj.schedule.is_due(last_run_at=datetime.now(pytz.utc))
date = datetime.now(pytz.utc) + timedelta(seconds=y)
return date
periodic_tasks = PeriodicTask.objects.all().filter(
enabled=True
).exclude(
# Don't show system tasks that are not in the knowledge-domain of the site user.
name__contains="celery.backend_cleanup"
).exclude(
# Don't show system tasks that are not in the knowledge-domain of the site user.
name__contains="failmap"
).exclude(
# Don't show tasks that are performed extremely frequently like onboarding.
crontab__minute__in=["*/5", "*/1", "*/10", "*/15", "*/30"]
)
next_scans = [] # upcoming scans
last_scans = [] # scans performed in the past
# get standardized task names.
# do not add
for periodic_task in periodic_tasks:
scan = {}
next_date = next(periodic_task)
scan['name'] = mark_safe(periodic_task.name)
scan['date'] = next_date
scan['human_date'] = naturaltime(next_date).capitalize()
# Tried cron_descriptor, but the text isn't as good as crontab guru.
# the translations aren't that great, also doesn't match django locale.
# scan['repetition'] = descripter.get_description(DescriptionTypeEnum.FULL)
next_scans.append(scan)
# ordering
next_scans = sorted(next_scans, key=lambda k: k['date'], reverse=False)
# last scans is not supportes, since celery doesn't store this information.
data = {'next': next_scans, 'last': last_scans}
return JsonResponse(data, encoder=JSEncoder, json_dumps_params={'indent': 2}, safe=False)
......@@ -696,6 +696,7 @@ CONSTANCE_CONFIG = {
'SHOW_COMPLY_OR_EXPLAIN_DISCUSS': (False, 'Shows a link to the comply or explain discussion forum. The url of this'
'forum can be edited below.', bool),
'SHOW_TICKER': (False, 'Shows stock-ticker with updates in the past month.', bool),
'SHOW_SCAN_SCHEDULE': (False, 'Shows list of upcoming scans, so everyone knows what scan is due next.', bool),
'SHOW_SERVICES': (True, 'Show table with how many services are scanned. Requires SHOW_STATS_NUMBERS.', bool),
'SHOW_DNS_DNSSEC': (True, 'Show graphs/stats of this? May cause empty spots on the site.', bool),
......@@ -776,7 +777,7 @@ CONSTANCE_CONFIG_FIELDSETS = OrderedDict([
'SHOW_TICKER',
'SHOW_DNS_DNSSEC', 'SHOW_HTTP_TLS_QUALYS', 'SHOW_HTTP_MISSING_TLS',
'SHOW_HTTP_HEADERS_HSTS', 'SHOW_HTTP_HEADERS_XFO', 'SHOW_HTTP_HEADERS_X_XSS',
'SHOW_HTTP_HEADERS_X_CONTENT', 'SHOW_FTP'
'SHOW_HTTP_HEADERS_X_CONTENT', 'SHOW_FTP', 'SHOW_SCAN_SCHEDULE'
)),
('Comply or Explain', ('SHOW_COMPLY_OR_EXPLAIN', 'SHOW_COMPLY_OR_EXPLAIN_DISCUSS',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment