mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-01-27 10:22:17 +00:00
95
RIGS/ical.py
95
RIGS/ical.py
@@ -17,15 +17,58 @@ class CalendarICS(ICalFeed):
|
|||||||
timezone = settings.TIME_ZONE
|
timezone = settings.TIME_ZONE
|
||||||
file_name = "rigs.ics"
|
file_name = "rigs.ics"
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
# Cancelled = 'cancelled' = False
|
||||||
timezone.activate(timezone.UTC)
|
# Dry Hire = 'dry-hire' = True
|
||||||
return super(CalendarICS, self).get(*args, **kwargs)
|
# Non Rig = 'non-rig' = True
|
||||||
|
# Rig = 'rig' = True
|
||||||
|
# Provisional = 'provisional' = True
|
||||||
|
# Confirmed/Booked = 'confirmed' = True
|
||||||
|
|
||||||
|
def get_object(self, request, *args, **kwargs):
|
||||||
|
params = {}
|
||||||
|
|
||||||
def items(self):
|
params['dry-hire'] = request.GET.get('dry-hire','true') == 'true'
|
||||||
|
params['non-rig'] = request.GET.get('non-rig','true') == 'true'
|
||||||
|
params['rig'] = request.GET.get('rig','true') == 'true'
|
||||||
|
|
||||||
|
params['cancelled'] = request.GET.get('cancelled','false') == 'true'
|
||||||
|
params['provisional'] = request.GET.get('provisional','true') == 'true'
|
||||||
|
params['confirmed'] = request.GET.get('confirmed','true') == 'true'
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
def description(self,params):
|
||||||
|
desc = "Calendar generated by RIGS system. This includes event types: " + ('Rig, ' if params['rig'] else '') + ('Non-rig, ' if params['non-rig'] else '') + ('Dry Hire ' if params['dry-hire'] else '') + '\n'
|
||||||
|
desc = desc + "Includes events with status: " + ('Cancelled, ' if params['cancelled'] else '') + ('Provisional, ' if params['provisional'] else '') + ('Confirmed/Booked, ' if params['confirmed'] else '')
|
||||||
|
|
||||||
|
return desc
|
||||||
|
|
||||||
|
def items(self, params):
|
||||||
#include events from up to 1 year ago
|
#include events from up to 1 year ago
|
||||||
start = datetime.datetime.now() - datetime.timedelta(days=365)
|
start = datetime.datetime.now() - datetime.timedelta(days=365)
|
||||||
filter = Q(start_date__gte=start) & ~Q(status=models.Event.CANCELLED)
|
filter = Q(start_date__gte=start)
|
||||||
|
|
||||||
|
typeFilters = Q(pk=None) #Need something that is false for every entry
|
||||||
|
|
||||||
|
if params['dry-hire']:
|
||||||
|
typeFilters = typeFilters | Q(dry_hire=True, is_rig=True)
|
||||||
|
|
||||||
|
if params['non-rig']:
|
||||||
|
typeFilters = typeFilters | Q(is_rig=False)
|
||||||
|
|
||||||
|
if params['rig']:
|
||||||
|
typeFilters = typeFilters | Q(is_rig=True, dry_hire=False)
|
||||||
|
|
||||||
|
statusFilters = Q(pk=None) #Need something that is false for every entry
|
||||||
|
|
||||||
|
if params['cancelled']:
|
||||||
|
statusFilters = statusFilters | Q(status=models.Event.CANCELLED)
|
||||||
|
if params['provisional']:
|
||||||
|
statusFilters = statusFilters | Q(status=models.Event.PROVISIONAL)
|
||||||
|
if params['confirmed']:
|
||||||
|
statusFilters = statusFilters | Q(status=models.Event.CONFIRMED) | Q(status=models.Event.BOOKED)
|
||||||
|
|
||||||
|
filter = filter & typeFilters & statusFilters
|
||||||
|
|
||||||
return models.Event.objects.filter(filter).order_by('-start_date').select_related('person', 'organisation', 'venue', 'mic')
|
return models.Event.objects.filter(filter).order_by('-start_date').select_related('person', 'organisation', 'venue', 'mic')
|
||||||
|
|
||||||
@@ -51,38 +94,10 @@ class CalendarICS(ICalFeed):
|
|||||||
return title
|
return title
|
||||||
|
|
||||||
def item_start_datetime(self, item):
|
def item_start_datetime(self, item):
|
||||||
#set start date to the earliest defined time for the event
|
return item.earliest_time
|
||||||
if item.meet_at:
|
|
||||||
startDateTime = item.meet_at
|
|
||||||
elif item.access_at:
|
|
||||||
startDateTime = item.access_at
|
|
||||||
elif item.has_start_time:
|
|
||||||
startDateTime = datetime.datetime.combine(item.start_date,item.start_time)
|
|
||||||
tz = pytz.timezone(settings.TIME_ZONE)
|
|
||||||
startDateTime = tz.normalize(tz.localize(startDateTime)).astimezone(pytz.timezone(self.timezone))
|
|
||||||
else:
|
|
||||||
startDateTime = item.start_date
|
|
||||||
|
|
||||||
return startDateTime
|
|
||||||
|
|
||||||
def item_end_datetime(self, item):
|
def item_end_datetime(self, item):
|
||||||
# Assume end is same as start
|
return item.latest_time
|
||||||
endDateTime = item.start_date
|
|
||||||
|
|
||||||
# If end date defined then use it
|
|
||||||
if item.end_date:
|
|
||||||
endDateTime = item.end_date
|
|
||||||
|
|
||||||
if item.has_start_time and item.has_end_time: # don't allow an event with specific end but no specific start
|
|
||||||
endDateTime = datetime.datetime.combine(endDateTime,item.end_time)
|
|
||||||
tz = pytz.timezone(settings.TIME_ZONE)
|
|
||||||
endDateTime = tz.normalize(tz.localize(endDateTime)).astimezone(pytz.timezone(self.timezone))
|
|
||||||
elif item.has_end_time: # if there's a start time specified then an end time should also be specified
|
|
||||||
endDateTime = datetime.datetime.combine(endDateTime+datetime.timedelta(days=1),datetime.time(00, 00))
|
|
||||||
#elif item.end_time: # end time but no start time - this is weird - don't think ICS will like it so ignoring
|
|
||||||
# do nothing
|
|
||||||
|
|
||||||
return endDateTime
|
|
||||||
|
|
||||||
def item_location(self,item):
|
def item_location(self,item):
|
||||||
return item.venue
|
return item.venue
|
||||||
@@ -91,6 +106,8 @@ class CalendarICS(ICalFeed):
|
|||||||
# Create a nice information-rich description
|
# Create a nice information-rich description
|
||||||
# note: only making use of information available to "non-keyholders"
|
# note: only making use of information available to "non-keyholders"
|
||||||
|
|
||||||
|
tz = pytz.timezone(self.timezone)
|
||||||
|
|
||||||
desc = 'Rig ID = '+str(item.pk)+'\n'
|
desc = 'Rig ID = '+str(item.pk)+'\n'
|
||||||
desc += 'Event = ' + item.name + '\n'
|
desc += 'Event = ' + item.name + '\n'
|
||||||
desc += 'Venue = ' + (item.venue.name if item.venue else '---') + '\n'
|
desc += 'Venue = ' + (item.venue.name if item.venue else '---') + '\n'
|
||||||
@@ -102,9 +119,9 @@ class CalendarICS(ICalFeed):
|
|||||||
|
|
||||||
desc += '\n'
|
desc += '\n'
|
||||||
if item.meet_at:
|
if item.meet_at:
|
||||||
desc += 'Crew Meet = ' + (item.meet_at.strftime('%Y-%m-%d %H:%M') if item.meet_at else '---') + '\n'
|
desc += 'Crew Meet = ' + (item.meet_at.astimezone(tz).strftime('%Y-%m-%d %H:%M') if item.meet_at else '---') + '\n'
|
||||||
if item.access_at:
|
if item.access_at:
|
||||||
desc += 'Access At = ' + item.access_at.strftime('%Y-%m-%d %H:%M') + '\n'
|
desc += 'Access At = ' + (item.access_at.astimezone(tz).strftime('%Y-%m-%d %H:%M') if item.access_at else '---') + '\n'
|
||||||
if item.start_date:
|
if item.start_date:
|
||||||
desc += 'Event Start = ' + item.start_date.strftime('%Y-%m-%d') + ((' '+item.start_time.strftime('%H:%M')) if item.has_start_time else '') + '\n'
|
desc += 'Event Start = ' + item.start_date.strftime('%Y-%m-%d') + ((' '+item.start_time.strftime('%H:%M')) if item.has_start_time else '') + '\n'
|
||||||
if item.end_date:
|
if item.end_date:
|
||||||
@@ -113,8 +130,8 @@ class CalendarICS(ICalFeed):
|
|||||||
desc += '\n'
|
desc += '\n'
|
||||||
if item.description:
|
if item.description:
|
||||||
desc += 'Event Description:\n'+item.description+'\n\n'
|
desc += 'Event Description:\n'+item.description+'\n\n'
|
||||||
if item.notes:
|
# if item.notes: // Need to add proper keyholder checks before this gets put back
|
||||||
desc += 'Notes:\n'+item.notes+'\n\n'
|
# desc += 'Notes:\n'+item.notes+'\n\n'
|
||||||
|
|
||||||
base_url = "http://rigs.nottinghamtec.co.uk"
|
base_url = "http://rigs.nottinghamtec.co.uk"
|
||||||
desc += 'URL = '+base_url+str(item.get_absolute_url())
|
desc += 'URL = '+base_url+str(item.get_absolute_url())
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import datetime
|
import datetime, pytz
|
||||||
|
|
||||||
from django.db import models, connection
|
from django.db import models, connection
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
@@ -239,7 +239,14 @@ class EventManager(models.Manager):
|
|||||||
(models.Q(start_date__gte=start.date(), start_date__lte=end.date())) | # Start date in bounds
|
(models.Q(start_date__gte=start.date(), start_date__lte=end.date())) | # Start date in bounds
|
||||||
(models.Q(end_date__gte=start.date(), end_date__lte=end.date())) | # End date in bounds
|
(models.Q(end_date__gte=start.date(), end_date__lte=end.date())) | # End date in bounds
|
||||||
(models.Q(access_at__gte=start, access_at__lte=end)) | # Access at in bounds
|
(models.Q(access_at__gte=start, access_at__lte=end)) | # Access at in bounds
|
||||||
(models.Q(meet_at__gte=start, meet_at__lte=end)) # Meet at in bounds
|
(models.Q(meet_at__gte=start, meet_at__lte=end)) | # Meet at in bounds
|
||||||
|
|
||||||
|
(models.Q(start_date__lte=start, end_date__gte=end)) | # Start before, end after
|
||||||
|
(models.Q(access_at__lte=start, start_date__gte=end)) | # Access before, start after
|
||||||
|
(models.Q(access_at__lte=start, end_date__gte=end)) | # Access before, end after
|
||||||
|
(models.Q(meet_at__lte=start, start_date__gte=end)) | # Meet before, start after
|
||||||
|
(models.Q(meet_at__lte=start, end_date__gte=end)) # Meet before, end after
|
||||||
|
|
||||||
).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person', 'organisation', 'venue', 'mic')
|
).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person', 'organisation', 'venue', 'mic')
|
||||||
return events
|
return events
|
||||||
|
|
||||||
@@ -359,6 +366,59 @@ class Event(models.Model, RevisionMixin):
|
|||||||
def has_end_time(self):
|
def has_end_time(self):
|
||||||
return self.end_time is not None
|
return self.end_time is not None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def earliest_time(self):
|
||||||
|
"""Finds the earliest time defined in the event - this function could return either a tzaware datetime, or a naiive date object"""
|
||||||
|
|
||||||
|
#Put all the datetimes in a list
|
||||||
|
datetime_list = []
|
||||||
|
|
||||||
|
if self.access_at:
|
||||||
|
datetime_list.append(self.access_at)
|
||||||
|
|
||||||
|
if self.meet_at:
|
||||||
|
datetime_list.append(self.meet_at)
|
||||||
|
|
||||||
|
# If there is no start time defined, pretend it's midnight
|
||||||
|
startTimeFaked = False
|
||||||
|
if self.has_start_time:
|
||||||
|
startDateTime = datetime.datetime.combine(self.start_date,self.start_time)
|
||||||
|
else:
|
||||||
|
startDateTime = datetime.datetime.combine(self.start_date,datetime.time(00,00))
|
||||||
|
startTimeFaked = True
|
||||||
|
|
||||||
|
#timezoneIssues - apply the default timezone to the naiive datetime
|
||||||
|
tz = pytz.timezone(settings.TIME_ZONE)
|
||||||
|
startDateTime = tz.localize(startDateTime)
|
||||||
|
datetime_list.append(startDateTime) # then add it to the list
|
||||||
|
|
||||||
|
earliest = min(datetime_list).astimezone(tz) #find the earliest datetime in the list
|
||||||
|
|
||||||
|
# if we faked it & it's the earliest, better own up
|
||||||
|
if startTimeFaked and earliest==startDateTime:
|
||||||
|
return self.start_date
|
||||||
|
|
||||||
|
return earliest
|
||||||
|
|
||||||
|
@property
|
||||||
|
def latest_time(self):
|
||||||
|
"""Returns the end of the event - this function could return either a tzaware datetime, or a naiive date object"""
|
||||||
|
tz = pytz.timezone(settings.TIME_ZONE)
|
||||||
|
endDate = self.end_date
|
||||||
|
if endDate is None:
|
||||||
|
endDate = self.start_date
|
||||||
|
|
||||||
|
if self.has_end_time:
|
||||||
|
endDateTime = datetime.datetime.combine(endDate,self.end_time)
|
||||||
|
tz = pytz.timezone(settings.TIME_ZONE)
|
||||||
|
endDateTime = tz.localize(endDateTime)
|
||||||
|
|
||||||
|
return endDateTime
|
||||||
|
|
||||||
|
else:
|
||||||
|
return endDate
|
||||||
|
|
||||||
|
|
||||||
objects = EventManager()
|
objects = EventManager()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
$('#calendar').fullCalendar({
|
$('#calendar').fullCalendar({
|
||||||
//defaultDate: '2015-02-12',
|
|
||||||
editable: false,
|
editable: false,
|
||||||
eventLimit: true, // allow "more" link when too many events
|
eventLimit: true, // allow "more" link when too many events
|
||||||
firstDay: 1,
|
firstDay: 1,
|
||||||
@@ -37,75 +36,156 @@
|
|||||||
// options apply to basicDay and agendaDay views
|
// options apply to basicDay and agendaDay views
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
buttonText:{
|
header:false,
|
||||||
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) {
|
events: function(start_moment, end_moment, timezone, callback) {
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/api/event',
|
url: '/api/event',
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
data: {
|
data: {
|
||||||
start: moment(start_moment).format("YYYY-MM-DD[T]HH:mm:ss[Z]"),
|
start: moment(start_moment).format("YYYY-MM-DD[T]HH:mm:ss"),
|
||||||
end: moment(end_moment).format("YYYY-MM-DD[T]HH:mm:ss[Z]")
|
end: moment(end_moment).format("YYYY-MM-DD[T]HH:mm:ss")
|
||||||
},
|
},
|
||||||
success: function(doc) {
|
success: function(doc) {
|
||||||
var events = [];
|
var events = [];
|
||||||
var colours =
|
colours = {'Provisional': '#f0ad4e',
|
||||||
|
'Confirmed': '#5cb85c' ,
|
||||||
|
'Booked': '#5cb85c' ,
|
||||||
|
'Cancelled': 'grey' ,
|
||||||
|
'non-rig': '#5bc0de'
|
||||||
|
};
|
||||||
$(doc).each(function() {
|
$(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');
|
thisEvent = {
|
||||||
|
'start': $(this).attr('earliest'),
|
||||||
|
'end': $(this).attr('latest'),
|
||||||
|
'className': 'modal-href',
|
||||||
|
'title': $(this).attr('title'),
|
||||||
|
'url': $(this).attr('url')
|
||||||
|
}
|
||||||
|
|
||||||
|
if($(this).attr('is_rig')==true || $(this).attr('status') == "Cancelled"){
|
||||||
|
thisEvent['color'] = colours[$(this).attr('status')];
|
||||||
|
}else{
|
||||||
|
thisEvent['color'] = colours['non-rig'];
|
||||||
|
}
|
||||||
|
|
||||||
events.push(thisEvent);
|
events.push(thisEvent);
|
||||||
});
|
});
|
||||||
callback(events);
|
callback(events);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
viewRender: function(view, element){
|
||||||
|
// Set the title of the view
|
||||||
|
$('#calendar-header').text(view.title);
|
||||||
|
|
||||||
|
// Enable/Disable "Today" button as required
|
||||||
|
if(moment().isBetween(view.intervalStart, view.intervalEnd)){
|
||||||
|
//Today is within the current view
|
||||||
|
$('#today-button').prop('disabled', true);
|
||||||
|
}else{
|
||||||
|
$('#today-button').prop('disabled', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set active view select button
|
||||||
|
switch(view.name){
|
||||||
|
case 'month':
|
||||||
|
$('#month-button').addClass('active');
|
||||||
|
$('#week-button').removeClass('active');
|
||||||
|
$('#day-button').removeClass('active');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'agendaWeek':
|
||||||
|
$('#month-button').removeClass('active');
|
||||||
|
$('#week-button').addClass('active');
|
||||||
|
$('#day-button').removeClass('active');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'agendaDay':
|
||||||
|
$('#month-button').removeClass('active');
|
||||||
|
$('#week-button').removeClass('active');
|
||||||
|
$('#day-button').addClass('active');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// set some button listeners
|
||||||
|
|
||||||
|
$('#next-button').click(function(){ $('#calendar').fullCalendar('next') });
|
||||||
|
$('#prev-button').click(function(){ $('#calendar').fullCalendar('prev') });
|
||||||
|
$('#today-button').click(function(){ $('#calendar').fullCalendar('today') });
|
||||||
|
|
||||||
|
$('#month-button').click(function(){ $('#calendar').fullCalendar('changeView','month') });
|
||||||
|
$('#week-button').click(function(){ $('#calendar').fullCalendar('changeView','agendaWeek') });
|
||||||
|
$('#day-button').click(function(){ $('#calendar').fullCalendar('changeView','agendaDay') });
|
||||||
|
|
||||||
|
$('#go-to-date-input').change(function(){
|
||||||
|
if( moment($('#go-to-date-input').val()).isValid() ){
|
||||||
|
$('#go-to-date-button').prop('disabled', false);
|
||||||
|
}else{
|
||||||
|
$('#go-to-date-button').prop('disabled', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#go-to-date-button').click(function(){
|
||||||
|
day = moment($('#go-to-date-input').val()) ;
|
||||||
|
if(day.isValid()){
|
||||||
|
$('#calendar').fullCalendar( 'gotoDate', day);
|
||||||
|
}else{
|
||||||
|
alert('Invalid Date');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="col-sm-12">
|
|
||||||
<div id='calendar'>
|
<div class="col-sm-12">
|
||||||
|
<div class="pull-left">
|
||||||
|
<span id="calendar-header" class="h2"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-inline pull-right btn-page">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="date" class="form-control" id="go-to-date-input" placeholder="Go to date...">
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="btn btn-default" id="go-to-date-button" type="button" disabled>Go!</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn btn-primary" id="today-button">Today</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn btn-default" id="prev-button"><span class="glyphicon glyphicon-chevron-left"></span></button>
|
||||||
|
<button type="button" class="btn btn-default" id="next-button"><span class="glyphicon glyphicon-chevron-right"></span></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn btn-default" id="month-button">Month</button>
|
||||||
|
<button type="button" class="btn btn-default" id="week-button">Week</button>
|
||||||
|
<button type="button" class="btn btn-default" id="day-button">Day</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div id='calendar'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -2,6 +2,33 @@
|
|||||||
|
|
||||||
{% block title %}RIGS Profile {{object.pk}}{% endblock %}
|
{% block title %}RIGS Profile {{object.pk}}{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#urlParamForm').change(function(){
|
||||||
|
url = "?";
|
||||||
|
$('#urlParamForm *').filter(':input').each(function(index, value){
|
||||||
|
defaultVal = $(value).data('default');
|
||||||
|
param = $(value).val();
|
||||||
|
val = $(value).prop('checked');
|
||||||
|
|
||||||
|
if(val != defaultVal){
|
||||||
|
url = url+param+"="+val+"&";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ics_url = $('#cal-url').data('url') + url.substring(0, url.length - 1);
|
||||||
|
$('#cal-url').text(ics_url);
|
||||||
|
|
||||||
|
gcal_url = $('#gcal-link').data('url') + encodeURIComponent(url.substring(0, url.length - 1));
|
||||||
|
$('#gcal-link').attr('href',gcal_url);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#urlParamForm').change(); //Do the initial setting
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-10 col-md-offset-1">
|
<div class="col-md-10 col-md-offset-1">
|
||||||
@@ -68,16 +95,42 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
|
<dt>Calendar Options</dt>
|
||||||
|
<dd>
|
||||||
|
<div class="well well-sm text-center">
|
||||||
|
<form class="form-inline" id="urlParamForm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="checkbox-inline">
|
||||||
|
<input type="checkbox" value="rig" data-default="true" checked> Rigs
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-inline">
|
||||||
|
<input type="checkbox" value="non-rig" data-default="true" checked> Non-Rigs
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-inline">
|
||||||
|
<input type="checkbox" value="dry-hire" data-default="true" checked> Dry-Hires
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-inline">
|
||||||
|
<input type="checkbox" value="cancelled" data-default="false" > Cancelled
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-inline">
|
||||||
|
<input type="checkbox" value="provisional" data-default="true" checked> Provisional
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-inline">
|
||||||
|
<input type="checkbox" value="confirmed" data-default="true" checked> Confirmed/Booked
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
<dt>Calendar URL</dt>
|
<dt>Calendar URL</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{% if user.api_key %}
|
{% if user.api_key %}
|
||||||
<pre>http{{ request.is_secure|yesno:"s,"}}://{{ request.get_host }}{% url 'ics_calendar' api_pk=user.pk api_key=user.api_key %}</pre>
|
<pre id="cal-url" data-url="http{{ request.is_secure|yesno:"s,"}}://{{ request.get_host }}{% url 'ics_calendar' api_pk=user.pk api_key=user.api_key %}"></pre>
|
||||||
<small><a href="http://www.google.com/calendar/render?cid=http{{ request.is_secure|yesno:"s,"}}://{{ request.get_host }}{% url 'ics_calendar' api_pk=user.pk api_key=user.api_key %}">Click here</a> to add to google calendar.<br/>
|
<small><a id="gcal-link" data-url="http://www.google.com/calendar/render?cid=http{{ request.is_secure|yesno:"s,"}}://{{ request.get_host }}{% url 'ics_calendar' api_pk=user.pk api_key=user.api_key %}" href="">Click here</a> to add to google calendar.<br/>
|
||||||
To sync from google calendar to mobile device, visit <a href="https://www.google.com/calendar/syncselect" target="_blank">this page</a> on your device and tick "RIGS Calendar".</small>
|
To sync from google calendar to mobile device, visit <a href="https://www.google.com/calendar/syncselect" target="_blank">this page</a> on your device and tick "RIGS Calendar".</small>
|
||||||
{% else %}
|
{% else %}
|
||||||
<pre>No API Key Generated</pre>
|
<pre>No API Key Generated</pre>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from django.test import LiveServerTestCase
|
from django.test import LiveServerTestCase
|
||||||
|
from django.test.client import Client
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from selenium import webdriver
|
from selenium import webdriver
|
||||||
from selenium.webdriver.common.keys import Keys
|
from selenium.webdriver.common.keys import Keys
|
||||||
@@ -8,6 +9,7 @@ from selenium.webdriver.support.ui import WebDriverWait
|
|||||||
from RIGS import models
|
from RIGS import models
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
|
from datetime import date, timedelta
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
import reversion
|
import reversion
|
||||||
import json
|
import json
|
||||||
@@ -576,6 +578,195 @@ class EventTest(LiveServerTestCase):
|
|||||||
|
|
||||||
organisationPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Contact Details")]/..')
|
organisationPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Contact Details")]/..')
|
||||||
|
|
||||||
|
class IcalTest(LiveServerTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.all_events = set(range(1, 18))
|
||||||
|
self.current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18)
|
||||||
|
self.not_current_events = set(self.all_events) - set(self.current_events)
|
||||||
|
|
||||||
|
self.vatrate = models.VatRate.objects.create(start_at='2014-03-05',rate=0.20,comment='test1')
|
||||||
|
self.profile = models.Profile(
|
||||||
|
username="EventTest", first_name="Event", last_name="Test", initials="ETU", is_superuser=True)
|
||||||
|
self.profile.set_password("EventTestPassword")
|
||||||
|
self.profile.save()
|
||||||
|
|
||||||
|
# produce 7 normal events - 5 current - 1 last week - 1 two years ago - 2 provisional - 2 confirmed - 3 booked
|
||||||
|
models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL, start_date=date.today() + timedelta(days=6), description="start future no end")
|
||||||
|
models.Event.objects.create(name="TE E2", status=models.Event.PROVISIONAL, start_date=date.today(), description="start today no end")
|
||||||
|
models.Event.objects.create(name="TE E3", status=models.Event.CONFIRMED, start_date=date.today(), end_date=date.today(), description="start today with end today")
|
||||||
|
models.Event.objects.create(name="TE E4", status=models.Event.CONFIRMED, start_date=date.today()-timedelta(weeks=104), description="start past 2 years no end")
|
||||||
|
models.Event.objects.create(name="TE E5", status=models.Event.BOOKED, start_date=date.today()-timedelta(days=7), end_date=date.today()-timedelta(days=1), description="start past 1 week with end past")
|
||||||
|
models.Event.objects.create(name="TE E6", status=models.Event.BOOKED, start_date=date.today()-timedelta(days=2), end_date=date.today()+timedelta(days=2), description="start past, end future")
|
||||||
|
models.Event.objects.create(name="TE E7", status=models.Event.BOOKED, start_date=date.today()+timedelta(days=2), end_date=date.today()+timedelta(days=2), description="start + end in future")
|
||||||
|
|
||||||
|
# 2 cancelled - 1 current
|
||||||
|
models.Event.objects.create(name="TE E8", start_date=date.today()+timedelta(days=2), end_date=date.today()+timedelta(days=2), status=models.Event.CANCELLED, description="cancelled in future")
|
||||||
|
models.Event.objects.create(name="TE E9", start_date=date.today()-timedelta(days=1), end_date=date.today()+timedelta(days=2), status=models.Event.CANCELLED, description="cancelled and started")
|
||||||
|
|
||||||
|
# 5 dry hire - 3 current - 1 cancelled
|
||||||
|
models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True, description="dryhire today")
|
||||||
|
models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True, checked_in_by=self.profile, description="dryhire today, checked in")
|
||||||
|
models.Event.objects.create(name="TE E12", start_date=date.today()-timedelta(days=1), dry_hire=True, status=models.Event.BOOKED, description="dryhire past")
|
||||||
|
models.Event.objects.create(name="TE E13", start_date=date.today()-timedelta(days=2), dry_hire=True, checked_in_by=self.profile, description="dryhire past checked in")
|
||||||
|
models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True, status=models.Event.CANCELLED, description="dryhire today cancelled")
|
||||||
|
|
||||||
|
# 4 non rig - 3 current
|
||||||
|
models.Event.objects.create(name="TE E15", start_date=date.today(), is_rig=False, description="non rig today")
|
||||||
|
models.Event.objects.create(name="TE E16", start_date=date.today()+timedelta(days=1), is_rig=False, description="non rig tomorrow")
|
||||||
|
models.Event.objects.create(name="TE E17", start_date=date.today()-timedelta(days=1), is_rig=False, description="non rig yesterday")
|
||||||
|
models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False, status=models.Event.CANCELLED, description="non rig today cancelled")
|
||||||
|
|
||||||
|
self.browser = webdriver.Firefox()
|
||||||
|
os.environ['RECAPTCHA_TESTING'] = 'True'
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.browser.quit()
|
||||||
|
os.environ['RECAPTCHA_TESTING'] = 'False'
|
||||||
|
|
||||||
|
def authenticate(self, n=None):
|
||||||
|
self.assertIn(
|
||||||
|
self.live_server_url + '/user/login/', self.browser.current_url)
|
||||||
|
if n:
|
||||||
|
self.assertIn('?next=%s' % n, self.browser.current_url)
|
||||||
|
username = self.browser.find_element_by_id('id_username')
|
||||||
|
password = self.browser.find_element_by_id('id_password')
|
||||||
|
submit = self.browser.find_element_by_css_selector(
|
||||||
|
'input[type=submit]')
|
||||||
|
|
||||||
|
username.send_keys("EventTest")
|
||||||
|
password.send_keys("EventTestPassword")
|
||||||
|
self.browser.execute_script(
|
||||||
|
"return jQuery('#g-recaptcha-response').val('PASSED')")
|
||||||
|
submit.click()
|
||||||
|
|
||||||
|
self.assertEqual(self.live_server_url + n, self.browser.current_url)
|
||||||
|
|
||||||
|
def testApiKeyGeneration(self):
|
||||||
|
# Requests address
|
||||||
|
self.browser.get(self.live_server_url + '/user/')
|
||||||
|
# Gets redirected to login
|
||||||
|
self.authenticate('/user/')
|
||||||
|
|
||||||
|
# Completes and comes back to /user/
|
||||||
|
# Checks that no api key is displayed
|
||||||
|
self.assertEqual("No API Key Generated", self.browser.find_element_by_xpath("//div[@id='content']/div/div/div[3]/dl[2]/dd").text)
|
||||||
|
self.assertEqual("No API Key Generated", self.browser.find_element_by_css_selector("pre").text)
|
||||||
|
|
||||||
|
# Now creates an API key, and check a URL is displayed one
|
||||||
|
self.browser.find_element_by_link_text("Generate API Key").click()
|
||||||
|
self.assertIn("rigs.ics", self.browser.find_element_by_id("cal-url").text)
|
||||||
|
self.assertNotIn("?", self.browser.find_element_by_id("cal-url").text)
|
||||||
|
|
||||||
|
# Lets change everything so it's not the default value
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='rig']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='non-rig']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='dry-hire']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='cancelled']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='provisional']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='confirmed']").click()
|
||||||
|
|
||||||
|
# and then check the url is correct
|
||||||
|
self.assertIn("rigs.ics?rig=false&non-rig=false&dry-hire=false&cancelled=true&provisional=false&confirmed=false", self.browser.find_element_by_id("cal-url").text)
|
||||||
|
|
||||||
|
# Awesome - all seems to work
|
||||||
|
|
||||||
|
def testICSFiles(self):
|
||||||
|
# Requests address
|
||||||
|
self.browser.get(self.live_server_url + '/user/')
|
||||||
|
# Gets redirected to login
|
||||||
|
self.authenticate('/user/')
|
||||||
|
|
||||||
|
# Now creates an API key, and check a URL is displayed one
|
||||||
|
self.browser.find_element_by_link_text("Generate API Key").click()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
c = Client()
|
||||||
|
|
||||||
|
# Default settings - should have all non-cancelled events
|
||||||
|
# Get the ical file (can't do this in selanium because reasons)
|
||||||
|
icalUrl = self.browser.find_element_by_id("cal-url").text
|
||||||
|
response = c.get(icalUrl)
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
|
#Check has entire file
|
||||||
|
self.assertIn("BEGIN:VCALENDAR", response.content)
|
||||||
|
self.assertIn("END:VCALENDAR", response.content)
|
||||||
|
|
||||||
|
expectedIn= [1,2,3,5,6,7,10,11,12,13,15,16,17]
|
||||||
|
for test in range(1,18):
|
||||||
|
if test in expectedIn:
|
||||||
|
self.assertIn("TE E"+str(test)+" ", response.content)
|
||||||
|
else:
|
||||||
|
self.assertNotIn("TE E"+str(test)+" ", response.content)
|
||||||
|
|
||||||
|
|
||||||
|
# Only dry hires
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='rig']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='non-rig']").click()
|
||||||
|
|
||||||
|
icalUrl = self.browser.find_element_by_id("cal-url").text
|
||||||
|
response = c.get(icalUrl)
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
|
expectedIn= [10,11,12,13]
|
||||||
|
for test in range(1,18):
|
||||||
|
if test in expectedIn:
|
||||||
|
self.assertIn("TE E"+str(test)+" ", response.content)
|
||||||
|
else:
|
||||||
|
self.assertNotIn("TE E"+str(test)+" ", response.content)
|
||||||
|
|
||||||
|
|
||||||
|
# Only provisional rigs
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='rig']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='dry-hire']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='confirmed']").click()
|
||||||
|
|
||||||
|
icalUrl = self.browser.find_element_by_id("cal-url").text
|
||||||
|
response = c.get(icalUrl)
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
|
expectedIn= [1,2]
|
||||||
|
for test in range(1,18):
|
||||||
|
if test in expectedIn:
|
||||||
|
self.assertIn("TE E"+str(test)+" ", response.content)
|
||||||
|
else:
|
||||||
|
self.assertNotIn("TE E"+str(test)+" ", response.content)
|
||||||
|
|
||||||
|
# Only cancelled non-rigs
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='rig']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='non-rig']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='provisional']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='cancelled']").click()
|
||||||
|
|
||||||
|
icalUrl = self.browser.find_element_by_id("cal-url").text
|
||||||
|
response = c.get(icalUrl)
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
|
expectedIn= [18]
|
||||||
|
for test in range(1,18):
|
||||||
|
if test in expectedIn:
|
||||||
|
self.assertIn("TE E"+str(test)+" ", response.content)
|
||||||
|
else:
|
||||||
|
self.assertNotIn("TE E"+str(test)+" ", response.content)
|
||||||
|
|
||||||
|
# Nothing selected
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='non-rig']").click()
|
||||||
|
self.browser.find_element_by_xpath("//input[@value='cancelled']").click()
|
||||||
|
|
||||||
|
icalUrl = self.browser.find_element_by_id("cal-url").text
|
||||||
|
response = c.get(icalUrl)
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
|
expectedIn= []
|
||||||
|
for test in range(1,18):
|
||||||
|
if test in expectedIn:
|
||||||
|
self.assertIn("TE E"+str(test)+" ", response.content)
|
||||||
|
else:
|
||||||
|
self.assertNotIn("TE E"+str(test)+" ", response.content)
|
||||||
|
|
||||||
|
# Wow - that was a lot of tests
|
||||||
|
|
||||||
class animation_is_finished(object):
|
class animation_is_finished(object):
|
||||||
""" Checks if animation is done """
|
""" Checks if animation is done """
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ from django.views import generic
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
|
from django.conf import settings
|
||||||
import simplejson
|
import simplejson
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
import datetime
|
import datetime, pytz
|
||||||
import operator
|
import operator
|
||||||
from registration.views import RegistrationView
|
from registration.views import RegistrationView
|
||||||
|
|
||||||
@@ -298,39 +299,22 @@ class SecureAPIRequest(generic.View):
|
|||||||
|
|
||||||
if model == "event" and start and end:
|
if model == "event" and start and end:
|
||||||
# Probably a calendar request
|
# Probably a calendar request
|
||||||
start_datetime = datetime.datetime.strptime( start, "%Y-%m-%dT%H:%M:%SZ" )
|
start_datetime = datetime.datetime.strptime( start, "%Y-%m-%dT%H:%M:%S" )
|
||||||
end_datetime = datetime.datetime.strptime( end, "%Y-%m-%dT%H:%M:%SZ" )
|
end_datetime = datetime.datetime.strptime( end, "%Y-%m-%dT%H:%M:%S" )
|
||||||
|
|
||||||
objects = self.models[model].objects.events_in_bounds(start_datetime,end_datetime)
|
objects = self.models[model].objects.events_in_bounds(start_datetime,end_datetime)
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
for item in objects:
|
for item in objects:
|
||||||
data = {
|
data = {
|
||||||
'pk': item.pk,
|
'pk': item.pk,
|
||||||
'title': item.name
|
'title': item.name,
|
||||||
|
'is_rig': item.is_rig,
|
||||||
|
'status': str(item.get_status_display()),
|
||||||
|
'earliest': item.earliest_time.isoformat(),
|
||||||
|
'latest': item.latest_time.isoformat(),
|
||||||
|
'url': str(item.get_absolute_url())
|
||||||
}
|
}
|
||||||
|
|
||||||
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.has_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.has_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')
|
|
||||||
|
|
||||||
data['url'] = str(reverse_lazy('event_detail',kwargs={'pk':item.pk}))
|
|
||||||
|
|
||||||
results.append(data)
|
results.append(data)
|
||||||
json = simplejson.dumps(results)
|
json = simplejson.dumps(results)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ PyPDF2==1.24
|
|||||||
python-dateutil==2.4.2
|
python-dateutil==2.4.2
|
||||||
pytz==2015.4
|
pytz==2015.4
|
||||||
reportlab==3.1.44
|
reportlab==3.1.44
|
||||||
selenium==2.45.0
|
selenium==2.46.0
|
||||||
simplejson==3.7.2
|
simplejson==3.7.2
|
||||||
six==1.9.0
|
six==1.9.0
|
||||||
sqlparse==0.1.15
|
sqlparse==0.1.15
|
||||||
|
|||||||
Reference in New Issue
Block a user