Commit 04049d95 authored by cermak's avatar cermak
Browse files

Added measurement logs view

Views refactoring
parent 4c26635c
# Generated by Django 3.0.6 on 2020-06-09 13:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0052_auto_20200609_0842'),
]
operations = [
migrations.AlterField(
model_name='usage',
name='amount',
field=models.FloatField(),
),
]
......@@ -868,7 +868,7 @@ class Log(models.Model):
super().save(*args, **kwargs)
class Usage(models.Model):
amount = models.IntegerField()
amount = models.FloatField()
resource = models.ForeignKey('app.Resource', on_delete=models.PROTECT)
log = models.ForeignKey('app.Log', on_delete=models.PROTECT)
......
# app/tables.py
import django_tables2 as tables
from .models import Proposals, Contacts, User, Status, Instruments, Experiments
from .models import Proposals, Contacts, User, Status, Instruments, Experiments, Log, Resource
from django_tables2.utils import A # alias for Accessor
import django_filters
from django.db.models import Q
......@@ -166,3 +166,32 @@ def instruments(request):
return qs
class LogTable(tables.Table):
usage_set = tables.ManyToManyColumn(transform=lambda r: f'{r.resource.name}={r.amount}{r.resource.unit}', verbose_name='Used Resources')
class Meta:
model = Log
template_name = 'django_tables2/bootstrap4.html'
exclude = ('created', 'last_updated', 'id', 'proposal')
sequence = ('instrument', '...')
attrs = { 'class': 'table table-striped table-sm table-hover'}
class LogSumTable(tables.Table):
instrument__name = tables.Column('Instrument')
sumduration = tables.Column(verbose_name='Summed duration')
class Meta:
template_name = 'django_tables2/bootstrap4.html'
attrs = { 'class': 'table table-striped table-sm table-hover'}
class UsedResourcesTable(tables.Table):
name = tables.Column()
sumamount = tables.Column(verbose_name='Summed amount')
def render_sumamount(self, value, record):
return f"{value} {record['unit']}"
class Meta:
template_name = 'django_tables2/bootstrap4.html'
attrs = { 'class': 'table table-striped table-sm table-hover'}
......@@ -112,7 +112,9 @@
{% elif perms.app.change_status and object.last_status in "AFXH" %}
<a class="btn btn-primary" href="{% url 'app_report_create' proposal_slug=object.slug %}">Create a report request</a>
{% endif %}
{% if object.last_status in "AFXH" %}
<a class="btn btn-info" href="{% url 'app_log_list' proposal_slug=object.slug %}">View measurement logs</a>
{% endif %}
<header>
<h2 class="h3 display">Status history</h2>
</header>
......
{% extends "form.html" %}
{% load static %}
{% load django_tables2 %}
{% load bootstrap4 %}
{% block breadcrumbs %}
<li class="breadcrumb-item">Proposal</li>
<li class="breadcrumb-item"><a href="{% url 'app_proposals_detail' slug=object.slug %}">{{ object.pid }}</a></li>
<li class="breadcrumb-item">Logs</li>
{% endblock %}
{% block formcontent %}
<header>
<h1 class="h3 display">Measurement logs for proposal {{ object.pid }}</h1>
</header>
</div></section>
<section class="dashboard-header"><div class="container-fluid">
<h2>Summary per instrument:</h2>
{% render_table tables.0 %}
</div></section>
<section class="dashboard-header"><div class="container-fluid">
<h2>Summary of used resources:</h2>
{% render_table tables.1 %}
</div></section>
<section class="dashboard-header"><div class="container-fluid">
<h2>All measurements</h2>
{% render_table tables.2 %}
{% endblock %}
\ No newline at end of file
......@@ -2,4 +2,5 @@ from .htmlviews import *
from .pdfviews import *
from .bookingviews import *
from .ajaxviews import *
from .proposalviews import *
from .userofficeviews import *
\ No newline at end of file
......@@ -17,17 +17,16 @@ from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.utils.encoding import force_bytes, force_text
from datetime import datetime
from django.views.generic import DetailView, ListView, UpdateView, CreateView, DeleteView
from app.models import Proposals, Instruments, Contacts, Affiliations, Countries, Options, SharedOptions
from app.models import Samples, SamplePhotos, SampleRemarks, Publication, Experiments, Status, Report
from app.forms import ProposalsForm, InstrumentsForm, ContactsForm, StatusForm, SamplesForm, ReportForm
from app.models import Instruments, Contacts, Affiliations, Countries, Options, SharedOptions
from app.models import Samples, SamplePhotos, SampleRemarks, Publication, Experiments, Proposals, Report
from app.forms import InstrumentsForm, ContactsForm, SamplesForm
from app.forms import SamplePhotosForm, SampleRemarksForm, PublicationForm, ExperimentsForm, SignupForm, ProfileForm, UserForm
from django.contrib.auth.decorators import login_required, permission_required
from django.core.mail import EmailMessage
from django.db.models import Q
from app.token import account_activation_token
from app.tables import ProposalTable, ProposalFilter, ContactsTable
from django_tables2.views import SingleTableView, SingleTableMixin
from django_filters.views import FilterView
from app.tables import ContactsTable
from django_tables2.views import SingleTableMixin
from django.core.exceptions import PermissionDenied
......@@ -345,189 +344,6 @@ def change_password(request):
'form': form
})
class ProposalsListView(LoginRequiredMixin, SingleTableMixin, FilterView):
model = Proposals
table_class = ProposalTable
paginate_by = 25
template_name = "proposal/list.html"
filterset_class = ProposalFilter
def get_queryset(self):
queryset = Proposals.objects.distinct()
if self.kwargs['filtering'] == "mine":
queryset = queryset.filter(
Q(proposer=self.request.user) |
Q(coproposers__uid__exact=self.request.user) |
Q(local_contacts__uid__exact=self.request.user)).distinct()
else:
#check permissions
if not self.request.user.has_perm('app.view_proposals'):
if self.request.user.has_perm('app.view_panel_proposals'):
queryset = queryset.exclude(last_status__in='PSU').exclude(proposaltype='T').exclude(review_process='B')
elif self.request.user.has_perm('app.view_board_proposals'):
queryset = queryset.exclude(last_status__in='PSU').exclude(proposaltype='T').exclude(review_process='P')
else:
queryset = queryset.filter(Q(proposer=self.request.user) |
Q(coproposers__uid__exact=self.request.user) |
Q(local_contacts__uid__exact=self.request.user))
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filtering'] = self.kwargs['filtering']
return context
class StatusCreateView(LoginRequiredMixin, CreateView):
model = Status
form_class = StatusForm
template_name = "proposal/status.html"
current_proposal = None
def get_initial(self):
initial = super(StatusCreateView, self).get_initial()
if "proposal_slug" in self.kwargs:
self.current_proposal = Proposals.objects.get(slug=self.kwargs["proposal_slug"])
initial.update({"proposal": self.current_proposal})
else:
raise Http404
if "new_status" in self.kwargs:
initial.update({"status": self.kwargs["new_status"]})
return initial
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['proposal'] = self.current_proposal
return context
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({ 'user': self.request.user})
return kwargs
def get_success_url(self):
return reverse('app_proposals_detail', args={ self.kwargs["proposal_slug"]})
class ProposalsCreateView(PermissionRequiredMixin, CreateView):
model = Proposals
form_class = ProposalsForm
template_name = "proposal/form.html"
permission_required = 'app.add_proposals'
permission_denied_message = 'You are not allowed to create proposals. You need to to fill your <a href="/profile">profile</a> first.'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({ 'user': self.request.user})
return kwargs
def form_valid(self, form):
form.instance.proposer = self.request.user
return super().form_valid(form)
class ProposalsDetailView(LoginRequiredMixin, DetailView):
model = Proposals
template_name = "proposal/detail.html"
def get_queryset(self):
# check permission
if self.request.user.has_perm('app.view_proposals'):
qs = super(ProposalsDetailView, self).get_queryset().distinct()
elif self.request.user.has_perm('app.view_panel_proposals'):
qs = super(ProposalsDetailView, self).get_queryset().exclude(last_status__in='P').exclude(proposaltype='T').distinct()
else: # can view only if it is part of the team
qs = super(ProposalsDetailView, self).get_queryset().filter(Q(proposer=self.request.user) |
Q(coproposers__uid__exact=self.request.user) |
Q(local_contacts__uid__exact=self.request.user)).distinct()
return qs
def get_context_data(self, *args, **kwargs):
context = super(ProposalsDetailView, self).get_context_data(*args, **kwargs)
context['status_history'] = Status.objects.filter(proposal=self.object)
return context
class ProposalsUpdateView(LoginRequiredMixin, UpdateView):
model = Proposals
form_class = ProposalsForm
template_name = "proposal/form.html"
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({ 'user': self.request.user})
kwargs.update({ 'status': super().get_object().last_status})
kwargs.update({ 'local_contacts': super().get_object().local_contacts})
kwargs.update({ 'proposer': super().get_object().proposer})
return kwargs
class ProposalsDelete(LoginRequiredMixin, DeleteView):
model = Proposals
success_url = reverse_lazy('app_proposals_list')
template_name = "proposal/delete.html"
def get_object(self, queryset=None):
""" Hook to ensure object is owned by request.user. """
obj = super(ProposalsDelete, self).get_object()
if not obj.proposer == self.request.user and obj.last_status == "P":
raise Http404
return obj
class ReportCreateView(PermissionRequiredMixin, CreateView):
model = Report
form_class = ReportForm
template_name = "proposal/report_form.html"
permission_required = 'app.change_status'
permission_denied_message = 'You are not allowed to create report requests.'
def get_initial(self):
initial = super().get_initial()
self.current_proposal = Proposals.objects.get(slug=self.kwargs["proposal_slug"])
if "proposal_slug" in self.kwargs:
initial.update({"proposal": self.current_proposal})
else:
raise Http404
return initial
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['proposal'] = self.current_proposal
return context
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({ 'user': self.request.user})
return kwargs
def get_success_url(self):
return reverse('app_proposals_detail', args={ self.kwargs["proposal_slug"]})
class ReportDetailView(DetailView):
model = Report
template_name = "proposal/report_detail.html"
class ReportUpdateView(UpdateView):
model = Report
form_class = ReportForm
template_name = "proposal/report_form.html"
def get_success_url(self):
return reverse('app_proposals_detail', args={ self.object.proposal.slug})
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({ 'user': self.request.user})
return kwargs
class InstrumentsListView(ListView):
......
"""
Definition of proposal relatated views.
Means also reports and logs
"""
from app.tables import ProposalTable, ProposalFilter, LogTable, LogSumTable, UsedResourcesTable
from app.models import Proposals, Status, Report, Log, Resource
from app.forms import ProposalsForm, StatusForm, ReportForm
from django.contrib.auth.models import User, Group
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from django.views.generic import DetailView, ListView, UpdateView, CreateView, DeleteView, TemplateView
from django.urls import reverse, reverse_lazy
from django.db.models import Q, Sum, Count
from django_tables2.views import SingleTableView, SingleTableMixin, MultiTableMixin
from django_filters.views import FilterView
class ProposalsListView(LoginRequiredMixin, SingleTableMixin, FilterView):
model = Proposals
table_class = ProposalTable
paginate_by = 25
template_name = "proposal/list.html"
filterset_class = ProposalFilter
def get_queryset(self):
queryset = Proposals.objects.distinct()
if self.kwargs['filtering'] == "mine":
queryset = queryset.filter(
Q(proposer=self.request.user) |
Q(coproposers__uid__exact=self.request.user) |
Q(local_contacts__uid__exact=self.request.user)).distinct()
else:
#check permissions
if not self.request.user.has_perm('app.view_proposals'):
if self.request.user.has_perm('app.view_panel_proposals'):
queryset = queryset.exclude(last_status__in='PSU').exclude(proposaltype='T').exclude(review_process='B')
elif self.request.user.has_perm('app.view_board_proposals'):
queryset = queryset.exclude(last_status__in='PSU').exclude(proposaltype='T').exclude(review_process='P')
else:
queryset = queryset.filter(Q(proposer=self.request.user) |
Q(coproposers__uid__exact=self.request.user) |
Q(local_contacts__uid__exact=self.request.user))
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filtering'] = self.kwargs['filtering']
return context
class StatusCreateView(LoginRequiredMixin, CreateView):
model = Status
form_class = StatusForm
template_name = "proposal/status.html"
current_proposal = None
def get_initial(self):
initial = super(StatusCreateView, self).get_initial()
if "proposal_slug" in self.kwargs:
self.current_proposal = Proposals.objects.get(slug=self.kwargs["proposal_slug"])
initial.update({"proposal": self.current_proposal})
else:
raise Http404
if "new_status" in self.kwargs:
initial.update({"status": self.kwargs["new_status"]})
return initial
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['proposal'] = self.current_proposal
return context
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({ 'user': self.request.user})
return kwargs
def get_success_url(self):
return reverse('app_proposals_detail', args={ self.kwargs["proposal_slug"]})
class ProposalsCreateView(PermissionRequiredMixin, CreateView):
model = Proposals
form_class = ProposalsForm
template_name = "proposal/form.html"
permission_required = 'app.add_proposals'
permission_denied_message = 'You are not allowed to create proposals. You need to to fill your <a href="/profile">profile</a> first.'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({ 'user': self.request.user})
return kwargs
def form_valid(self, form):
form.instance.proposer = self.request.user
return super().form_valid(form)
class ProposalsDetailView(LoginRequiredMixin, DetailView):
model = Proposals
template_name = "proposal/detail.html"
def get_queryset(self):
# check permission
if self.request.user.has_perm('app.view_proposals'):
qs = super(ProposalsDetailView, self).get_queryset().distinct()
elif self.request.user.has_perm('app.view_panel_proposals'):
qs = super(ProposalsDetailView, self).get_queryset().exclude(last_status__in='P').exclude(proposaltype='T').distinct()
else: # can view only if it is part of the team
qs = super(ProposalsDetailView, self).get_queryset().filter(Q(proposer=self.request.user) |
Q(coproposers__uid__exact=self.request.user) |
Q(local_contacts__uid__exact=self.request.user)).distinct()
return qs
def get_context_data(self, *args, **kwargs):
context = super(ProposalsDetailView, self).get_context_data(*args, **kwargs)
context['status_history'] = Status.objects.filter(proposal=self.object)
return context
class ProposalsUpdateView(LoginRequiredMixin, UpdateView):
model = Proposals
form_class = ProposalsForm
template_name = "proposal/form.html"
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({ 'user': self.request.user})
kwargs.update({ 'status': super().get_object().last_status})
kwargs.update({ 'local_contacts': super().get_object().local_contacts})
kwargs.update({ 'proposer': super().get_object().proposer})
return kwargs
class ProposalsDelete(LoginRequiredMixin, DeleteView):
model = Proposals
success_url = reverse_lazy('app_proposals_list')
template_name = "proposal/delete.html"
def get_object(self, queryset=None):
""" Hook to ensure object is owned by request.user. """
obj = super(ProposalsDelete, self).get_object()
if not obj.proposer == self.request.user and obj.last_status == "P":
raise Http404
return obj
class ReportCreateView(PermissionRequiredMixin, CreateView):
model = Report
form_class = ReportForm
template_name = "proposal/report_form.html"
permission_required = 'app.change_status'
permission_denied_message = 'You are not allowed to create report requests.'
def get_initial(self):
initial = super().get_initial()
self.current_proposal = Proposals.objects.get(slug=self.kwargs["proposal_slug"])
if "proposal_slug" in self.kwargs:
initial.update({"proposal": self.current_proposal})
else:
raise Http404
return initial
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['proposal'] = self.current_proposal
return context
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({ 'user': self.request.user})
return kwargs
def get_success_url(self):
return reverse('app_proposals_detail', args={ self.kwargs["proposal_slug"]})
class ReportDetailView(DetailView):
model = Report
template_name = "proposal/report_detail.html"
class ReportUpdateView(UpdateView):
model = Report
form_class = ReportForm
template_name = "proposal/report_form.html"
def get_success_url(self):
return reverse('app_proposals_detail', args={ self.object.proposal.slug})
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({ 'user': self.request.user})
return kwargs
class LogsListView(LoginRequiredMixin, MultiTableMixin, TemplateView):
table_pagination = {
"per_page": 25
}
template_name = "proposal/log_list.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['object'] = Proposals.objects.get(slug=self.kwargs["proposal_slug"])
return context
def get_tables(self):
qs_logs = Log.objects.filter(proposal__slug = self.kwargs["proposal_slug"])
qs_sum = qs_logs.values('instrument__name').annotate(sumduration=Sum('duration')).order_by()
qs_resources = Resource.objects.filter(log__proposal__slug = self.kwargs["proposal_slug"]).values('name', 'unit').annotate(sumamount=Sum('usage__amount'))
return [
LogSumTable(qs_sum),
UsedResourcesTable(qs_resources),
LogTable(qs_logs),
]
......@@ -67,6 +67,9 @@
<Compile Include="app\views\bookingviews.py" />
<Compile Include="app\views\pdfviews.py" />
<Compile Include="app\views\htmlviews.py" />
<Compile Include="app\views\proposalviews.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="app\views\userofficeviews.py">
<SubType>Code</SubType>
</Compile>
......@@ -148,6 +151,7 @@
<Content Include="app\templates\pinax\notifications\x_booking_new\full.txt" />
<Content Include="app\templates\pinax\notifications\x_reminder\full.txt" />
<Content Include="app\templates\pinax\notifications\X_report\full.txt" />
<Content Include="app\templates\proposal\log_list.html" />
<Content Include="app\templates\proposal\report_detail.html" />
<Content Include="app\templates\proposal\report_form.html" />
<Content Include="app\templates\booking\sos_form.html" />
......
......@@ -91,6 +91,7 @@ urlpatterns += (
url(r'^proposals/(?P<proposal_slug>\S+)/report/create/$', views.ReportCreateView.as_view(), name='app_report_create'),
url(r'^proposals/report/detail/(?P<pk>\S+)/$', views.ReportDetailView.as_view(), name='app_report_detail'),
url(r'^proposals/report/update/(?P<pk>\S+)/$', views.ReportUpdateView.as_view(), name='app_report_update'),
url(r'^proposals/(?P<proposal_slug>\S+)/logs$', views.LogsListView.as_view(), name='app_log_list'),
)
urlpatterns += (
......
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