Added calendar view

This commit is contained in:
David Taylor
2015-04-19 22:25:59 +01:00
parent 8c135fbc18
commit df1a285089
10 changed files with 12284 additions and 11 deletions

View File

@@ -33,6 +33,8 @@ class RigboardIndex(generic.TemplateView):
context['events'] = models.Event.objects.current_events() context['events'] = models.Event.objects.current_events()
return context return context
class WebCalendar(generic.TemplateView):
template_name = 'RIGS/calendar.html'
class EventDetail(generic.DetailView): class EventDetail(generic.DetailView):
model = models.Event model = models.Event

1061
RIGS/static/css/fullcalendar.css Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,202 @@
/*!
* FullCalendar v2.3.1 Print Stylesheet
* Docs & License: http://fullcalendar.io/
* (c) 2015 Adam Shaw
*/
/*
* Include this stylesheet on your page to get a more printer-friendly calendar.
* When including this stylesheet, use the media='print' attribute of the <link> tag.
* Make sure to include this stylesheet IN ADDITION to the regular fullcalendar.css.
*/
.fc {
max-width: 100% !important;
}
/* Global Event Restyling
--------------------------------------------------------------------------------------------------*/
.fc-event {
background: #fff !important;
color: #000 !important;
page-break-inside: avoid;
}
.fc-event .fc-resizer {
display: none;
}
/* Table & Day-Row Restyling
--------------------------------------------------------------------------------------------------*/
th,
td,
hr,
thead,
tbody,
.fc-row {
border-color: #ccc !important;
background: #fff !important;
}
/* kill the overlaid, absolutely-positioned common components */
.fc-bg,
.fc-bgevent-skeleton,
.fc-highlight-skeleton,
.fc-helper-skeleton {
display: none;
}
/* don't force a min-height on rows (for DayGrid) */
.fc tbody .fc-row {
height: auto !important; /* undo height that JS set in distributeHeight */
min-height: 0 !important; /* undo the min-height from each view's specific stylesheet */
}
.fc tbody .fc-row .fc-content-skeleton {
position: static; /* undo .fc-rigid */
padding-bottom: 0 !important; /* use a more border-friendly method for this... */
}
.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td { /* only works in newer browsers */
padding-bottom: 1em; /* ...gives space within the skeleton. also ensures min height in a way */
}
.fc tbody .fc-row .fc-content-skeleton table {
/* provides a min-height for the row, but only effective for IE, which exaggerates this value,
making it look more like 3em. for other browers, it will already be this tall */
height: 1em;
}
/* Undo month-view event limiting. Display all events and hide the "more" links
--------------------------------------------------------------------------------------------------*/
.fc-more-cell,
.fc-more {
display: none !important;
}
.fc tr.fc-limited {
display: table-row !important;
}
.fc td.fc-limited {
display: table-cell !important;
}
.fc-popover {
display: none; /* never display the "more.." popover in print mode */
}
/* TimeGrid Restyling
--------------------------------------------------------------------------------------------------*/
/* undo the min-height 100% trick used to fill the container's height */
.fc-time-grid {
min-height: 0 !important;
}
/* don't display the side axis at all ("all-day" and time cells) */
.fc-agenda-view .fc-axis {
display: none;
}
/* don't display the horizontal lines */
.fc-slats,
.fc-time-grid hr { /* this hr is used when height is underused and needs to be filled */
display: none !important; /* important overrides inline declaration */
}
/* let the container that holds the events be naturally positioned and create real height */
.fc-time-grid .fc-content-skeleton {
position: static;
}
/* in case there are no events, we still want some height */
.fc-time-grid .fc-content-skeleton table {
height: 4em;
}
/* kill the horizontal spacing made by the event container. event margins will be done below */
.fc-time-grid .fc-event-container {
margin: 0 !important;
}
/* TimeGrid *Event* Restyling
--------------------------------------------------------------------------------------------------*/
/* naturally position events, vertically stacking them */
.fc-time-grid .fc-event {
position: static !important;
margin: 3px 2px !important;
}
/* for events that continue to a future day, give the bottom border back */
.fc-time-grid .fc-event.fc-not-end {
border-bottom-width: 1px !important;
}
/* indicate the event continues via "..." text */
.fc-time-grid .fc-event.fc-not-end:after {
content: "...";
}
/* for events that are continuations from previous days, give the top border back */
.fc-time-grid .fc-event.fc-not-start {
border-top-width: 1px !important;
}
/* indicate the event is a continuation via "..." text */
.fc-time-grid .fc-event.fc-not-start:before {
content: "...";
}
/* time */
/* undo a previous declaration and let the time text span to a second line */
.fc-time-grid .fc-event .fc-time {
white-space: normal !important;
}
/* hide the the time that is normally displayed... */
.fc-time-grid .fc-event .fc-time span {
display: none;
}
/* ...replace it with a more verbose version (includes AM/PM) stored in an html attribute */
.fc-time-grid .fc-event .fc-time:after {
content: attr(data-full);
}
/* Vertical Scroller & Containers
--------------------------------------------------------------------------------------------------*/
/* kill the scrollbars and allow natural height */
.fc-scroller,
.fc-day-grid-container, /* these divs might be assigned height, which we need to cleared */
.fc-time-grid-container { /* */
overflow: visible !important;
height: auto !important;
}
/* kill the horizontal border/padding used to compensate for scrollbars */
.fc-row {
border: 0 !important;
margin: 0 !important;
}
/* Button Controls
--------------------------------------------------------------------------------------------------*/
.fc-button-group,
.fc button {
display: none; /* don't display any button-related controls */
}

