Compare commits

...

68 Commits

Author SHA1 Message Date
David Taylor
44ce14f600 Enabled linking to specific views & dates within web calendar 2016-05-23 01:35:54 +01:00
Tom Price
e286d8bdee Merge pull request #229 from nottinghamtec/develop 2016-05-23 00:38:31 +01:00
David Taylor
1faf8f95c8 Fixed colour coding on invoice waiting page 2016-05-22 23:13:23 +01:00
Tom Price
2913b254b4 Fix an overflow issue with dropdown fields. Closes #204 2016-05-19 14:33:33 +01:00
Tom Price
ef81536066 Add tooltips to rig type selectors. Closes #227 2016-05-19 14:21:21 +01:00
David Taylor
29aa13316d Fix for versioning when event has been deleted, closes #226 2016-04-27 20:49:42 +01:00
David Taylor
98f28aaafd Based on has name as well as MIC - Fix #224 2016-04-12 20:15:13 +01:00
David Taylor
f8a2a7a959 Made items display in version history again 2016-04-07 09:23:35 +01:00
David Taylor
767b5512e3 Fail, missed a change, actually closes #185 now 2016-04-07 00:53:57 +01:00
David Taylor
f1bd1ca674 Make 'created' noun class-specific. Closes #185 2016-04-07 00:52:33 +01:00
David Taylor
b47cfed5a9 Fixed versioning UI for revisions containing multiple event versions, hopefully 2016-04-07 00:19:18 +01:00
davidtaylorhq
71b69aa0f6 Merge pull request #220 from nottinghamtec/merge-interface
Merge interface
2016-04-06 22:08:32 +01:00
Tom Price
df61225b73 Optimise imports
So many unused imports, these have been banished
2016-04-06 21:57:54 +01:00
Tom Price
823db68a6a PEP8 format files 2016-04-06 21:53:38 +01:00
Tom Price
ebe08c3bf1 Update tests to check items that aren't selected aren't affected.
PEP8 format the file
2016-04-06 21:52:25 +01:00
David Taylor
44ccead0a4 Added merge tests
I feel like it should be possible to abstract some of these so that they're not copied out for each model, but not sure how to go about it...
2016-04-05 15:32:13 +01:00
David Taylor
99dfdcd253 Make confirmation more useful 2016-04-05 12:53:04 +01:00
David Taylor
03ca65602f Allow sorting by number of events 2016-04-05 12:08:19 +01:00
David Taylor
ca6cddb392 Add comments display to versioning history (because why not).
Maybe in future we could have a box people can type in before they save changes to an event... But that's a separate project
2016-04-05 11:50:34 +01:00
David Taylor
33ce4b622d Fixed bug with versioning interface when related objects are deleted 2016-04-05 04:18:53 +01:00
David Taylor
46434977fb Created merge admin action for Person, Venue and Organisation models. Added template. 2016-04-05 04:18:19 +01:00
davidtaylorhq
f13303490a Merge pull request #211 from nottinghamtec/wercker
Wercker
2016-03-24 12:34:00 +00:00
David Taylor
84b0a57e14 Fixed line breaks for new items. Closes #212 2016-03-24 12:22:37 +00:00
Tom Price
2ee7c064af Add a dirty work around for wercker not quite working correctly. 2016-03-18 16:48:43 +00:00
Tom Price
b2b8546e3c Add test for events_in_bounds 2016-03-18 16:41:13 +00:00
Tom Price
2945a607bf Add more tests for models.Event properties 2016-03-18 16:27:20 +00:00
Tom Price
4370cf60d1 And yet some more waits 2016-03-17 18:44:03 +00:00
Tom Price
e7496a9d82 Add some more waits for animations in failing test 2016-03-17 18:37:15 +00:00
Tom Price
77f7a25fa8 Try maximising firefox so it is large enough to pass the test 2016-03-17 18:06:37 +00:00
Tom Price
10c5ea3e7c Add a wait for the animation to complete during event creation. 2016-03-17 17:47:02 +00:00
Tom Price
cd82712742 Merge pull request #207 from nottinghamtec/fixed_jquery
Fixed jquery version.

Closes #192
2016-03-17 17:34:58 +00:00
Tom Price
3067dcdda5 Update wercker in README.md
Only show builds from master

