'django.contrib.webdesign',
'qcg',
'django_openid_auth',
+ 'bootstrap3',
)
MIDDLEWARE_CLASSES = (
--- /dev/null
+# coding=utf-8
+from django import forms
+from django.core.validators import RegexValidator
+from django.template.defaultfilters import capfirst
+from django.utils.functional import lazy
+from pyqcg.utils import TaskStatus
+
+from qcg.models import Task, Allocation
+
+
+def host_choices():
+ return tuple((host, capfirst(host.split('.')[0])) for host in
+ Allocation.objects.values_list('host_name', flat=True).order_by('host_name').distinct())
+
+
+date_range_validator = RegexValidator(r'[0-9]{2}\.[0-9]{2}\.[0-9]{4} - [0-9]{2}\.[0-9]{2}\.[0-9]{4}')
+
+
+class FiltersForm(forms.Form):
+ ACTIVE, FINISHED, FAILED = range(3)
+ STATUS_CHOICES = (
+ (ACTIVE, u"Aktywne"),
+ (FINISHED, u"Zakończone"),
+ (FAILED, u"Niepowodzenia"),
+ )
+
+ STATUS_MAP = {
+ ACTIVE: (
+ Task.STATUS_CHOICES_REVERSED[TaskStatus.QUEUED],
+ Task.STATUS_CHOICES_REVERSED[TaskStatus.PREPROCESSING],
+ Task.STATUS_CHOICES_REVERSED[TaskStatus.PENDING],
+ Task.STATUS_CHOICES_REVERSED[TaskStatus.RUNNING],
+ Task.STATUS_CHOICES_REVERSED[TaskStatus.STOPPED],
+ Task.STATUS_CHOICES_REVERSED[TaskStatus.POSTPROCESSING],
+ ),
+ FINISHED: (
+ Task.STATUS_CHOICES_REVERSED[TaskStatus.FINISHED],
+ Task.STATUS_CHOICES_REVERSED[TaskStatus.FAILED],
+ Task.STATUS_CHOICES_REVERSED[TaskStatus.CANCELED],
+ ),
+ FAILED: (
+ Task.STATUS_CHOICES_REVERSED[TaskStatus.FAILED],
+ Task.STATUS_CHOICES_REVERSED[TaskStatus.CANCELED],
+ ),
+ }
+
+ status = forms.MultipleChoiceField(choices=STATUS_CHOICES, label=u"Status", required=False,
+ widget=forms.CheckboxSelectMultiple)
+ host = forms.MultipleChoiceField(choices=lazy(host_choices, tuple)(), label=u"Host", required=False,
+ widget=forms.CheckboxSelectMultiple)
+
+ # advanced
+ keywords = forms.CharField(max_length=100, label=u"Wyszukaj frazę", required=False)
+ status_exact = forms.ChoiceField(choices=[(None, u"----------")] + Task.STATUS_CHOICES, label=u"Status", required=False)
+ submission = forms.CharField(label=u"Data zlecenia", validators=[date_range_validator], required=False)
+ finish = forms.CharField(label=u"Data zakończenia", validators=[date_range_validator], required=False)
{% extends 'qcg/base.html' %}
-
-{% load staticfiles %}
+{% load staticfiles bootstrap3 %}
{% block extra_css %}
<link href="{% static 'qcg/treegrid/css/jquery.treegrid.css' %}" rel="stylesheet">
+ <link href="{% static 'qcg/daterangepicker/daterangepicker-bs3.css' %}" rel="stylesheet" />
<style>
.treegrid-expander {
{% block extra_js %}
<script src="{% static 'qcg/treegrid/js/jquery.treegrid.js' %}"></script>
+ <script src="{% static 'qcg/daterangepicker/moment.min.js' %}"></script>
+ <script src="{% static 'qcg/daterangepicker/daterangepicker.js' %}"></script>
<script>
$(function() {
}
});
+ $('input[name="submission"],input[name="finish"]').daterangepicker({
+ opens: 'center',
+ format: 'DD.MM.YYYY',
+ ranges: {
+ 'Dzisiaj': [moment(), moment()],
+ 'Wczoraj': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
+ 'Ostatnie 7 dni': [moment().subtract(6, 'days'), moment()],
+ 'Ostatnie 30 dni': [moment().subtract(29, 'days'), moment()]
+ },
+ locale: {
+ applyLabel: 'OK',
+ cancelLabel: 'Anuluj',
+ fromLabel: 'Od',
+ toLabel: 'Do',
+ weekLabel: 'T',
+ customRangeLabel: 'Zakres',
+ daysOfWeek: moment.weekdaysMin(),
+ monthNames: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
+ firstDay: 1
+ }
+ });
+ $('#toggle-advanced').click(function() {
+ if($('#advanced-filters').attr('aria-expanded') == "false")
+ $(this).text('« mniej');
+ else
+ $(this).text('więcej »');
+ });
});
</script>
{% endblock %}
{% block title %}Lista zadań{% endblock %}
</h1>
-{# <form class="row form-inline">#}
-{# <div class="col-md-9 col-md-offset-1">#}
-{# <div class="form-group">#}
-{# <label class="sr-only" for="search">Szukaj w opisie lub uwagach</label>#}
-{# <input type="text" class="form-control" id="search" placeholder="Szukaj w opisie lub uwagach">#}
+ <form action=".">
+ <div class="row">
+ <div class="col-lg-10 col-lg-offset-1">
+ <div class="pull-right">
+ <a id="toggle-advanced" href="#advanced-filters" data-toggle="collapse">
+ {% if advanced %}
+ « mniej
+ {% else %}
+ więcej »
+ {% endif %}
+ </a>
+
+ <a href="." class="btn btn-default{% if not filters.data %} disabled{% endif %}">Wyczyść</a>
+ <button type="submit" class="btn btn-default">Filtruj</button>
+ </div>
+ <div class="btn-toolbar">
+ <div class="btn-group" data-toggle="buttons">
+ {% for choice in filters.status %}
+ <label class="btn btn-default{% if choice.is_checked %} active{% endif %}">
+ <input type="checkbox" name="{{ choice.name }}" value="{{ choice.choice_value }}"
+ {% if choice.is_checked %}checked{% endif %}> {{ choice.choice_label }}
+ </label>
+ {% endfor %}
+ </div>
+ <div class="btn-group" data-toggle="buttons">
+ {% for choice in filters.host %}
+ <label class="btn btn-default{% if choice.is_checked %} active{% endif %}">
+ <input type="checkbox" name="{{ choice.name }}" value="{{ choice.choice_value }}"
+ {% if choice.is_checked %}checked{% endif %}> {{ choice.choice_label }}
+ </label>
+ {% endfor %}
+ </div>
+ </div>
+ </div>
+ </div>
+{# <div id="advanced-filters" class="form-horizontal collapse in" aria-expanded="true" style="margin-top: 10px">#}
+{# <div class="row">#}
+{# {% bootstrap_field filters.keywords layout='horizontal' horizontal_label_class='col-md-5' horizontal_field_class='col-md-4' bound_css_class=' ' %}#}
{# </div>#}
-{# <div class="form-group">#}
-{# <label class="sr-only" for="status">Status</label>#}
-{# <select class="form-control" id="status">#}
-{# <option>Wybierz stan</option>#}
-{# <option>PENDING</option>#}
-{# <option>RUNNING</option>#}
-{# <option>FAILED</option>#}
-{# <option>FINISHED</option>#}
-{# </select>#}
+{# <div class="row">#}
+{# {% bootstrap_field filters.status_exact layout='horizontal' horizontal_label_class='col-md-5' horizontal_field_class='col-md-4' bound_css_class=' ' %}#}
{# </div>#}
-{# <div class="form-group">#}
-{# <label class="sr-only" for="cluster">Klaster</label>#}
-{# <select class="form-control" id="cluster">#}
-{# <option>Wybierz klaster</option>#}
-{# <option>Inula</option>#}
-{# <option>Galera</option>#}
-{# <option>Hydra</option>#}
-{# <option>Zeus</option>#}
-{# </select>#}
+{# <div class="row">#}
+{# {% bootstrap_field filters.submission layout='horizontal' horizontal_label_class='col-md-5' horizontal_field_class='col-md-4' bound_css_class=' ' %}#}
+{# </div>#}
+{# <div class="row">#}
+{# {% bootstrap_field filters.finish layout='horizontal' horizontal_label_class='col-md-5' horizontal_field_class='col-md-4' bound_css_class=' ' %}#}
{# </div>#}
{# </div>#}
-{# <div class="col-md-1">#}
-{# <button type="submit" class="btn btn-default">Filtruj</button>#}
-{# </div>#}
-{# </form>#}
-{##}
-{# <hr />#}
+ <div id="advanced-filters" class="form-horizontal collapse{% if advanced %} in" aria-expanded="true"{% else %}" aria-expanded="false"{% endif %} style="margin-top: 10px">
+ <div class="row">
+ <div class="col-ld-1"></div>
+ {% bootstrap_field filters.keywords layout='horizontal' form_group_class='form-group row col-lg-5 col-md-6' horizontal_label_class='col-md-4' horizontal_field_class='col-md-8' bound_css_class=' ' %}
+ {% bootstrap_field filters.status_exact layout='horizontal' form_group_class='form-group row col-lg-5 col-md-6' horizontal_label_class='col-md-4' horizontal_field_class='col-md-8' bound_css_class=' ' %}
+ </div>
+ <div class="row">
+ <div class="col-ld-1"></div>
+ {% bootstrap_field filters.submission layout='horizontal' form_group_class='form-group row col-lg-5 col-md-6' horizontal_label_class='col-md-4' horizontal_field_class='col-md-8' bound_css_class=' ' %}
+ {% bootstrap_field filters.finish layout='horizontal' form_group_class='form-group row col-lg-5 col-md-6' horizontal_label_class='col-md-4' horizontal_field_class='col-md-8' bound_css_class=' ' %}
+ </div>
+ </div>
+ </form>
+
+ <hr />
<nav class="text-center" style="margin-bottom: 15px">
</tbody>
</table>
+ {% if not page %}
+ <div class="alert alert-info">Brak elementów</div>
+ {% endif %}
+
<nav class="text-center">
<ul class="pagination">
{% if page.has_previous %}
+from datetime import datetime, timedelta
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
+from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from django.utils.http import urlencode
+from django.utils.timezone import UTC
from django_openid_auth.views import make_consumer
from openid.extensions import ax
from pyqcg import QCG
+from qcg.forms import FiltersForm
from qcg.utils import update_user_data, paginator_context
return HttpResponse(openid_request.htmlMarkup(request.build_absolute_uri('/'), return_to))
+search_fields = ('status_description', 'type', 'note', 'task_id', 'job__note', 'job__project', 'job__job_id',
+ 'allocations__status_description', 'allocations__processes_group_id', 'allocations__comment')
+
+
+def parse_date(string):
+ return datetime.strptime(string.strip(), "%d.%m.%Y").replace(tzinfo=UTC())
+
+
@login_required
def jobs_list(request):
# QCG.start()
# update_user_data(request.user, request.session['proxy'])
tasks = request.user.tasks.order_by('-job__submission_time', '-submission_time') \
- .select_related('job').prefetch_related('allocations__nodes')
-
- context = paginator_context(request, tasks)
+ .select_related('job').prefetch_related('allocations__nodes')
+
+ filters = FiltersForm(request.GET)
+ advanced = False
+ if filters.is_valid():
+ keywords = filters.cleaned_data['keywords']
+ status = filters.cleaned_data['status']
+ host = filters.cleaned_data['host']
+ status_exact = filters.cleaned_data['status_exact']
+ submission = filters.cleaned_data['submission']
+ finish = filters.cleaned_data['finish']
+
+ if status:
+ statuses = []
+ for s in status:
+ statuses.extend(FiltersForm.STATUS_MAP[int(s)])
+
+ print statuses
+
+ tasks = tasks.filter(status__in=statuses)
+ if host:
+ tasks = tasks.filter(allocations__host_name__in=host)
+
+ if keywords:
+ and_query = Q()
+
+ for q in keywords.split():
+ or_query = Q()
+ for field in search_fields:
+ or_query |= Q(**{field + '__icontains': q})
+ and_query &= or_query
+
+ tasks = tasks.filter(and_query)
+ if status_exact:
+ tasks = tasks.filter(status=status_exact)
+ if submission:
+ start, end = submission.split('-')
+
+ tasks = tasks.filter(submission_time__gte=parse_date(start),
+ submission_time__lte=parse_date(end) + timedelta(days=1))
+ if finish:
+ start, end = finish.split('-')
+
+ tasks = tasks.filter(finish_time__gte=parse_date(start),
+ finish_time__lte=parse_date(end) + timedelta(days=1))
+
+ advanced = bool(keywords or status_exact or submission or finish)
+
+ context = {'filters': filters, 'advanced': advanced}
+ context.update(paginator_context(request, tasks))
return render(request, 'qcg/jobs.html', context)
Django
django-grappelli
+django-bootstrap3
pkgs/django-openid-auth-0.5.1.tar.gz
python-openid
+pytz