10789
RIGS/static/js/fullcalendar.js Executable file

File diff suppressed because it is too large Load Diff

7
RIGS/static/js/moment.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,111 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Calendar{% endblock %}
{% block css %}
<link href="{% static "css/fullcalendar.css" %}" rel='stylesheet' />
<link href="{% static "css/fullcalendar.print.css" %}" rel='stylesheet' media='print' />
{% endblock %}
{% block js %}
{# <script src="//code.jquery.com/jquery-latest.min.js"></script> #}
<script src="{% static "js/moment.min.js" %}"></script>
<script src="{% static "js/fullcalendar.js" %}"></script>
<script>
$(document).ready(function() {
$('#calendar').fullCalendar({
//defaultDate: '2015-02-12',
editable: false,
eventLimit: true, // allow "more" link when too many events
firstDay: 1,
aspectRatio: 1.5,
timeFormat: 'HH:mm',
views: {
basic: {
// options apply to basicWeek and basicDay views
},
agenda: {
// options apply to agendaWeek and agendaDay views
},
week: {
columnFormat:'ddd D/M'
},
day: {
// options apply to basicDay and agendaDay views
}
},
buttonText:{
today: 'Today',
month: 'Month',
week: 'Week',
day: 'Day'
},
header:{
left: 'title',
center: '',
right: 'today prev,next month,agendaWeek,agendaDay'
},
events: function(start_moment, end_moment, timezone, callback) {
$.ajax({
url: '/api/event',
dataType: 'json',
data: {
start: moment(start_moment).format("YYYY-MM-DD[T]HH:mm:ss[Z]"),
end: moment(end_moment).format("YYYY-MM-DD[T]HH:mm:ss[Z]")
},
success: function(doc) {
var events = [];
var colours =
$(doc).each(function() {
thisEvent = [];
colours = {'Provisional': 'orange',
'Confirmed': 'green' ,
'Booked': 'green' ,
'Cancelled': 'grey'
};
thisEvent['start'] = $(this).attr('start_date');
if ($(this).attr('start_time')) {
thisEvent['start'] += 'T' + $(this).attr('start_time');
}
if ($(this).attr('end_date')) {
thisEvent['end'] = $(this).attr('end_date');
if ($(this).attr('end_time')) {
thisEvent['end'] += 'T' + $(this).attr('end_time');
}
}
thisEvent['className'] = 'modal-href';
thisEvent['title'] = $(this).attr('title');
thisEvent['color'] = colours[$(this).attr('status')];
thisEvent['url'] = $(this).attr('url');
events.push(thisEvent);
});
callback(events);
}
});
}
});
});
</script>
{% endblock %}
{% block content %}
<div class="row">
<div style="col-sm-12">
<div id='calendar'>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,13 +1,22 @@
{% extends 'base.html' %} {% extends request.is_ajax|yesno:"base_ajax.html,base.html" %}
{% block title %}Event {% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }} {% block title %}Event {% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }}
{% endif %}{% endblock %} {% endif %}{% endblock %}
{% block buttons %}
{% if not request.is_ajax %}
{% endif %}
{% endblock %}
{% block content %} {% block content %}
<div class="row">
{% if not request.is_ajax %}
<div class="col-sm-12"> <div class="col-sm-12">
<div class="col-sm-8"> <div class="col-sm-8">
<h1>Event {% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }}{% endif %}</h1> <h1>Event {% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }}{% endif %}</h1>
</div> </div>
<div class="col-sm-4 text-right"> <div class="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span <a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-edit"></span></a> class="glyphicon glyphicon-edit"></span></a>
@@ -26,7 +35,7 @@
</div> </div>
</div> </div>
</div> </div>
{% endif %}
{% if object.is_rig %} {% if object.is_rig %}
{# only need contact details for a rig #} {# only need contact details for a rig #}
<div class="col-sm-12 col-md-6 col-lg-5"> <div class="col-sm-12 col-md-6 col-lg-5">
@@ -154,6 +163,7 @@
</div> </div>
</div> </div>
</div> </div>
{% if not request.is_ajax %}
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span <a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
@@ -172,6 +182,7 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endif %}
{% if event.is_rig %} {% if event.is_rig %}
<div class="col-sm-12"> <div class="col-sm-12">
<div class="panel panel-default"> <div class="panel panel-default">
@@ -185,22 +196,45 @@
</div> </div>
</div> </div>
</div> </div>
{% if not request.is_ajax %}
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span <a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-edit"></span></a> class="glyphicon glyphicon-edit"></span></a>
<a href="{% url 'event_print' event.pk %}" class="btn btn-default"><span {% if event.is_rig %}
class="glyphicon glyphicon-print"></span></a> <a href="{% url 'event_print' event.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-print"></span></a>
{% endif %}
<a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default" title="Duplicate Rig"><span <a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default" title="Duplicate Rig"><span
class="glyphicon glyphicon-duplicate"></span></a> class="glyphicon glyphicon-duplicate"></span></a>
{% if perms.RIGS.add_invoice %} {% if event.is_rig %}
<a href="{% url 'invoice_event' event.pk %}" class="btn btn-default" title="Invoice Rig"><span {% if perms.RIGS.add_invoice %}
class="glyphicon glyphicon-gbp"></span></a> <a href="{% url 'invoice_event' event.pk %}" class="btn btn-default" title="Invoice Rig"><span
class="glyphicon glyphicon-gbp"></span></a>
{% endif %}
{% endif %} {% endif %}
</div> </div>
<div>Last edited at {{ object.last_edited_at|date:"SHORT_DATETIME_FORMAT" }} <div>Last edited at {{ object.last_edited_at|date:"SHORT_DATETIME_FORMAT" }}
by {{ object.last_edited_by.name }}. by {{ object.last_edited_by.name }}.
</div> </div>
</div> </div>
{% endif %}
{% endif %} {% endif %}
</div>
{% endblock %} {% endblock %}
{% if request.is_ajax %}
{% block footer %}
<div class="row">
<div class="col-sm-10 align-left">
Lasted edited at {{ object.last_edited_at|date:"SHORT_DATE_FORMAT" }} by {{ object.last_edited_by.name }}
</div>
<div class="col-sm-2">
<div class="pull-right">
<a href="{% url 'event_detail' object.pk %}" class="btn btn-primary">Open Event Page <span
class="glyphicon glyphicon-eye"></span></a>
</div>
</div>
</div>
{% endblock %}
{% endif %}

