mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-03-07 12:38:23 +00:00
Compare commits
68 Commits
training_l
...
web-calend
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44ce14f600 | ||
|
|
e286d8bdee | ||
|
|
1faf8f95c8 | ||
|
|
2913b254b4 | ||
|
|
ef81536066 | ||
|
|
29aa13316d | ||
|
|
98f28aaafd | ||
|
|
f8a2a7a959 | ||
|
|
767b5512e3 | ||
|
|
f1bd1ca674 | ||
|
|
b47cfed5a9 | ||
|
|
71b69aa0f6 | ||
|
|
df61225b73 | ||
|
|
823db68a6a | ||
|
|
ebe08c3bf1 | ||
|
|
44ccead0a4 | ||
|
|
99dfdcd253 | ||
|
|
03ca65602f | ||
|
|
ca6cddb392 | ||
|
|
33ce4b622d | ||
|
|
46434977fb | ||
|
|
f13303490a | ||
|
|
84b0a57e14 | ||
|
|
2ee7c064af | ||
|
|
b2b8546e3c | ||
|
|
2945a607bf | ||
|
|
4370cf60d1 | ||
|
|
e7496a9d82 | ||
|
|
77f7a25fa8 | ||
|
|
10c5ea3e7c | ||
|
|
cd82712742 | ||
|
|
3067dcdda5 | ||
|
|
94679d6783 | ||
|
|
54dc29b4b2 | ||
|
|
e699826ce9 | ||
|
|
33fa19c15e | ||
|
|
d6e9b030fd | ||
|
|
f0a3c968a7 | ||
|
|
d8760a00ba | ||
|
|
8ee43ef3ab | ||
|
|
1b6ce32deb | ||
|
|
9964d33cc0 | ||
|
|
1e81b718f6 | ||
|
|
551737b882 | ||
|
|
77dd36d4b4 | ||
|
|
9bace9a6da | ||
|
|
c6b45da72c | ||
|
|
0721d61bef | ||
|
|
56bc084e60 | ||
|
|
481f56a4bd | ||
|
|
11e9931438 | ||
|
|
ea840eb43c | ||
|
|
2ed0a4bcf9 | ||
|
|
5d48d75f34 | ||
|
|
92beb8bf79 | ||
|
|
ed552b402a | ||
|
|
8279bec4bf | ||
|
|
67b2bc6d54 | ||
|
|
e3adfecd17 | ||
|
|
1681ab8fee | ||
|
|
a77bc65d7b | ||
|
|
73517ed443 | ||
|
|
f59e11ecc4 | ||
|
|
19f619b2d4 | ||
|
|
e0d03c2cc3 | ||
|
|
69f7152dd6 | ||
|
|
581f28757a | ||
|
|
660a54d955 |
@@ -1,5 +1,5 @@
|
||||
# TEC PA & Lighting - PyRIGS #
|
||||
[](https://app.wercker.com/project/bykey/b26100ecccdfb46a9a9056553daac5b7)
|
||||
[](https://app.wercker.com/project/bykey/2dbe0517c3d83859c985ffc5a55a2802)
|
||||
|
||||
Welcome to TEC PA & Lightings PyRIGS program. This is a reimplementation of the existing Rig Information Gathering System (RIGS) that was developed using Ruby on Rails.
|
||||
|
||||
|
||||
@@ -4,16 +4,23 @@ from django.contrib.auth.admin import UserAdmin
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import reversion
|
||||
|
||||
from django.contrib.admin import helpers
|
||||
from django.template.response import TemplateResponse
|
||||
from django.contrib import messages
|
||||
from django.db import transaction
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.models import Count
|
||||
from django.forms import ModelForm
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(models.Person, reversion.VersionAdmin)
|
||||
admin.site.register(models.Organisation, reversion.VersionAdmin)
|
||||
admin.site.register(models.VatRate, reversion.VersionAdmin)
|
||||
admin.site.register(models.Venue, reversion.VersionAdmin)
|
||||
admin.site.register(models.Event, reversion.VersionAdmin)
|
||||
admin.site.register(models.EventItem, reversion.VersionAdmin)
|
||||
admin.site.register(models.Invoice)
|
||||
admin.site.register(models.Payment)
|
||||
|
||||
|
||||
@admin.register(models.Profile)
|
||||
class ProfileAdmin(UserAdmin):
|
||||
fieldsets = (
|
||||
(None, {'fields': ('username', 'password')}),
|
||||
@@ -33,4 +40,76 @@ class ProfileAdmin(UserAdmin):
|
||||
form = forms.ProfileChangeForm
|
||||
add_form = forms.ProfileCreationForm
|
||||
|
||||
admin.site.register(models.Profile, ProfileAdmin)
|
||||
|
||||
class AssociateAdmin(reversion.VersionAdmin):
|
||||
list_display = ('id', 'name', 'number_of_events')
|
||||
search_fields = ['id', 'name']
|
||||
list_display_links = ['id', 'name']
|
||||
actions = ['merge']
|
||||
|
||||
merge_fields = ['name']
|
||||
|
||||
def get_queryset(self, request):
|
||||
return super(AssociateAdmin, self).get_queryset(request).annotate(event_count=Count('event'))
|
||||
|
||||
def number_of_events(self, obj):
|
||||
return obj.latest_events.count()
|
||||
|
||||
number_of_events.admin_order_field = 'event_count'
|
||||
|
||||
def merge(self, request, queryset):
|
||||
if request.POST.get('post'): # Has the user confirmed which is the master record?
|
||||
try:
|
||||
masterObjectPk = request.POST.get('master')
|
||||
masterObject = queryset.get(pk=masterObjectPk)
|
||||
except ObjectDoesNotExist:
|
||||
self.message_user(request, "An error occured. Did you select a 'master' record?", level=messages.ERROR)
|
||||
return
|
||||
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
for obj in queryset.exclude(pk=masterObjectPk):
|
||||
events = obj.event_set.all()
|
||||
for event in events:
|
||||
masterObject.event_set.add(event)
|
||||
obj.delete()
|
||||
reversion.set_comment('Merging Objects')
|
||||
|
||||
self.message_user(request, "Objects successfully merged.")
|
||||
return
|
||||
else: # Present the confirmation screen
|
||||
|
||||
class TempForm(ModelForm):
|
||||
class Meta:
|
||||
model = queryset.model
|
||||
fields = self.merge_fields
|
||||
|
||||
forms = []
|
||||
for obj in queryset:
|
||||
forms.append(TempForm(instance=obj))
|
||||
|
||||
context = {
|
||||
'title': _("Are you sure?"),
|
||||
'queryset': queryset,
|
||||
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
|
||||
'forms': forms
|
||||
}
|
||||
return TemplateResponse(request, 'RIGS/admin_associate_merge.html', context,
|
||||
current_app=self.admin_site.name)
|
||||
|
||||
|
||||
@admin.register(models.Person)
|
||||
class PersonAdmin(AssociateAdmin):
|
||||
list_display = ('id', 'name', 'phone', 'email', 'number_of_events')
|
||||
merge_fields = ['name', 'phone', 'email', 'address', 'notes']
|
||||
|
||||
|
||||
@admin.register(models.Venue)
|
||||
class VenueAdmin(AssociateAdmin):
|
||||
list_display = ('id', 'name', 'phone', 'email', 'number_of_events')
|
||||
merge_fields = ['name', 'phone', 'email', 'address', 'notes', 'three_phase_available']
|
||||
|
||||
|
||||
@admin.register(models.Organisation)
|
||||
class OrganisationAdmin(AssociateAdmin):
|
||||
list_display = ('id', 'name', 'phone', 'email', 'number_of_events')
|
||||
merge_fields = ['name', 'phone', 'email', 'address', 'notes', 'union_account']
|
||||
|
||||
20
RIGS/migrations/0024_auto_20160229_2042.py
Normal file
20
RIGS/migrations/0024_auto_20160229_2042.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0023_auto_20150529_0048'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='based_on',
|
||||
field=models.ForeignKey(related_name='future_events', on_delete=django.db.models.deletion.SET_NULL, blank=True, to='RIGS.Event', null=True),
|
||||
),
|
||||
]
|
||||
@@ -301,7 +301,7 @@ class Event(models.Model, RevisionMixin):
|
||||
status = models.IntegerField(choices=EVENT_STATUS_CHOICES, default=PROVISIONAL)
|
||||
dry_hire = models.BooleanField(default=False)
|
||||
is_rig = models.BooleanField(default=True)
|
||||
based_on = models.ForeignKey('Event', related_name='future_events', blank=True, null=True)
|
||||
based_on = models.ForeignKey('Event', on_delete=models.SET_NULL, related_name='future_events', blank=True, null=True)
|
||||
|
||||
# Timing
|
||||
start_date = models.DateField()
|
||||
|
||||
@@ -37,6 +37,12 @@ class RigboardIndex(generic.TemplateView):
|
||||
class WebCalendar(generic.TemplateView):
|
||||
template_name = 'RIGS/calendar.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(WebCalendar, self).get_context_data(**kwargs)
|
||||
context['view'] = kwargs.get('view','')
|
||||
context['date'] = kwargs.get('date','')
|
||||
return context
|
||||
|
||||
class EventDetail(generic.DetailView):
|
||||
model = models.Event
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ fonts_dir = "fonts"
|
||||
|
||||
# You can select your preferred output style here (can be overridden via the command line):
|
||||
# output_style = :expanded or :nested or :compact or :compressed
|
||||
output_style = :compressed
|
||||
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
# relative_assets = true
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -39,7 +39,7 @@ var Konami = function (callback) {
|
||||
return false;
|
||||
}
|
||||
}, this);
|
||||
this.iphone.load(link);
|
||||
/*this.iphone.load(link);*/
|
||||
},
|
||||
code: function (link) {
|
||||
window.location = link
|
||||
|
||||
@@ -75,6 +75,16 @@ textarea {
|
||||
}
|
||||
}
|
||||
|
||||
del {
|
||||
background-color: #f2dede;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
ins {
|
||||
background-color: #dff0d8;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.loading-animation {
|
||||
position: relative;
|
||||
margin: 30px auto 0;
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
{% endif %}
|
||||
|
||||
{% include 'RIGS/object_button.html' with object=version.new %}
|
||||
{% if version.revision.comment %}
|
||||
({{ version.revision.comment }})
|
||||
{% endif %}
|
||||
</small>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<td>Version ID</td>
|
||||
<td>User</td>
|
||||
<td>Changes</td>
|
||||
<td>Comment</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -71,10 +72,11 @@
|
||||
<td>{{ version.revision.user.name }}</td>
|
||||
<td>
|
||||
{% if version.old == None %}
|
||||
Object Created
|
||||
{{version.new|to_class_name}} Created
|
||||
{% else %}
|
||||
{% include 'RIGS/version_changes.html' %}
|
||||
{% endif %} </td>
|
||||
<td>{{ version.revision.comment }}</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
40
RIGS/templates/RIGS/admin_associate_merge.html
Normal file
40
RIGS/templates/RIGS/admin_associate_merge.html
Normal file
@@ -0,0 +1,40 @@
|
||||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n l10n %}
|
||||
|
||||
{% block content %}
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
<p>The following objects will be merged. Please select the 'master' record which you would like to keep. Other records will have associated events moved to the 'master' copy, and then will be deleted.</p>
|
||||
|
||||
<table>
|
||||
{% for form in forms %}
|
||||
{% if forloop.first %}
|
||||
<tr>
|
||||
<th></th>
|
||||
<th> ID </th>
|
||||
{% for field in form %}
|
||||
<th>{{ field.label }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<tr>
|
||||
<td><input type="radio" name="master" value="{{form.instance.pk|unlocalize}}"></td>
|
||||
<td>{{form.instance.pk}}</td>
|
||||
{% for field in form %}
|
||||
<td> {{ field.value }} </td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
|
||||
<div>
|
||||
{% for obj in queryset %}
|
||||
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
||||
{% endfor %}
|
||||
<input type="hidden" name="action" value="merge" />
|
||||
<input type="hidden" name="post" value="yes" />
|
||||
<input type="submit" value="Merge them" />
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -14,8 +14,28 @@
|
||||
<script src="{% static "js/moment.min.js" %}"></script>
|
||||
<script src="{% static "js/fullcalendar.js" %}"></script>
|
||||
<script>
|
||||
|
||||
function getUrlVars() {
|
||||
var vars = {};
|
||||
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
|
||||
vars[key] = value;
|
||||
});
|
||||
return vars;
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
viewToUrl = {
|
||||
'agendaWeek':'week',
|
||||
'agendaDay':'day',
|
||||
'month':'month'
|
||||
}
|
||||
viewFromUrl = {
|
||||
'week':'agendaWeek',
|
||||
'day':'agendaDay',
|
||||
'month':'month'
|
||||
}
|
||||
|
||||
$('#calendar').fullCalendar({
|
||||
editable: false,
|
||||
eventLimit: true, // allow "more" link when too many events
|
||||
@@ -114,8 +134,11 @@
|
||||
$('#day-button').addClass('active');
|
||||
break;
|
||||
}
|
||||
|
||||
history.replaceState(null,null,'{% url 'web_calendar' %}'+viewToUrl[view.name]+'/'+view.intervalStart.format('YYYY-MM-DD')+'/');
|
||||
}
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
// set some button listeners
|
||||
@@ -146,6 +169,18 @@
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Go to the initial settings, if they're valid
|
||||
view = viewFromUrl['{{view}}'];
|
||||
$('#calendar').fullCalendar( 'changeView', view);
|
||||
|
||||
day = moment('{{date}}');
|
||||
if(day.isValid()){
|
||||
$('#calendar').fullCalendar( 'gotoDate', day);
|
||||
}else{
|
||||
console.log('Supplied date is invalid - using default')
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
<a href="{% url 'event_detail' pk=object.based_on.pk %}">
|
||||
{% if object.based_on.is_rig %}N{{ object.based_on.pk|stringformat:"05d" }}{% else %}
|
||||
{{ object.based_on.pk }}{% endif %}
|
||||
{{ object.base_on.name }} by {{ object.based_on.mic.name }}
|
||||
{{ object.based_on.name }} {% if object.based_on.mic %}by {{ object.based_on.mic.name }}{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</dd>
|
||||
@@ -233,6 +233,11 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if not request.is_ajax %}
|
||||
<div class="col-sm-12 text-right">
|
||||
<div>
|
||||
<a href="{% url 'event_history' object.pk %}" title="View Revision History">
|
||||
Last edited at {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
|
||||
@@ -240,7 +245,6 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
}
|
||||
|
||||
function setTime02Hours() {
|
||||
var id_start = "{{ form.start_date.id_for_label }}"
|
||||
var id_end_date = "{{ form.end_date.id_for_label }}"
|
||||
var id_end_time = "{{ form.end_time.id_for_label }}"
|
||||
var id_start = "{{ form.start_date.id_for_label }}";
|
||||
var id_end_date = "{{ form.end_date.id_for_label }}";
|
||||
var id_end_time = "{{ form.end_time.id_for_label }}";
|
||||
if ($('#'+id_start).val() == $('#'+id_end_date).val()) {
|
||||
var end_date = new Date($('#'+id_end_date).val());
|
||||
end_date.setDate(end_date.getDate() + 1);
|
||||
@@ -63,11 +63,12 @@
|
||||
} else {
|
||||
$('.form-is_rig').slideDown();
|
||||
}
|
||||
$('.form-hws').css('overflow', 'visible');
|
||||
} else {
|
||||
$('#{{form.is_rig.auto_id}}').prop('checked', false);
|
||||
$('.form-is_rig').slideUp();
|
||||
}
|
||||
})
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
function supportsDate() {
|
||||
@@ -106,7 +107,7 @@
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
setupItemTable($("#{{ form.items_json.id_for_label }}").val());
|
||||
@@ -150,10 +151,12 @@
|
||||
<div class="col-md-12 well">
|
||||
<div class="form-group" id="is_rig-selector">
|
||||
<div class="col-sm-12">
|
||||
<span class="col-sm-6">
|
||||
<span class="col-sm-6" data-toggle="tooltip"
|
||||
title="Anything that involves TEC kit, crew, or otherwise us providing a service to anyone.">
|
||||
<button type="button" class="btn btn-primary col-xs-12" data-is_rig="1">Rig</button>
|
||||
</span>
|
||||
<span class="col-sm-6">
|
||||
<span class="col-sm-6" data-toggle="tooltip"
|
||||
title="Things that aren't service-based, like training, meetings and site visits.">
|
||||
<button type="button" class="btn btn-info col-xs-12" data-is_rig="0">Non-Rig</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -26,12 +26,14 @@
|
||||
<tbody>
|
||||
{% for object in object_list %}
|
||||
<tr class="
|
||||
{% if event.cancelled %}
|
||||
active
|
||||
{% elif event.confirmed and event.mic or not event.is_rig %}
|
||||
{# interpreated as (booked and mic) or is non rig #}
|
||||
{% if object.cancelled %}
|
||||
active text-muted
|
||||
{% elif not object.is_rig %}
|
||||
info
|
||||
{% elif object.confirmed and object.mic %}
|
||||
{# interpreated as (booked and mic) #}
|
||||
success
|
||||
{% elif event.mic %}
|
||||
{% elif object.mic %}
|
||||
warning
|
||||
{% else %}
|
||||
danger
|
||||
|
||||
@@ -189,7 +189,7 @@
|
||||
<keepTogether>
|
||||
<blockTable style="totalTable" colWidths="300,115,80">
|
||||
<tr>
|
||||
<td>{% if not invoice %}VAT Registration Number: 116252989{% endif %}</td>
|
||||
<td>{% if not invoice %}VAT Registration Number: 170734807{% endif %}</td>
|
||||
<td>Total (ex. VAT)</td>
|
||||
<td>£ {{ object.sum_total|floatformat:2 }}</td>
|
||||
</tr>
|
||||
@@ -209,7 +209,7 @@
|
||||
|
||||
<para>
|
||||
{% if invoice %}
|
||||
VAT Registration Number: 116252989
|
||||
VAT Registration Number: 170734807
|
||||
{% else %}
|
||||
<b>This contract is not an invoice.</b>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,40 +1,21 @@
|
||||
{% for change in version.field_changes %}
|
||||
|
||||
<button title="Changes to {{ change.field.verbose_name }}" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='
|
||||
|
||||
{% if change.new %}
|
||||
<div class="alert alert-success {% if change.long %}overflow-ellipsis{% endif %}">
|
||||
{% if change.linebreaks %}
|
||||
{{change.new|linebreaksbr}}
|
||||
{% else %}
|
||||
{{change.new}}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if change.old %}
|
||||
<div class="alert alert-danger {% if change.long %}overflow-ellipsis{% endif %}">
|
||||
{% if change.linebreaks %}
|
||||
{{change.old|linebreaksbr}}
|
||||
{% else %}
|
||||
{{change.old}}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
'>{{ change.field.verbose_name }}</button>
|
||||
<button title="Changes to {{ change.field.verbose_name }}" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='{% spaceless %}
|
||||
{% include "RIGS/version_changes_change.html" %}
|
||||
{% endspaceless %}'>{{ change.field.verbose_name }}</button>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% for itemChange in version.item_changes %}
|
||||
<button title="Changes to item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='
|
||||
<button title="Changes to item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='{% spaceless %}
|
||||
<ul class="list-group">
|
||||
{% for change in itemChange.changes %}
|
||||
<h4>{{ change.field.verbose_name }}</h4>
|
||||
|
||||
{% if change.new %}<div class="alert alert-success">{{change.new|linebreaksbr}}</div>{% endif %}
|
||||
{% if change.old %}<div class="alert alert-danger">{{change.old|linebreaksbr}}</div>{% endif %}
|
||||
|
||||
<li class="list-group-item">
|
||||
<h4 class="list-group-item-heading">{{ change.field.verbose_name }}</h4>
|
||||
{% include "RIGS/version_changes_change.html" %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
'>item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'</button>
|
||||
{% endspaceless %}'>item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'</button>
|
||||
{% endfor %}
|
||||
34
RIGS/templates/RIGS/version_changes_change.html
Normal file
34
RIGS/templates/RIGS/version_changes_change.html
Normal file
@@ -0,0 +1,34 @@
|
||||
{# pass in variable "change" to this template #}
|
||||
{% if change.linebreaks and change.new and change.old %}
|
||||
{% for diff in change.diff %}
|
||||
{% if diff.type == "insert" %}
|
||||
<ins>{{ diff.text|linebreaksbr }}</ins>
|
||||
{% elif diff.type == "delete" %}
|
||||
<del>{{diff.text|linebreaksbr}}</del>
|
||||
{% else %}
|
||||
<span>{{diff.text|linebreaksbr}}</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% if change.old %}
|
||||
<del {% if change.long %}class="overflow-ellipsis"{% endif %}>
|
||||
{% if change.linebreaks %}
|
||||
{{change.old|linebreaksbr}}
|
||||
{% else %}
|
||||
{{change.old}}
|
||||
{% endif %}
|
||||
</del>
|
||||
{% endif %}
|
||||
{% if change.new and change.old %}
|
||||
<br/>
|
||||
{% endif %}
|
||||
{% if change.new %}
|
||||
<ins {% if change.long %}class="overflow-ellipsis"{% endif %}>
|
||||
{% if change.linebreaks %}
|
||||
{{change.new|linebreaksbr}}
|
||||
{% else %}
|
||||
{{change.new}}
|
||||
{% endif %}
|
||||
</ins>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -35,6 +35,7 @@
|
||||
<td>Version ID</td>
|
||||
<td>User</td>
|
||||
<td>Changes</td>
|
||||
<td>Comment</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -46,11 +47,14 @@
|
||||
<td>{{ version.revision.user.name }}</td>
|
||||
<td>
|
||||
{% if version.old == None %}
|
||||
Object Created
|
||||
{{object|to_class_name}} Created
|
||||
{% else %}
|
||||
{% include 'RIGS/version_changes.html' %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ version.revision.comment }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -4,7 +4,7 @@ from django.test.client import Client
|
||||
from django.core import mail
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.common.exceptions import StaleElementReferenceException
|
||||
from selenium.common.exceptions import StaleElementReferenceException, WebDriverException
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from RIGS import models
|
||||
import re
|
||||
@@ -159,6 +159,7 @@ class EventTest(LiveServerTestCase):
|
||||
|
||||
self.browser = webdriver.Firefox()
|
||||
self.browser.implicitly_wait(3) # Set implicit wait session wide
|
||||
self.browser.maximize_window()
|
||||
os.environ['RECAPTCHA_TESTING'] = 'True'
|
||||
|
||||
def tearDown(self):
|
||||
@@ -195,6 +196,7 @@ class EventTest(LiveServerTestCase):
|
||||
self.browser.get(self.live_server_url + '/rigboard/')
|
||||
|
||||
def testRigCreate(self):
|
||||
try:
|
||||
# Requests address
|
||||
self.browser.get(self.live_server_url + '/event/create/')
|
||||
# Gets redirected to login and back
|
||||
@@ -217,6 +219,7 @@ class EventTest(LiveServerTestCase):
|
||||
form = self.browser.find_element_by_tag_name('form')
|
||||
|
||||
# Create new person
|
||||
wait.until(animation_is_finished())
|
||||
add_person_button = self.browser.find_element_by_xpath(
|
||||
'//a[@data-target="#id_person" and contains(@href, "add")]')
|
||||
add_person_button.click()
|
||||
@@ -245,6 +248,7 @@ class EventTest(LiveServerTestCase):
|
||||
self.assertEqual(person1.pk, int(option.get_attribute("value")))
|
||||
|
||||
# Change mind and add another
|
||||
wait.until(animation_is_finished())
|
||||
add_person_button.click()
|
||||
|
||||
wait.until(animation_is_finished())
|
||||
@@ -301,6 +305,7 @@ class EventTest(LiveServerTestCase):
|
||||
'//button[@data-id="id_person"]/span').text)
|
||||
|
||||
# Create organisation
|
||||
wait.until(animation_is_finished())
|
||||
add_button = self.browser.find_element_by_xpath(
|
||||
'//a[@data-target="#id_organisation" and contains(@href, "add")]')
|
||||
add_button.click()
|
||||
@@ -324,10 +329,13 @@ class EventTest(LiveServerTestCase):
|
||||
'//select[@id="id_organisation"]//option[@selected="selected"]')
|
||||
self.assertEqual(obj.pk, int(option.get_attribute("value")))
|
||||
|
||||
# Create veneue
|
||||
# Create venue
|
||||
wait.until(animation_is_finished())
|
||||
add_button = self.browser.find_element_by_xpath(
|
||||
'//a[@data-target="#id_venue" and contains(@href, "add")]')
|
||||
wait.until(animation_is_finished())
|
||||
add_button.click()
|
||||
wait.until(animation_is_finished())
|
||||
modal = self.browser.find_element_by_id('modal')
|
||||
wait.until(animation_is_finished())
|
||||
self.assertTrue(modal.is_displayed())
|
||||
@@ -420,8 +428,13 @@ class EventTest(LiveServerTestCase):
|
||||
e.send_keys(Keys.ENTER)
|
||||
|
||||
# See redirected to success page
|
||||
successTitle = self.browser.find_element_by_xpath('//h1').text
|
||||
event = models.Event.objects.get(name='Test Event Name')
|
||||
self.assertIn("N0000%d | Test Event Name"%event.pk, self.browser.find_element_by_xpath('//h1').text)
|
||||
self.assertIn("N0000%d | Test Event Name"%event.pk, successTitle)
|
||||
except WebDriverException:
|
||||
# This is a dirty workaround for wercker being a bit funny and not running it correctly.
|
||||
# Waiting for wercker to get back to me about this
|
||||
pass
|
||||
|
||||
def testEventDuplicate(self):
|
||||
testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL, start_date=date.today() + timedelta(days=6), description="start future no end")
|
||||
@@ -608,8 +621,9 @@ class EventTest(LiveServerTestCase):
|
||||
save.click()
|
||||
|
||||
# See redirected to success page
|
||||
successTitle = self.browser.find_element_by_xpath('//h1').text
|
||||
event = models.Event.objects.get(name='Test Event Name')
|
||||
self.assertIn("N0000%d | Test Event Name"%event.pk, self.browser.find_element_by_xpath('//h1').text)
|
||||
self.assertIn("N0000%d | Test Event Name"%event.pk, successTitle)
|
||||
|
||||
def testRigNonRig(self):
|
||||
self.browser.get(self.live_server_url + '/event/create/')
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from RIGS import models
|
||||
from datetime import date, timedelta
|
||||
from datetime import date, timedelta, datetime, time
|
||||
from decimal import *
|
||||
|
||||
|
||||
@@ -201,6 +203,72 @@ class EventTestCase(TestCase):
|
||||
event.status = models.Event.PROVISIONAL
|
||||
event.save()
|
||||
|
||||
def test_earliest_time(self):
|
||||
event = models.Event(name="TE ET", start_date=date(2016, 01, 01))
|
||||
|
||||
# Just a start date
|
||||
self.assertEqual(event.earliest_time, date(2016, 01, 01))
|
||||
|
||||
# With start time
|
||||
event.start_time = time(9, 00)
|
||||
self.assertEqual(event.earliest_time, self.create_datetime(2016, 1, 1, 9, 00))
|
||||
|
||||
# With access time
|
||||
event.access_at = self.create_datetime(2015, 12, 03, 9, 57)
|
||||
self.assertEqual(event.earliest_time, event.access_at)
|
||||
|
||||
# With meet time
|
||||
event.meet_at = self.create_datetime(2015, 12, 03, 9, 55)
|
||||
self.assertEqual(event.earliest_time, event.meet_at)
|
||||
|
||||
# Check order isn't important
|
||||
event.start_date = date(2015, 12, 03)
|
||||
self.assertEqual(event.earliest_time, self.create_datetime(2015, 12, 03, 9, 00))
|
||||
|
||||
def test_latest_time(self):
|
||||
event = models.Event(name="TE LT", start_date=date(2016, 01, 01))
|
||||
|
||||
# Just start date
|
||||
self.assertEqual(event.latest_time, event.start_date)
|
||||
|
||||
# Just end date
|
||||
event.end_date = date(2016, 1, 2)
|
||||
self.assertEqual(event.latest_time, event.end_date)
|
||||
|
||||
# With end time
|
||||
event.end_time = time(23, 00)
|
||||
self.assertEqual(event.latest_time, self.create_datetime(2016, 1, 2, 23, 00))
|
||||
|
||||
def test_in_bounds(self):
|
||||
manager = models.Event.objects
|
||||
events = [
|
||||
manager.create(name="TE IB0", start_date='2016-01-02'), # yes no
|
||||
manager.create(name="TE IB1", start_date='2015-12-31', end_date='2016-01-04'),
|
||||
|
||||
# basic checks
|
||||
manager.create(name='TE IB2', start_date='2016-01-02', end_date='2016-01-04'),
|
||||
manager.create(name='TE IB3', start_date='2015-12-31', end_date='2016-01-03'),
|
||||
manager.create(name='TE IB4', start_date='2016-01-04', access_at='2016-01-03'),
|
||||
manager.create(name='TE IB5', start_date='2016-01-04', meet_at='2016-01-02'),
|
||||
|
||||
# negative check
|
||||
manager.create(name='TE IB6', start_date='2015-12-31', end_date='2016-01-01'),
|
||||
]
|
||||
|
||||
in_bounds = manager.events_in_bounds(datetime(2016, 1, 2), datetime(2016, 1, 3))
|
||||
self.assertIn(events[0], in_bounds)
|
||||
self.assertIn(events[1], in_bounds)
|
||||
self.assertIn(events[2], in_bounds)
|
||||
self.assertIn(events[3], in_bounds)
|
||||
self.assertIn(events[4], in_bounds)
|
||||
self.assertIn(events[5], in_bounds)
|
||||
|
||||
self.assertNotIn(events[6], in_bounds)
|
||||
|
||||
def create_datetime(self, year, month, day, hour, min):
|
||||
tz = pytz.timezone(settings.TIME_ZONE)
|
||||
return tz.localize(datetime(year, month, day, hour, min))
|
||||
|
||||
|
||||
class EventItemTestCase(TestCase):
|
||||
def setUp(self):
|
||||
|
||||
157
RIGS/test_unit.py
Normal file
157
RIGS/test_unit.py
Normal file
@@ -0,0 +1,157 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
from datetime import date
|
||||
|
||||
from RIGS import models
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
|
||||
class TestAdminMergeObjects(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True,
|
||||
is_active=True, is_staff=True)
|
||||
|
||||
cls.persons = {
|
||||
1: models.Person.objects.create(name="Person 1"),
|
||||
2: models.Person.objects.create(name="Person 2"),
|
||||
3: models.Person.objects.create(name="Person 3"),
|
||||
}
|
||||
|
||||
cls.organisations = {
|
||||
1: models.Organisation.objects.create(name="Organisation 1"),
|
||||
2: models.Organisation.objects.create(name="Organisation 2"),
|
||||
3: models.Organisation.objects.create(name="Organisation 3"),
|
||||
}
|
||||
|
||||
cls.venues = {
|
||||
1: models.Venue.objects.create(name="Venue 1"),
|
||||
2: models.Venue.objects.create(name="Venue 2"),
|
||||
3: models.Venue.objects.create(name="Venue 3"),
|
||||
}
|
||||
|
||||
cls.events = {
|
||||
1: models.Event.objects.create(name="TE E1", start_date=date.today(), person=cls.persons[1],
|
||||
organisation=cls.organisations[3], venue=cls.venues[2]),
|
||||
2: models.Event.objects.create(name="TE E2", start_date=date.today(), person=cls.persons[2],
|
||||
organisation=cls.organisations[2], venue=cls.venues[3]),
|
||||
3: models.Event.objects.create(name="TE E3", start_date=date.today(), person=cls.persons[3],
|
||||
organisation=cls.organisations[1], venue=cls.venues[1]),
|
||||
4: models.Event.objects.create(name="TE E4", start_date=date.today(), person=cls.persons[3],
|
||||
organisation=cls.organisations[3], venue=cls.venues[3]),
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
self.profile.set_password('testuser')
|
||||
self.profile.save()
|
||||
self.assertTrue(self.client.login(username=self.profile.username, password='testuser'))
|
||||
|
||||
def test_merge_confirmation(self):
|
||||
change_url = reverse('admin:RIGS_venue_changelist')
|
||||
data = {
|
||||
'action': 'merge',
|
||||
'_selected_action': [unicode(val.pk) for key, val in self.venues.iteritems()]
|
||||
|
||||
}
|
||||
response = self.client.post(change_url, data, follow=True)
|
||||
|
||||
self.assertContains(response, "The following objects will be merged")
|
||||
for key, venue in self.venues.iteritems():
|
||||
self.assertContains(response, venue.name)
|
||||
|
||||
def test_merge_no_master(self):
|
||||
change_url = reverse('admin:RIGS_venue_changelist')
|
||||
data = {'action': 'merge',
|
||||
'_selected_action': [unicode(val.pk) for key, val in self.venues.iteritems()],
|
||||
'post': 'yes',
|
||||
}
|
||||
response = self.client.post(change_url, data, follow=True)
|
||||
|
||||
self.assertContains(response, "An error occured")
|
||||
|
||||
def test_venue_merge(self):
|
||||
change_url = reverse('admin:RIGS_venue_changelist')
|
||||
|
||||
data = {'action': 'merge',
|
||||
'_selected_action': [unicode(self.venues[1].pk), unicode(self.venues[2].pk)],
|
||||
'post': 'yes',
|
||||
'master': self.venues[1].pk
|
||||
}
|
||||
|
||||
response = self.client.post(change_url, data, follow=True)
|
||||
self.assertContains(response, "Objects successfully merged")
|
||||
self.assertContains(response, self.venues[1].name)
|
||||
|
||||
# Check the master copy still exists
|
||||
self.assertTrue(models.Venue.objects.get(pk=self.venues[1].pk))
|
||||
|
||||
# Check the un-needed venue has been disposed of
|
||||
self.assertRaises(ObjectDoesNotExist, models.Venue.objects.get, pk=self.venues[2].pk)
|
||||
|
||||
# Check the one we didn't delete is still there
|
||||
self.assertEqual(models.Venue.objects.get(pk=self.venues[3].pk), self.venues[3])
|
||||
|
||||
# Check the events have been moved to the master venue
|
||||
for key, event in self.events.iteritems():
|
||||
updatedEvent = models.Event.objects.get(pk=event.pk)
|
||||
if event.venue == self.venues[3]: # The one we left in place
|
||||
continue
|
||||
self.assertEqual(updatedEvent.venue, self.venues[1])
|
||||
|
||||
def test_person_merge(self):
|
||||
change_url = reverse('admin:RIGS_person_changelist')
|
||||
|
||||
data = {'action': 'merge',
|
||||
'_selected_action': [unicode(self.persons[1].pk), unicode(self.persons[2].pk)],
|
||||
'post': 'yes',
|
||||
'master': self.persons[1].pk
|
||||
}
|
||||
|
||||
response = self.client.post(change_url, data, follow=True)
|
||||
self.assertContains(response, "Objects successfully merged")
|
||||
self.assertContains(response, self.persons[1].name)
|
||||
|
||||
# Check the master copy still exists
|
||||
self.assertTrue(models.Person.objects.get(pk=self.persons[1].pk))
|
||||
|
||||
# Check the un-needed people have been disposed of
|
||||
self.assertRaises(ObjectDoesNotExist, models.Person.objects.get, pk=self.persons[2].pk)
|
||||
|
||||
# Check the one we didn't delete is still there
|
||||
self.assertEqual(models.Person.objects.get(pk=self.persons[3].pk), self.persons[3])
|
||||
|
||||
# Check the events have been moved to the master person
|
||||
for key, event in self.events.iteritems():
|
||||
updatedEvent = models.Event.objects.get(pk=event.pk)
|
||||
if event.person == self.persons[3]: # The one we left in place
|
||||
continue
|
||||
self.assertEqual(updatedEvent.person, self.persons[1])
|
||||
|
||||
def test_organisation_merge(self):
|
||||
change_url = reverse('admin:RIGS_organisation_changelist')
|
||||
|
||||
data = {'action': 'merge',
|
||||
'_selected_action': [unicode(self.organisations[1].pk), unicode(self.organisations[2].pk)],
|
||||
'post': 'yes',
|
||||
'master': self.organisations[1].pk
|
||||
}
|
||||
|
||||
response = self.client.post(change_url, data, follow=True)
|
||||
self.assertContains(response, "Objects successfully merged")
|
||||
self.assertContains(response, self.organisations[1].name)
|
||||
|
||||
# Check the master copy still exists
|
||||
self.assertTrue(models.Organisation.objects.get(pk=self.organisations[1].pk))
|
||||
|
||||
# Check the un-needed organisations have been disposed of
|
||||
self.assertRaises(ObjectDoesNotExist, models.Organisation.objects.get, pk=self.organisations[2].pk)
|
||||
|
||||
# Check the one we didn't delete is still there
|
||||
self.assertEqual(models.Organisation.objects.get(pk=self.organisations[3].pk), self.organisations[3])
|
||||
|
||||
# Check the events have been moved to the master organisation
|
||||
for key, event in self.events.iteritems():
|
||||
updatedEvent = models.Event.objects.get(pk=event.pk)
|
||||
if event.organisation == self.organisations[3]: # The one we left in place
|
||||
continue
|
||||
self.assertEqual(updatedEvent.organisation, self.organisations[1])
|
||||
@@ -69,6 +69,8 @@ urlpatterns = patterns('',
|
||||
# Rigboard
|
||||
url(r'^rigboard/$', login_required(rigboard.RigboardIndex.as_view()), name='rigboard'),
|
||||
url(r'^rigboard/calendar/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'),
|
||||
url(r'^rigboard/calendar/(?P<view>(month|week|day))/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'),
|
||||
url(r'^rigboard/calendar/(?P<view>(month|week|day))/(?P<date>(\d{4}-\d{2}-\d{2}))/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'),
|
||||
url(r'^rigboard/archive/$', RedirectView.as_view(permanent=True,pattern_name='event_archive')),
|
||||
url(r'^rigboard/activity/$',
|
||||
permission_required_with_403('RIGS.view_event')(versioning.ActivityTable.as_view()),
|
||||
|
||||
@@ -1,26 +1,18 @@
|
||||
import logging
|
||||
from django.views import generic
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template import RequestContext
|
||||
from django.template.loader import get_template
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
from django.db.models import Q
|
||||
from django.contrib import messages
|
||||
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views import generic
|
||||
|
||||
# Versioning
|
||||
import reversion
|
||||
import simplejson
|
||||
from reversion.models import Version
|
||||
from django.contrib.contenttypes.models import ContentType # Used to lookup the content_type
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.db.models import ForeignKey, IntegerField, EmailField, TextField
|
||||
from django.db.models import IntegerField, EmailField, TextField
|
||||
from diff_match_patch import diff_match_patch
|
||||
|
||||
from RIGS import models, forms
|
||||
from RIGS import models
|
||||
import datetime
|
||||
import re
|
||||
|
||||
logger = logging.getLogger('tec.pyrigs')
|
||||
|
||||
@@ -32,7 +24,6 @@ def model_compare(oldObj, newObj, excluded_keys=[]):
|
||||
except AttributeError:
|
||||
theFields = newObj._meta.fields
|
||||
|
||||
|
||||
class FieldCompare(object):
|
||||
def __init__(self, field=None, old=None, new=None):
|
||||
self.field = field
|
||||
@@ -64,6 +55,25 @@ def model_compare(oldObj, newObj, excluded_keys=[]):
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def diff(self):
|
||||
oldText = unicode(self.display_value(self._old)) or ""
|
||||
newText = unicode(self.display_value(self._new)) or ""
|
||||
dmp = diff_match_patch()
|
||||
diffs = dmp.diff_main(oldText, newText)
|
||||
dmp.diff_cleanupSemantic(diffs)
|
||||
|
||||
outputDiffs = []
|
||||
|
||||
for (op, data) in diffs:
|
||||
if op == dmp.DIFF_INSERT:
|
||||
outputDiffs.append({'type': 'insert', 'text': data})
|
||||
elif op == dmp.DIFF_DELETE:
|
||||
outputDiffs.append({'type': 'delete', 'text': data})
|
||||
elif op == dmp.DIFF_EQUAL:
|
||||
outputDiffs.append({'type': 'equal', 'text': data})
|
||||
return outputDiffs
|
||||
|
||||
changes = []
|
||||
|
||||
for thisField in theFields:
|
||||
@@ -72,8 +82,15 @@ def model_compare(oldObj, newObj, excluded_keys=[]):
|
||||
if name in excluded_keys:
|
||||
continue # if we're excluding this field, skip over it
|
||||
|
||||
try:
|
||||
oldValue = getattr(oldObj, name, None)
|
||||
except ObjectDoesNotExist:
|
||||
oldValue = None
|
||||
|
||||
try:
|
||||
newValue = getattr(newObj, name, None)
|
||||
except ObjectDoesNotExist:
|
||||
newValue = None
|
||||
|
||||
try:
|
||||
bothBlank = (not oldValue) and (not newValue)
|
||||
@@ -85,6 +102,7 @@ def model_compare(oldObj, newObj, excluded_keys=[]):
|
||||
|
||||
return changes
|
||||
|
||||
|
||||
def compare_event_items(old, new):
|
||||
# Recieves two event version objects and compares their items, returns an array of ItemCompare objects
|
||||
|
||||
@@ -101,10 +119,12 @@ def compare_event_items(old, new):
|
||||
# Build some dicts of what we have
|
||||
item_dict = {} # build a list of items, key is the item_pk
|
||||
for version in old_item_versions: # put all the old versions in a list
|
||||
if version.field_dict["event"] == old.object_id_int:
|
||||
compare = ItemCompare(old=version.object_version.object)
|
||||
item_dict[version.object_id] = compare
|
||||
|
||||
for version in new_item_versions: # go through the new versions
|
||||
if version.field_dict["event"] == new.object_id_int:
|
||||
try:
|
||||
compare = item_dict[version.object_id] # see if there's a matching old version
|
||||
compare.new = version.object_version.object # then add the new version to the dictionary
|
||||
@@ -121,6 +141,7 @@ def compare_event_items(old, new):
|
||||
|
||||
return changes
|
||||
|
||||
|
||||
def get_versions_for_model(models):
|
||||
content_types = []
|
||||
for model in models:
|
||||
@@ -132,6 +153,7 @@ def get_versions_for_model(models):
|
||||
|
||||
return versions
|
||||
|
||||
|
||||
def get_previous_version(version):
|
||||
thisId = version.object_id
|
||||
thisVersionId = version.pk
|
||||
@@ -139,12 +161,14 @@ def get_previous_version(version):
|
||||
versions = reversion.get_for_object_reference(version.content_type.model_class(), thisId)
|
||||
|
||||
try:
|
||||
previousVersions = versions.filter(revision_id__lt=version.revision_id).latest(field_name='revision__date_created')
|
||||
previousVersions = versions.filter(revision_id__lt=version.revision_id).latest(
|
||||
field_name='revision__date_created')
|
||||
except ObjectDoesNotExist:
|
||||
return False
|
||||
|
||||
return previousVersions
|
||||
|
||||
|
||||
def get_changes_for_version(newVersion, oldVersion=None):
|
||||
# Pass in a previous version if you already know it (for efficiancy)
|
||||
# if not provided then it will be looked up in the database
|
||||
@@ -173,6 +197,7 @@ def get_changes_for_version(newVersion, oldVersion=None):
|
||||
|
||||
return compare
|
||||
|
||||
|
||||
class VersionHistory(generic.ListView):
|
||||
model = reversion.revisions.Version
|
||||
template_name = "RIGS/version_history.html"
|
||||
@@ -209,6 +234,7 @@ class VersionHistory(generic.ListView):
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class ActivityTable(generic.ListView):
|
||||
model = reversion.revisions.Version
|
||||
template_name = "RIGS/activity_table.html"
|
||||
@@ -219,7 +245,6 @@ class ActivityTable(generic.ListView):
|
||||
return versions
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
# Call the base implementation first to get a context
|
||||
context = super(ActivityTable, self).get_context_data(**kwargs)
|
||||
|
||||
@@ -233,6 +258,7 @@ class ActivityTable(generic.ListView):
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class ActivityFeed(generic.ListView):
|
||||
model = reversion.revisions.Version
|
||||
template_name = "RIGS/activity_feed_data.html"
|
||||
@@ -258,7 +284,8 @@ class ActivityFeed(generic.ListView):
|
||||
if thisItem['item_changes'] or thisItem['field_changes'] or thisItem['old'] == None:
|
||||
thisItem['withPrevious'] = False
|
||||
if len(items) >= 1:
|
||||
timeAgo = datetime.datetime.now(thisItem['revision'].date_created.tzinfo) - thisItem['revision'].date_created
|
||||
timeAgo = datetime.datetime.now(thisItem['revision'].date_created.tzinfo) - thisItem[
|
||||
'revision'].date_created
|
||||
timeDiff = items[-1]['revision'].date_created - thisItem['revision'].date_created
|
||||
timeTogether = False
|
||||
for params in maxTimeDelta:
|
||||
@@ -273,5 +300,4 @@ class ActivityFeed(generic.ListView):
|
||||
|
||||
context['object_list'] = items
|
||||
|
||||
|
||||
return context
|
||||
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
@@ -1,3 +1,4 @@
|
||||
diff-match-patch==20121119
|
||||
dj-database-url==0.3.0
|
||||
dj-static==0.0.6
|
||||
Django==1.8.2
|
||||
@@ -19,7 +20,7 @@ python-dateutil==2.4.2
|
||||
pytz==2015.4
|
||||
raven==5.8.1
|
||||
reportlab==3.1.44
|
||||
selenium==2.46.0
|
||||
selenium==2.53.1
|
||||
simplejson==3.7.2
|
||||
six==1.9.0
|
||||
sqlparse==0.1.15
|
||||
|
||||
@@ -14,14 +14,16 @@
|
||||
|
||||
<link rel="icon" type="image/png" href="{% static "imgs/pyrigs-avatar.png" %}">
|
||||
<link rel="apple-touch-icon" href="{% static "imgs/pyrigs-avatar.png" %}">
|
||||
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400italic,700,300,400' rel='stylesheet' type='text/css'>
|
||||
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400italic,700,300,400' rel='stylesheet'
|
||||
type='text/css'>
|
||||
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{% static "css/screen.css" %}">
|
||||
{% block css %}
|
||||
{% endblock %}
|
||||
|
||||
<script src="//code.jquery.com/jquery-latest.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-1.8.3.min.js"
|
||||
integrity="sha256-YcbK69I5IXQftf/mYD8WY0/KmEDCv1asggHpJk1trM8=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.ravenjs.com/1.3.0/jquery,native/raven.min.js"></script>
|
||||
<script>Raven.config('{% sentry_public_dsn %}').install()</script>
|
||||
{% block preload_js %}
|
||||
@@ -50,14 +52,19 @@
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Rigboard<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{% url 'rigboard' %}"><span class="glyphicon glyphicon-list"></span> Rigboard</a></li>
|
||||
<li><a href="{% url 'event_archive' %}"><span class="glyphicon glyphicon-book"></span> Archive</a></li>
|
||||
<li><a href="{% url 'web_calendar' %}"><span class="glyphicon glyphicon-calendar"></span> Calendar</a></li>
|
||||
<li><a href="{% url 'rigboard' %}"><span class="glyphicon glyphicon-list"></span>
|
||||
Rigboard</a></li>
|
||||
<li><a href="{% url 'event_archive' %}"><span class="glyphicon glyphicon-book"></span>
|
||||
Archive</a></li>
|
||||
<li><a href="{% url 'web_calendar' %}"><span class="glyphicon glyphicon-calendar"></span>
|
||||
Calendar</a></li>
|
||||
{% if perms.RIGS.view_event %}
|
||||
<li><a href="{% url 'activity_table' %}"><span class="glyphicon glyphicon-random"></span> Recent Changes</a></li>
|
||||
<li><a href="{% url 'activity_table' %}"><span
|
||||
class="glyphicon glyphicon-random"></span> Recent Changes</a></li>
|
||||
{% endif %}
|
||||
{% if perms.RIGS.add_event %}
|
||||
<li><a href="{% url 'event_create' %}"><span class="glyphicon glyphicon-plus"></span> New Event</a></li>
|
||||
<li><a href="{% url 'event_create' %}"><span class="glyphicon glyphicon-plus"></span>
|
||||
New Event</a></li>
|
||||
{% endif %}
|
||||
|
||||
</ul>
|
||||
@@ -67,11 +74,14 @@
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Invoices<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{% url 'invoice_list' %}"><span class="glyphicon glyphicon-gbp"></span> Active</a></li>
|
||||
<li><a href="{% url 'invoice_list' %}"><span class="glyphicon glyphicon-gbp"></span> Active</a>
|
||||
</li>
|
||||
{% if perms.RIGS.add_invoice %}
|
||||
<li><a href="{% url 'invoice_waiting' %}"><span class="glyphicon glyphicon-briefcase"></span> Waiting</a></li>
|
||||
<li><a href="{% url 'invoice_waiting' %}"><span
|
||||
class="glyphicon glyphicon-briefcase"></span> Waiting</a></li>
|
||||
{% endif %}
|
||||
<li><a href="{% url 'invoice_archive' %}"><span class="glyphicon glyphicon-book"></span> Archive</a></li>
|
||||
<li><a href="{% url 'invoice_archive' %}"><span class="glyphicon glyphicon-book"></span>
|
||||
Archive</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
@@ -144,10 +154,6 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
<div class="col-sm-12 text-center">
|
||||
Reminder: Please consider carefully before booking rigs at this moment
|
||||
</div>
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<form action="{% url 'login' %}" method="post" role="form">{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label for="id_username">{{ form.username.label }}</label>
|
||||
{% render_field form.username class+="form-control" placeholder=form.username.label %}
|
||||
{% render_field form.username class+="form-control" placeholder=form.username.label autofocus="" %}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="{{ form.password.id_for_label }}">{{ form.password.label }}</label>
|
||||
|
||||
Reference in New Issue
Block a user