[ci skip]
2016-03-17 17:31:14 +00:00
Tom Price
94679d6783 Update seleium version for Wercker 2016-03-17 17:23:21 +00:00
Tom Price
54dc29b4b2 Switch to jquery CDN who provide a sha256 hash to validate against.
Advise is now to always use HTTPS for libraries as somebody else manages the certificate it will always validate and it makes sure that a large target doesn't get subject to MITM attack.
2016-03-17 17:18:42 +00:00
Tom Price
e699826ce9 Merge branch 'master' into fixed_jquery 2016-03-17 17:10:35 +00:00
David Taylor
33fa19c15e Merge branch 'version_history_diff', second time lucky 2016-03-16 15:21:53 +00:00
David Taylor
d6e9b030fd Merge branch 'master' into version_history_diff 2016-03-16 15:21:13 +00:00
David Taylor
f0a3c968a7 Added unicode support 2016-03-16 15:15:12 +00:00
David Taylor
d8760a00ba Revert "Merge branch 'version_history_diff'"
This reverts commit 8ee43ef3ab, reversing
changes made to 9964d33cc0.
2016-03-16 14:54:09 +00:00
David Taylor
8ee43ef3ab Merge branch 'version_history_diff' 2016-03-16 14:37:09 +00:00
David Taylor
1b6ce32deb Merge branch 'master' into version_history_diff 2016-03-16 14:36:24 +00:00
Tom Price
9964d33cc0 Update wercker in README.md
[ci skip]
2016-03-16 14:32:38 +00:00
David Taylor
1e81b718f6 Merge branch 'deleting_duplicates' 2016-03-16 13:09:02 +00:00
David Taylor
551737b882 Merge branch 'master' into deleting_duplicates 2016-03-16 13:05:54 +00:00
David Taylor
77dd36d4b4 Merge branch 'web_calendar' 2016-03-16 12:55:26 +00:00
David Taylor
9bace9a6da Merge branch 'master' into web_calendar 2016-03-16 12:52:57 +00:00
David Taylor
c6b45da72c Made tests more reliable - wait for success page to load before checking item exists in database 2016-03-16 12:49:42 +00:00
David Taylor
0721d61bef Merge branch 'master' into web_calendar 2016-03-16 12:30:22 +00:00
David Taylor
56bc084e60 Made interface more compact 2016-03-16 12:28:37 +00:00
David Taylor
481f56a4bd Added highlighting styles 2016-03-16 12:17:26 +00:00
David Taylor
11e9931438 Fixed compass config 2016-03-16 12:16:57 +00:00
David Taylor
ea840eb43c Added diff-match-patch to requirements 2016-03-16 12:08:41 +00:00
David Taylor
2ed0a4bcf9 Updated changes view to use diff where appropriate. 2016-03-16 10:08:26 +00:00
Harry Bridge
5d48d75f34 Make protocol-agnostic 2016-03-12 18:17:03 +00:00
Harry Bridge
92beb8bf79 Fixed jquery version 2016-03-12 18:12:39 +00:00
David Taylor
ed552b402a Based on event with no MIC - closes #163 2016-03-10 11:56:09 +00:00
David Taylor
8279bec4bf Removed warning message at top - closes #203 2016-03-10 11:46:28 +00:00
David Taylor
67b2bc6d54 Applied migration to development database 2016-02-29 20:44:09 +00:00
David Taylor
e3adfecd17 Added database migration 2016-02-29 20:43:11 +00:00
David Taylor
1681ab8fee Allowed linking to specific views/dates on the calendar - closes #153 2016-02-29 20:35:53 +00:00
David Taylor
a77bc65d7b Changed delete condition to SET_NULL - closes #199 2016-02-29 20:12:41 +00:00
David Taylor
73517ed443 Revision history link on non-rigs - closes #186 2016-02-29 19:55:04 +00:00
David Taylor
f59e11ecc4 Updated VAT Registration Number - Closes #197 2016-02-29 19:37:59 +00:00
Tom Price
19f619b2d4 Merge pull request #194 from nottinghamtec/login-autofocus
Made username field autofocus (using HTML5 Attribute). Fixes #193
2016-02-12 16:46:10 +00:00
David Taylor
e0d03c2cc3 Made username field autofocus (using HTML5 Attribute). Fixes #193 2016-02-12 16:27:11 +00:00
Tom Price
69f7152dd6 Merge pull request #191 from samozzy/patch-1
Update konami.js
2016-02-01 17:11:20 +00:00
samozzy
581f28757a Update konami.js
Disable Weirdness Factor 12 on devices without an 'escape' key.
2016-02-01 17:10:06 +00:00
Tom Price
660a54d955 Merge pull request #177 from nottinghamtec/login
Move captcha from login screen.

Close #176
2016-01-20 13:45:26 +00:00
30 changed files with 897 additions and 399 deletions

View File

@@ -1,5 +1,5 @@
# TEC PA & Lighting - PyRIGS #
[![wercker status](https://app.wercker.com/status/b26100ecccdfb46a9a9056553daac5b7/m/master "wercker status")](https://app.wercker.com/project/bykey/b26100ecccdfb46a9a9056553daac5b7)
[![wercker status](https://app.wercker.com/status/2dbe0517c3d83859c985ffc5a55a2802/m/master "wercker status")](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.

View File

@@ -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']

View 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),
),
]

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -40,6 +40,9 @@
{% endif %}
{% include 'RIGS/object_button.html' with object=version.new %}
{% if version.revision.comment %}
({{ version.revision.comment }})
{% endif %}
</small>
</p>

View File

@@ -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 %}

View 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 %}

View File

@@ -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>

View File

@@ -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 %}

View File

@@ -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>

View File

@@ -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

View File

@@ -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 %}

View File

@@ -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 %}

View 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 %}

View File

@@ -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 %}

View File

@@ -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/')

View File

@@ -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
View 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])

View File

@@ -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()),

View File

@@ -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

Binary file not shown.

View File

@@ -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

View File

@@ -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>

View File

@@ -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>