View File

@@ -57,6 +57,7 @@ urlpatterns = patterns('',
# Rigboard # Rigboard
url(r'^rigboard/$', rigboard.RigboardIndex.as_view(), name='rigboard'), url(r'^rigboard/$', rigboard.RigboardIndex.as_view(), name='rigboard'),
url(r'^rigboard/calendar/$', rigboard.WebCalendar.as_view(), name='web_calendar'),
url(r'^rigboard/archive/$', RedirectView.as_view(pattern_name='event_archive')), url(r'^rigboard/archive/$', RedirectView.as_view(pattern_name='event_archive')),
url(r'^event/(?P<pk>\d+)/$', url(r'^event/(?P<pk>\d+)/$',
@@ -122,8 +123,5 @@ urlpatterns = patterns('',
url(r'^rig/show/(?P<pk>\d+)/$', RedirectView.as_view(pattern_name='event_detail')), url(r'^rig/show/(?P<pk>\d+)/$', RedirectView.as_view(pattern_name='event_detail')),
url(r'^bookings/$', RedirectView.as_view(pattern_name='rigboard')), url(r'^bookings/$', RedirectView.as_view(pattern_name='rigboard')),
url(r'^bookings/past/$', RedirectView.as_view(pattern_name='event_archive')), url(r'^bookings/past/$', RedirectView.as_view(pattern_name='event_archive')),
# Calendar may have gone away, redirect to the archive for now
url(r'^rigboard/calendar/$',
RedirectView.as_view(pattern_name='event_archive', permanent=False)),
) )

View File

@@ -8,6 +8,7 @@ from django.shortcuts import get_object_or_404
from django.core import serializers from django.core import serializers
import simplejson import simplejson
from django.contrib import messages from django.contrib import messages
import datetime
from RIGS import models, forms from RIGS import models, forms
@@ -194,6 +195,7 @@ class SecureAPIRequest(generic.View):
'person': models.Person, 'person': models.Person,
'organisation': models.Organisation, 'organisation': models.Organisation,
'profile': models.Profile, 'profile': models.Profile,
'event': models.Event,
} }
perms = { perms = {
@@ -201,6 +203,7 @@ class SecureAPIRequest(generic.View):
'person': 'RIGS.view_person', 'person': 'RIGS.view_person',
'organisation': 'RIGS.view_organisation', 'organisation': 'RIGS.view_organisation',
'profile': None, 'profile': None,
'event': 'RIGS.view_event',
} }
''' '''
@@ -261,6 +264,71 @@ class SecureAPIRequest(generic.View):
json = simplejson.dumps(results[:20]) json = simplejson.dumps(results[:20])
return HttpResponse(json, content_type="application/json") # Always json return HttpResponse(json, content_type="application/json") # Always json
start = request.GET.get('start', None)
end = request.GET.get('end', None)
if model == "event" and start and end:
# Probably a calendar request
start_datetime = datetime.datetime.strptime( start, "%Y-%m-%dT%H:%M:%SZ" )
end_datetime = datetime.datetime.strptime( end, "%Y-%m-%dT%H:%M:%SZ" )
all_objects = self.models[model].objects
results = []
filter = Q(start_date__lte=end_datetime) & Q(start_date__gte=start_datetime)
objects = all_objects.filter(filter).select_related('person', 'organisation', 'venue', 'mic').order_by('-start_date')
for item in objects:
data = {
'pk': item.pk,
'title': item.name
}
data['is_rig'] = item.is_rig
data['status'] = str(item.get_status_display())
if item.start_date:
data['start_date'] = item.start_date.strftime('%Y-%m-%d')
if item.start_time:
data['start_time'] = item.start_time.strftime('%H:%M:%SZ')
if item.end_date:
data['end_date'] = item.end_date.strftime('%Y-%m-%d')
if item.end_time:
data['end_time'] = item.end_time.strftime('%H:%M:%SZ')
if item.meet_at:
data['meet_at'] = item.meet_at.strftime('%Y-%m-%dT%H:%M:%SZ')
if item.access_at:
data['access_at'] = item.access_at.strftime('%Y-%m-%dT%H:%M:%SZ')
if item.venue:
data['venue'] = item.venue.name
if item.person:
data['person'] = item.person.name
if item.organisation:
data['organisation'] = item.organisation.name
if item.mic:
data['mic'] = {
'name':item.mic.get_full_name(),
'initials':item.mic.initials
}
if item.description:
data['description'] = item.description
if item.notes:
data['notes'] = item.notes
data['url'] = str(reverse_lazy('event_detail',kwargs={'pk':item.pk}))
results.append(data)
json = simplejson.dumps(results)
return HttpResponse(json, content_type="application/json") # Always json
return HttpResponse(model) return HttpResponse(model)
class ProfileDetail(generic.DetailView): class ProfileDetail(generic.DetailView):

View File

@@ -42,6 +42,7 @@
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="{% url 'rigboard' %}">Rigboard</a></li> <li><a href="{% url 'rigboard' %}">Rigboard</a></li>
<li><a href="{% url 'event_archive' %}">Archive</a></li> <li><a href="{% url 'event_archive' %}">Archive</a></li>
<li><a href="{% url 'web_calendar' %}">Calendar</a></li>
</ul> </ul>
</li> </li>
{% if perms.RIGS.view_invoice %} {% if perms.RIGS.view_invoice %}