mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-02-09 08:19:41 +00:00
Merge branch 'master' into markdown
# Conflicts: # RIGS/static/css/screen.css # db.sqlite3 # requirements.txt
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# TEC PA & Lighting - PyRIGS #
|
# 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.
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,14 @@ import simplejson
|
|||||||
|
|
||||||
from RIGS import models
|
from RIGS import models
|
||||||
|
|
||||||
#Registration
|
|
||||||
|
# Registration
|
||||||
class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
|
class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
|
||||||
captcha = ReCaptchaField()
|
captcha = ReCaptchaField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Profile
|
model = models.Profile
|
||||||
fields = ('username','email','first_name','last_name','initials','phone')
|
fields = ('username', 'email', 'first_name', 'last_name', 'initials', 'phone')
|
||||||
|
|
||||||
def clean_initials(self):
|
def clean_initials(self):
|
||||||
"""
|
"""
|
||||||
@@ -26,24 +27,22 @@ class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
|
|||||||
raise forms.ValidationError("These initials are already in use. Please supply different initials.")
|
raise forms.ValidationError("These initials are already in use. Please supply different initials.")
|
||||||
return self.cleaned_data['initials']
|
return self.cleaned_data['initials']
|
||||||
|
|
||||||
# Login form
|
|
||||||
class LoginForm(AuthenticationForm):
|
|
||||||
captcha = ReCaptchaField(label='Captcha')
|
|
||||||
|
|
||||||
|
# Login form
|
||||||
class PasswordReset(PasswordResetForm):
|
class PasswordReset(PasswordResetForm):
|
||||||
captcha = ReCaptchaField(label='Captcha')
|
captcha = ReCaptchaField(label='Captcha')
|
||||||
|
|
||||||
class ProfileCreationForm(UserCreationForm):
|
|
||||||
|
|
||||||
|
class ProfileCreationForm(UserCreationForm):
|
||||||
class Meta(UserCreationForm.Meta):
|
class Meta(UserCreationForm.Meta):
|
||||||
model = models.Profile
|
model = models.Profile
|
||||||
|
|
||||||
|
|
||||||
class ProfileChangeForm(UserChangeForm):
|
class ProfileChangeForm(UserChangeForm):
|
||||||
|
|
||||||
class Meta(UserChangeForm.Meta):
|
class Meta(UserChangeForm.Meta):
|
||||||
model = models.Profile
|
model = models.Profile
|
||||||
|
|
||||||
|
|
||||||
# Events Shit
|
# Events Shit
|
||||||
class EventForm(forms.ModelForm):
|
class EventForm(forms.ModelForm):
|
||||||
datetime_input_formats = formats.get_format_lazy("DATETIME_INPUT_FORMATS") + settings.DATETIME_INPUT_FORMATS
|
datetime_input_formats = formats.get_format_lazy("DATETIME_INPUT_FORMATS") + settings.DATETIME_INPUT_FORMATS
|
||||||
@@ -96,7 +95,7 @@ class EventForm(forms.ModelForm):
|
|||||||
|
|
||||||
def _get_or_initialise_item(self, pk, data, event):
|
def _get_or_initialise_item(self, pk, data, event):
|
||||||
try:
|
try:
|
||||||
item = models.EventItem.objects.get(pk=pk,event=event)
|
item = models.EventItem.objects.get(pk=pk, event=event)
|
||||||
except models.EventItem.DoesNotExist:
|
except models.EventItem.DoesNotExist:
|
||||||
# This occurs for one of two reasons
|
# This occurs for one of two reasons
|
||||||
# 1) The event has been duplicated, so the item PKs belong to another event
|
# 1) The event has been duplicated, so the item PKs belong to another event
|
||||||
@@ -134,7 +133,6 @@ class EventForm(forms.ModelForm):
|
|||||||
for key in items:
|
for key in items:
|
||||||
items[key].save()
|
items[key].save()
|
||||||
|
|
||||||
|
|
||||||
return m
|
return m
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -142,4 +140,4 @@ class EventForm(forms.ModelForm):
|
|||||||
fields = ['is_rig', 'name', 'venue', 'start_time', 'end_date', 'start_date',
|
fields = ['is_rig', 'name', 'venue', 'start_time', 'end_date', 'start_date',
|
||||||
'end_time', 'meet_at', 'access_at', 'description', 'notes', 'mic',
|
'end_time', 'meet_at', 'access_at', 'description', 'notes', 'mic',
|
||||||
'person', 'organisation', 'dry_hire', 'checked_in_by', 'status',
|
'person', 'organisation', 'dry_hire', 'checked_in_by', 'status',
|
||||||
'collector','purchase_order']
|
'collector', 'purchase_order']
|
||||||
|
|||||||
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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -38,7 +38,10 @@ class Profile(AbstractUser):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return self.get_full_name() + ' "' + self.initials + '"'
|
name = self.get_full_name()
|
||||||
|
if self.initials:
|
||||||
|
name += ' "{}"'.format(self.initials)
|
||||||
|
return name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def latest_events(self):
|
def latest_events(self):
|
||||||
@@ -298,7 +301,7 @@ class Event(models.Model, RevisionMixin):
|
|||||||
status = models.IntegerField(choices=EVENT_STATUS_CHOICES, default=PROVISIONAL)
|
status = models.IntegerField(choices=EVENT_STATUS_CHOICES, default=PROVISIONAL)
|
||||||
dry_hire = models.BooleanField(default=False)
|
dry_hire = models.BooleanField(default=False)
|
||||||
is_rig = models.BooleanField(default=True)
|
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
|
# Timing
|
||||||
start_date = models.DateField()
|
start_date = models.DateField()
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ class RigboardIndex(generic.TemplateView):
|
|||||||
class WebCalendar(generic.TemplateView):
|
class WebCalendar(generic.TemplateView):
|
||||||
template_name = 'RIGS/calendar.html'
|
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):
|
class EventDetail(generic.DetailView):
|
||||||
model = models.Event
|
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):
|
# 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 = :expanded or :nested or :compact or :compressed
|
||||||
|
output_style = :compressed
|
||||||
|
|
||||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||||
# relative_assets = true
|
# 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;
|
return false;
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
this.iphone.load(link);
|
/*this.iphone.load(link);*/
|
||||||
},
|
},
|
||||||
code: function (link) {
|
code: function (link) {
|
||||||
window.location = 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 {
|
.loading-animation {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 30px auto 0;
|
margin: 30px auto 0;
|
||||||
|
|||||||
@@ -14,8 +14,28 @@
|
|||||||
<script src="{% static "js/moment.min.js" %}"></script>
|
<script src="{% static "js/moment.min.js" %}"></script>
|
||||||
<script src="{% static "js/fullcalendar.js" %}"></script>
|
<script src="{% static "js/fullcalendar.js" %}"></script>
|
||||||
<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() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
viewToUrl = {
|
||||||
|
'agendaWeek':'week',
|
||||||
|
'agendaDay':'day',
|
||||||
|
'month':'month'
|
||||||
|
}
|
||||||
|
viewFromUrl = {
|
||||||
|
'week':'agendaWeek',
|
||||||
|
'day':'agendaDay',
|
||||||
|
'month':'month'
|
||||||
|
}
|
||||||
|
|
||||||
$('#calendar').fullCalendar({
|
$('#calendar').fullCalendar({
|
||||||
editable: false,
|
editable: false,
|
||||||
eventLimit: true, // allow "more" link when too many events
|
eventLimit: true, // allow "more" link when too many events
|
||||||
@@ -114,8 +134,11 @@
|
|||||||
$('#day-button').addClass('active');
|
$('#day-button').addClass('active');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
history.replaceState(null,null,'{% url 'web_calendar' %}'+viewToUrl[view.name]+'/'+view.intervalStart.format('YYYY-MM-DD')+'/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// set some button listeners
|
// 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>
|
</script>
|
||||||
|
|||||||
@@ -152,7 +152,7 @@
|
|||||||
<a href="{% url 'event_detail' pk=object.based_on.pk %}">
|
<a href="{% url 'event_detail' pk=object.based_on.pk %}">
|
||||||
{% if object.based_on.is_rig %}N{{ object.based_on.pk|stringformat:"05d" }}{% else %}
|
{% if object.based_on.is_rig %}N{{ object.based_on.pk|stringformat:"05d" }}{% else %}
|
||||||
{{ object.based_on.pk }}{% endif %}
|
{{ object.based_on.pk }}{% endif %}
|
||||||
{{ object.base_on.name }} by {{ object.based_on.mic.name }}
|
{{ object.base_on.name }} {% if object.based_on.mic %}by {{ object.based_on.mic.name }}{% endif %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</dd>
|
</dd>
|
||||||
@@ -234,13 +234,17 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if not request.is_ajax %}
|
||||||
|
<div class="col-sm-12 text-right">
|
||||||
<div>
|
<div>
|
||||||
<a href="{% url 'event_history' object.pk %}" title="View Revision History">
|
<a href="{% url 'event_history' object.pk %}" title="View Revision History">
|
||||||
Last edited at {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
|
Last edited at {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -205,7 +205,7 @@
|
|||||||
<keepTogether>
|
<keepTogether>
|
||||||
<blockTable style="totalTable" colWidths="300,115,80">
|
<blockTable style="totalTable" colWidths="300,115,80">
|
||||||
<tr>
|
<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>Total (ex. VAT)</td>
|
||||||
<td>£ {{ object.sum_total|floatformat:2 }}</td>
|
<td>£ {{ object.sum_total|floatformat:2 }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -225,7 +225,7 @@
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
{% if invoice %}
|
{% if invoice %}
|
||||||
VAT Registration Number: 116252989
|
VAT Registration Number: 170734807
|
||||||
{% else %}
|
{% else %}
|
||||||
<b>This contract is not an invoice.</b>
|
<b>This contract is not an invoice.</b>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -1,40 +1,21 @@
|
|||||||
{% for change in version.field_changes %}
|
{% 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='
|
<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" %}
|
||||||
{% if change.new %}
|
{% endspaceless %}'>{{ change.field.verbose_name }}</button>
|
||||||
<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>
|
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% for itemChange in version.item_changes %}
|
{% 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 %}
|
{% for change in itemChange.changes %}
|
||||||
<h4>{{ change.field.verbose_name }}</h4>
|
<li class="list-group-item">
|
||||||
|
<h4 class="list-group-item-heading">{{ change.field.verbose_name }}</h4>
|
||||||
{% if change.new %}<div class="alert alert-success">{{change.new|linebreaksbr}}</div>{% endif %}
|
{% include "RIGS/version_changes_change.html" %}
|
||||||
{% if change.old %}<div class="alert alert-danger">{{change.old|linebreaksbr}}</div>{% endif %}
|
</li>
|
||||||
|
|
||||||
{% endfor %}
|
{% 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 %}
|
{% 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 %}
|
||||||
@@ -4,7 +4,7 @@ from django.test.client import Client
|
|||||||
from django.core import mail
|
from django.core import mail
|
||||||
from selenium import webdriver
|
from selenium import webdriver
|
||||||
from selenium.webdriver.common.keys import Keys
|
from selenium.webdriver.common.keys import Keys
|
||||||
from selenium.common.exceptions import StaleElementReferenceException
|
from selenium.common.exceptions import StaleElementReferenceException, WebDriverException
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from RIGS import models
|
from RIGS import models
|
||||||
import re
|
import re
|
||||||
@@ -159,6 +159,7 @@ class EventTest(LiveServerTestCase):
|
|||||||
|
|
||||||
self.browser = webdriver.Firefox()
|
self.browser = webdriver.Firefox()
|
||||||
self.browser.implicitly_wait(3) # Set implicit wait session wide
|
self.browser.implicitly_wait(3) # Set implicit wait session wide
|
||||||
|
self.browser.maximize_window()
|
||||||
os.environ['RECAPTCHA_TESTING'] = 'True'
|
os.environ['RECAPTCHA_TESTING'] = 'True'
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
@@ -177,8 +178,6 @@ class EventTest(LiveServerTestCase):
|
|||||||
|
|
||||||
username.send_keys("EventTest")
|
username.send_keys("EventTest")
|
||||||
password.send_keys("EventTestPassword")
|
password.send_keys("EventTestPassword")
|
||||||
self.browser.execute_script(
|
|
||||||
"return jQuery('#g-recaptcha-response').val('PASSED')")
|
|
||||||
submit.click()
|
submit.click()
|
||||||
|
|
||||||
self.assertEqual(self.live_server_url + n, self.browser.current_url)
|
self.assertEqual(self.live_server_url + n, self.browser.current_url)
|
||||||
@@ -197,233 +196,245 @@ class EventTest(LiveServerTestCase):
|
|||||||
self.browser.get(self.live_server_url + '/rigboard/')
|
self.browser.get(self.live_server_url + '/rigboard/')
|
||||||
|
|
||||||
def testRigCreate(self):
|
def testRigCreate(self):
|
||||||
# Requests address
|
|
||||||
self.browser.get(self.live_server_url + '/event/create/')
|
|
||||||
# Gets redirected to login and back
|
|
||||||
self.authenticate('/event/create/')
|
|
||||||
|
|
||||||
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
|
|
||||||
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
|
|
||||||
# Check has slided up correctly - second save button hidden
|
|
||||||
save = self.browser.find_element_by_xpath(
|
|
||||||
'(//button[@type="submit"])[3]')
|
|
||||||
self.assertFalse(save.is_displayed())
|
|
||||||
|
|
||||||
# Click Rig button
|
|
||||||
self.browser.find_element_by_xpath('//button[.="Rig"]').click()
|
|
||||||
|
|
||||||
# Slider expands and save button visible
|
|
||||||
self.assertTrue(save.is_displayed())
|
|
||||||
form = self.browser.find_element_by_tag_name('form')
|
|
||||||
|
|
||||||
# Create new person
|
|
||||||
add_person_button = self.browser.find_element_by_xpath(
|
|
||||||
'//a[@data-target="#id_person" and contains(@href, "add")]')
|
|
||||||
add_person_button.click()
|
|
||||||
|
|
||||||
# See modal has opened
|
|
||||||
modal = self.browser.find_element_by_id('modal')
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
self.assertTrue(modal.is_displayed())
|
|
||||||
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
|
|
||||||
|
|
||||||
# Fill person form out and submit
|
|
||||||
modal.find_element_by_xpath(
|
|
||||||
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 1")
|
|
||||||
modal.find_element_by_xpath(
|
|
||||||
'//div[@id="modal"]//input[@type="submit"]').click()
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
self.assertFalse(modal.is_displayed())
|
|
||||||
|
|
||||||
# See new person selected
|
|
||||||
person1 = models.Person.objects.get(name="Test Person 1")
|
|
||||||
self.assertEqual(person1.name, form.find_element_by_xpath(
|
|
||||||
'//button[@data-id="id_person"]/span').text)
|
|
||||||
# and backend
|
|
||||||
option = form.find_element_by_xpath(
|
|
||||||
'//select[@id="id_person"]//option[@selected="selected"]')
|
|
||||||
self.assertEqual(person1.pk, int(option.get_attribute("value")))
|
|
||||||
|
|
||||||
# Change mind and add another
|
|
||||||
add_person_button.click()
|
|
||||||
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
self.assertTrue(modal.is_displayed())
|
|
||||||
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
|
|
||||||
|
|
||||||
modal.find_element_by_xpath(
|
|
||||||
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 2")
|
|
||||||
modal.find_element_by_xpath(
|
|
||||||
'//div[@id="modal"]//input[@type="submit"]').click()
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
self.assertFalse(modal.is_displayed())
|
|
||||||
|
|
||||||
person2 = models.Person.objects.get(name="Test Person 2")
|
|
||||||
self.assertEqual(person2.name, form.find_element_by_xpath(
|
|
||||||
'//button[@data-id="id_person"]/span').text)
|
|
||||||
# Have to do this explcitly to force the wait for it to update
|
|
||||||
option = form.find_element_by_xpath(
|
|
||||||
'//select[@id="id_person"]//option[@selected="selected"]')
|
|
||||||
self.assertEqual(person2.pk, int(option.get_attribute("value")))
|
|
||||||
|
|
||||||
# Was right the first time, change it back
|
|
||||||
person_select = form.find_element_by_xpath(
|
|
||||||
'//button[@data-id="id_person"]')
|
|
||||||
person_select.send_keys(person1.name)
|
|
||||||
person_dropped = form.find_element_by_xpath(
|
|
||||||
'//ul[contains(@class, "inner selectpicker")]//span[contains(text(), "%s")]' % person1.name)
|
|
||||||
person_dropped.click()
|
|
||||||
|
|
||||||
self.assertEqual(person1.name, form.find_element_by_xpath(
|
|
||||||
'//button[@data-id="id_person"]/span').text)
|
|
||||||
option = form.find_element_by_xpath(
|
|
||||||
'//select[@id="id_person"]//option[@selected="selected"]')
|
|
||||||
self.assertEqual(person1.pk, int(option.get_attribute("value")))
|
|
||||||
|
|
||||||
# Edit Person 1 to have a better name
|
|
||||||
form.find_element_by_xpath(
|
|
||||||
'//a[@data-target="#id_person" and contains(@href, "%s/edit/")]' % person1.pk).click()
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
self.assertTrue(modal.is_displayed())
|
|
||||||
self.assertIn("Edit Person", modal.find_element_by_tag_name('h3').text)
|
|
||||||
name = modal.find_element_by_xpath(
|
|
||||||
'//div[@id="modal"]//input[@id="id_name"]')
|
|
||||||
self.assertEqual(person1.name, name.get_attribute('value'))
|
|
||||||
name.clear()
|
|
||||||
name.send_keys('Rig ' + person1.name)
|
|
||||||
name.send_keys(Keys.ENTER)
|
|
||||||
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
|
|
||||||
self.assertFalse(modal.is_displayed())
|
|
||||||
person1 = models.Person.objects.get(pk=person1.pk)
|
|
||||||
self.assertEqual(person1.name, form.find_element_by_xpath(
|
|
||||||
'//button[@data-id="id_person"]/span').text)
|
|
||||||
|
|
||||||
# Create organisation
|
|
||||||
add_button = self.browser.find_element_by_xpath(
|
|
||||||
'//a[@data-target="#id_organisation" and contains(@href, "add")]')
|
|
||||||
add_button.click()
|
|
||||||
modal = self.browser.find_element_by_id('modal')
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
self.assertTrue(modal.is_displayed())
|
|
||||||
self.assertIn("Add Organisation", modal.find_element_by_tag_name('h3').text)
|
|
||||||
modal.find_element_by_xpath(
|
|
||||||
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Organisation")
|
|
||||||
modal.find_element_by_xpath(
|
|
||||||
'//div[@id="modal"]//input[@type="submit"]').click()
|
|
||||||
|
|
||||||
# See it is selected
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
self.assertFalse(modal.is_displayed())
|
|
||||||
obj = models.Organisation.objects.get(name="Test Organisation")
|
|
||||||
self.assertEqual(obj.name, form.find_element_by_xpath(
|
|
||||||
'//button[@data-id="id_organisation"]/span').text)
|
|
||||||
# and backend
|
|
||||||
option = form.find_element_by_xpath(
|
|
||||||
'//select[@id="id_organisation"]//option[@selected="selected"]')
|
|
||||||
self.assertEqual(obj.pk, int(option.get_attribute("value")))
|
|
||||||
|
|
||||||
# Create veneue
|
|
||||||
add_button = self.browser.find_element_by_xpath(
|
|
||||||
'//a[@data-target="#id_venue" and contains(@href, "add")]')
|
|
||||||
add_button.click()
|
|
||||||
modal = self.browser.find_element_by_id('modal')
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
self.assertTrue(modal.is_displayed())
|
|
||||||
self.assertIn("Add Venue", modal.find_element_by_tag_name('h3').text)
|
|
||||||
modal.find_element_by_xpath(
|
|
||||||
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Venue")
|
|
||||||
modal.find_element_by_xpath(
|
|
||||||
'//div[@id="modal"]//input[@type="submit"]').click()
|
|
||||||
|
|
||||||
# See it is selected
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
self.assertFalse(modal.is_displayed())
|
|
||||||
obj = models.Venue.objects.get(name="Test Venue")
|
|
||||||
self.assertEqual(obj.name, form.find_element_by_xpath(
|
|
||||||
'//button[@data-id="id_venue"]/span').text)
|
|
||||||
# and backend
|
|
||||||
option = form.find_element_by_xpath(
|
|
||||||
'//select[@id="id_venue"]//option[@selected="selected"]')
|
|
||||||
self.assertEqual(obj.pk, int(option.get_attribute("value")))
|
|
||||||
|
|
||||||
# Set start date/time
|
|
||||||
form.find_element_by_id('id_start_date').send_keys('3015-05-25')
|
|
||||||
form.find_element_by_id('id_start_time').send_keys('06:59')
|
|
||||||
|
|
||||||
# Set end date/time
|
|
||||||
form.find_element_by_id('id_end_date').send_keys('4000-06-27')
|
|
||||||
form.find_element_by_id('id_end_time').send_keys('07:00')
|
|
||||||
|
|
||||||
# Add item
|
|
||||||
form.find_element_by_xpath('//button[contains(@class, "item-add")]').click()
|
|
||||||
wait.until(animation_is_finished())
|
|
||||||
modal = self.browser.find_element_by_id("itemModal")
|
|
||||||
modal.find_element_by_id("item_name").send_keys("Test Item 1")
|
|
||||||
modal.find_element_by_id("item_description").send_keys("This is an item description\nthat for reasons unkown spans two lines")
|
|
||||||
e = modal.find_element_by_id("item_quantity")
|
|
||||||
e.click()
|
|
||||||
e.send_keys(Keys.UP)
|
|
||||||
e.send_keys(Keys.UP)
|
|
||||||
e = modal.find_element_by_id("item_cost")
|
|
||||||
e.send_keys("23.95")
|
|
||||||
e.send_keys(Keys.ENTER) # enter submit
|
|
||||||
|
|
||||||
# Confirm item has been saved to json field
|
|
||||||
objectitems = self.browser.execute_script("return objectitems;")
|
|
||||||
self.assertEqual(1, len(objectitems))
|
|
||||||
testitem = objectitems["-1"]['fields'] # as we are deliberately creating this we know the ID
|
|
||||||
self.assertEqual("Test Item 1", testitem['name'])
|
|
||||||
self.assertEqual("2", testitem['quantity']) # test a couple of "worse case" fields
|
|
||||||
|
|
||||||
# See new item appear in table
|
|
||||||
row = self.browser.find_element_by_id('item--1') # ID number is known, see above
|
|
||||||
self.assertIn("Test Item 1", row.find_element_by_xpath('//span[@class="name"]').text)
|
|
||||||
self.assertIn("This is an item description", row.find_element_by_xpath('//div[@class="item-description"]').text)
|
|
||||||
self.assertEqual(u'£ 23.95', row.find_element_by_xpath('//tr[@id="item--1"]/td[2]').text)
|
|
||||||
self.assertEqual("2", row.find_element_by_xpath('//td[@class="quantity"]').text)
|
|
||||||
self.assertEqual(u'£ 47.90', row.find_element_by_xpath('//tr[@id="item--1"]/td[4]').text)
|
|
||||||
|
|
||||||
# Check totals
|
|
||||||
self.assertEqual("47.90", self.browser.find_element_by_id('sumtotal').text)
|
|
||||||
self.assertIn("(TBC)", self.browser.find_element_by_id('vat-rate').text)
|
|
||||||
self.assertEqual("9.58", self.browser.find_element_by_id('vat').text)
|
|
||||||
self.assertEqual("57.48", self.browser.find_element_by_id('total').text)
|
|
||||||
|
|
||||||
# Attempt to save - missing title
|
|
||||||
save.click()
|
|
||||||
|
|
||||||
# See error
|
|
||||||
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
|
|
||||||
self.assertTrue(error.is_displayed())
|
|
||||||
# Should only have one error message
|
|
||||||
self.assertEqual("Name", error.find_element_by_xpath('//dt[1]').text)
|
|
||||||
self.assertEqual("This field is required.", error.find_element_by_xpath('//dd[1]/ul/li').text)
|
|
||||||
# don't need error so close it
|
|
||||||
error.find_element_by_xpath('//div[contains(@class, "alert-danger")]//button[@class="close"]').click()
|
|
||||||
try:
|
try:
|
||||||
self.assertFalse(error.is_displayed())
|
# Requests address
|
||||||
except StaleElementReferenceException:
|
self.browser.get(self.live_server_url + '/event/create/')
|
||||||
|
# Gets redirected to login and back
|
||||||
|
self.authenticate('/event/create/')
|
||||||
|
|
||||||
|
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
|
||||||
|
|
||||||
|
wait.until(animation_is_finished())
|
||||||
|
|
||||||
|
# Check has slided up correctly - second save button hidden
|
||||||
|
save = self.browser.find_element_by_xpath(
|
||||||
|
'(//button[@type="submit"])[3]')
|
||||||
|
self.assertFalse(save.is_displayed())
|
||||||
|
|
||||||
|
# Click Rig button
|
||||||
|
self.browser.find_element_by_xpath('//button[.="Rig"]').click()
|
||||||
|
|
||||||
|
# Slider expands and save button visible
|
||||||
|
self.assertTrue(save.is_displayed())
|
||||||
|
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()
|
||||||
|
|
||||||
|
# See modal has opened
|
||||||
|
modal = self.browser.find_element_by_id('modal')
|
||||||
|
wait.until(animation_is_finished())
|
||||||
|
self.assertTrue(modal.is_displayed())
|
||||||
|
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
|
||||||
|
|
||||||
|
# Fill person form out and submit
|
||||||
|
modal.find_element_by_xpath(
|
||||||
|
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 1")
|
||||||
|
modal.find_element_by_xpath(
|
||||||
|
'//div[@id="modal"]//input[@type="submit"]').click()
|
||||||
|
wait.until(animation_is_finished())
|
||||||
|
self.assertFalse(modal.is_displayed())
|
||||||
|
|
||||||
|
# See new person selected
|
||||||
|
person1 = models.Person.objects.get(name="Test Person 1")
|
||||||
|
self.assertEqual(person1.name, form.find_element_by_xpath(
|
||||||
|
'//button[@data-id="id_person"]/span').text)
|
||||||
|
# and backend
|
||||||
|
option = form.find_element_by_xpath(
|
||||||
|
'//select[@id="id_person"]//option[@selected="selected"]')
|
||||||
|
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())
|
||||||
|
self.assertTrue(modal.is_displayed())
|
||||||
|
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
|
||||||
|
|
||||||
|
modal.find_element_by_xpath(
|
||||||
|
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 2")
|
||||||
|
modal.find_element_by_xpath(
|
||||||
|
'//div[@id="modal"]//input[@type="submit"]').click()
|
||||||
|
wait.until(animation_is_finished())
|
||||||
|
self.assertFalse(modal.is_displayed())
|
||||||
|
|
||||||
|
person2 = models.Person.objects.get(name="Test Person 2")
|
||||||
|
self.assertEqual(person2.name, form.find_element_by_xpath(
|
||||||
|
'//button[@data-id="id_person"]/span').text)
|
||||||
|
# Have to do this explcitly to force the wait for it to update
|
||||||
|
option = form.find_element_by_xpath(
|
||||||
|
'//select[@id="id_person"]//option[@selected="selected"]')
|
||||||
|
self.assertEqual(person2.pk, int(option.get_attribute("value")))
|
||||||
|
|
||||||
|
# Was right the first time, change it back
|
||||||
|
person_select = form.find_element_by_xpath(
|
||||||
|
'//button[@data-id="id_person"]')
|
||||||
|
person_select.send_keys(person1.name)
|
||||||
|
person_dropped = form.find_element_by_xpath(
|
||||||
|
'//ul[contains(@class, "inner selectpicker")]//span[contains(text(), "%s")]' % person1.name)
|
||||||
|
person_dropped.click()
|
||||||
|
|
||||||
|
self.assertEqual(person1.name, form.find_element_by_xpath(
|
||||||
|
'//button[@data-id="id_person"]/span').text)
|
||||||
|
option = form.find_element_by_xpath(
|
||||||
|
'//select[@id="id_person"]//option[@selected="selected"]')
|
||||||
|
self.assertEqual(person1.pk, int(option.get_attribute("value")))
|
||||||
|
|
||||||
|
# Edit Person 1 to have a better name
|
||||||
|
form.find_element_by_xpath(
|
||||||
|
'//a[@data-target="#id_person" and contains(@href, "%s/edit/")]' % person1.pk).click()
|
||||||
|
wait.until(animation_is_finished())
|
||||||
|
self.assertTrue(modal.is_displayed())
|
||||||
|
self.assertIn("Edit Person", modal.find_element_by_tag_name('h3').text)
|
||||||
|
name = modal.find_element_by_xpath(
|
||||||
|
'//div[@id="modal"]//input[@id="id_name"]')
|
||||||
|
self.assertEqual(person1.name, name.get_attribute('value'))
|
||||||
|
name.clear()
|
||||||
|
name.send_keys('Rig ' + person1.name)
|
||||||
|
name.send_keys(Keys.ENTER)
|
||||||
|
|
||||||
|
wait.until(animation_is_finished())
|
||||||
|
|
||||||
|
self.assertFalse(modal.is_displayed())
|
||||||
|
person1 = models.Person.objects.get(pk=person1.pk)
|
||||||
|
self.assertEqual(person1.name, form.find_element_by_xpath(
|
||||||
|
'//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()
|
||||||
|
modal = self.browser.find_element_by_id('modal')
|
||||||
|
wait.until(animation_is_finished())
|
||||||
|
self.assertTrue(modal.is_displayed())
|
||||||
|
self.assertIn("Add Organisation", modal.find_element_by_tag_name('h3').text)
|
||||||
|
modal.find_element_by_xpath(
|
||||||
|
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Organisation")
|
||||||
|
modal.find_element_by_xpath(
|
||||||
|
'//div[@id="modal"]//input[@type="submit"]').click()
|
||||||
|
|
||||||
|
# See it is selected
|
||||||
|
wait.until(animation_is_finished())
|
||||||
|
self.assertFalse(modal.is_displayed())
|
||||||
|
obj = models.Organisation.objects.get(name="Test Organisation")
|
||||||
|
self.assertEqual(obj.name, form.find_element_by_xpath(
|
||||||
|
'//button[@data-id="id_organisation"]/span').text)
|
||||||
|
# and backend
|
||||||
|
option = form.find_element_by_xpath(
|
||||||
|
'//select[@id="id_organisation"]//option[@selected="selected"]')
|
||||||
|
self.assertEqual(obj.pk, int(option.get_attribute("value")))
|
||||||
|
|
||||||
|
# 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())
|
||||||
|
self.assertIn("Add Venue", modal.find_element_by_tag_name('h3').text)
|
||||||
|
modal.find_element_by_xpath(
|
||||||
|
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Venue")
|
||||||
|
modal.find_element_by_xpath(
|
||||||
|
'//div[@id="modal"]//input[@type="submit"]').click()
|
||||||
|
|
||||||
|
# See it is selected
|
||||||
|
wait.until(animation_is_finished())
|
||||||
|
self.assertFalse(modal.is_displayed())
|
||||||
|
obj = models.Venue.objects.get(name="Test Venue")
|
||||||
|
self.assertEqual(obj.name, form.find_element_by_xpath(
|
||||||
|
'//button[@data-id="id_venue"]/span').text)
|
||||||
|
# and backend
|
||||||
|
option = form.find_element_by_xpath(
|
||||||
|
'//select[@id="id_venue"]//option[@selected="selected"]')
|
||||||
|
self.assertEqual(obj.pk, int(option.get_attribute("value")))
|
||||||
|
|
||||||
|
# Set start date/time
|
||||||
|
form.find_element_by_id('id_start_date').send_keys('3015-05-25')
|
||||||
|
form.find_element_by_id('id_start_time').send_keys('06:59')
|
||||||
|
|
||||||
|
# Set end date/time
|
||||||
|
form.find_element_by_id('id_end_date').send_keys('4000-06-27')
|
||||||
|
form.find_element_by_id('id_end_time').send_keys('07:00')
|
||||||
|
|
||||||
|
# Add item
|
||||||
|
form.find_element_by_xpath('//button[contains(@class, "item-add")]').click()
|
||||||
|
wait.until(animation_is_finished())
|
||||||
|
modal = self.browser.find_element_by_id("itemModal")
|
||||||
|
modal.find_element_by_id("item_name").send_keys("Test Item 1")
|
||||||
|
modal.find_element_by_id("item_description").send_keys("This is an item description\nthat for reasons unkown spans two lines")
|
||||||
|
e = modal.find_element_by_id("item_quantity")
|
||||||
|
e.click()
|
||||||
|
e.send_keys(Keys.UP)
|
||||||
|
e.send_keys(Keys.UP)
|
||||||
|
e = modal.find_element_by_id("item_cost")
|
||||||
|
e.send_keys("23.95")
|
||||||
|
e.send_keys(Keys.ENTER) # enter submit
|
||||||
|
|
||||||
|
# Confirm item has been saved to json field
|
||||||
|
objectitems = self.browser.execute_script("return objectitems;")
|
||||||
|
self.assertEqual(1, len(objectitems))
|
||||||
|
testitem = objectitems["-1"]['fields'] # as we are deliberately creating this we know the ID
|
||||||
|
self.assertEqual("Test Item 1", testitem['name'])
|
||||||
|
self.assertEqual("2", testitem['quantity']) # test a couple of "worse case" fields
|
||||||
|
|
||||||
|
# See new item appear in table
|
||||||
|
row = self.browser.find_element_by_id('item--1') # ID number is known, see above
|
||||||
|
self.assertIn("Test Item 1", row.find_element_by_xpath('//span[@class="name"]').text)
|
||||||
|
self.assertIn("This is an item description", row.find_element_by_xpath('//div[@class="item-description"]').text)
|
||||||
|
self.assertEqual(u'£ 23.95', row.find_element_by_xpath('//tr[@id="item--1"]/td[2]').text)
|
||||||
|
self.assertEqual("2", row.find_element_by_xpath('//td[@class="quantity"]').text)
|
||||||
|
self.assertEqual(u'£ 47.90', row.find_element_by_xpath('//tr[@id="item--1"]/td[4]').text)
|
||||||
|
|
||||||
|
# Check totals
|
||||||
|
self.assertEqual("47.90", self.browser.find_element_by_id('sumtotal').text)
|
||||||
|
self.assertIn("(TBC)", self.browser.find_element_by_id('vat-rate').text)
|
||||||
|
self.assertEqual("9.58", self.browser.find_element_by_id('vat').text)
|
||||||
|
self.assertEqual("57.48", self.browser.find_element_by_id('total').text)
|
||||||
|
|
||||||
|
# Attempt to save - missing title
|
||||||
|
save.click()
|
||||||
|
|
||||||
|
# See error
|
||||||
|
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
|
||||||
|
self.assertTrue(error.is_displayed())
|
||||||
|
# Should only have one error message
|
||||||
|
self.assertEqual("Name", error.find_element_by_xpath('//dt[1]').text)
|
||||||
|
self.assertEqual("This field is required.", error.find_element_by_xpath('//dd[1]/ul/li').text)
|
||||||
|
# don't need error so close it
|
||||||
|
error.find_element_by_xpath('//div[contains(@class, "alert-danger")]//button[@class="close"]').click()
|
||||||
|
try:
|
||||||
|
self.assertFalse(error.is_displayed())
|
||||||
|
except StaleElementReferenceException:
|
||||||
|
pass
|
||||||
|
except:
|
||||||
|
self.assertFail("Element does not appear to have been deleted")
|
||||||
|
|
||||||
|
# Check at least some data is preserved. Some = all will be there
|
||||||
|
option = self.browser.find_element_by_xpath(
|
||||||
|
'//select[@id="id_person"]//option[@selected="selected"]')
|
||||||
|
self.assertEqual(person1.pk, int(option.get_attribute("value")))
|
||||||
|
|
||||||
|
# Set title
|
||||||
|
e = self.browser.find_element_by_id('id_name')
|
||||||
|
e.send_keys('Test Event Name')
|
||||||
|
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, 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
|
pass
|
||||||
except:
|
|
||||||
self.assertFail("Element does not appear to have been deleted")
|
|
||||||
|
|
||||||
# Check at least some data is preserved. Some = all will be there
|
|
||||||
option = self.browser.find_element_by_xpath(
|
|
||||||
'//select[@id="id_person"]//option[@selected="selected"]')
|
|
||||||
self.assertEqual(person1.pk, int(option.get_attribute("value")))
|
|
||||||
|
|
||||||
# Set title
|
|
||||||
e = self.browser.find_element_by_id('id_name')
|
|
||||||
e.send_keys('Test Event Name')
|
|
||||||
e.send_keys(Keys.ENTER)
|
|
||||||
|
|
||||||
# See redirected to success page
|
|
||||||
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)
|
|
||||||
|
|
||||||
def testEventDuplicate(self):
|
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")
|
testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL, start_date=date.today() + timedelta(days=6), description="start future no end")
|
||||||
@@ -610,8 +621,9 @@ class EventTest(LiveServerTestCase):
|
|||||||
save.click()
|
save.click()
|
||||||
|
|
||||||
# See redirected to success page
|
# See redirected to success page
|
||||||
|
successTitle = self.browser.find_element_by_xpath('//h1').text
|
||||||
event = models.Event.objects.get(name='Test Event Name')
|
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):
|
def testRigNonRig(self):
|
||||||
self.browser.get(self.live_server_url + '/event/create/')
|
self.browser.get(self.live_server_url + '/event/create/')
|
||||||
@@ -752,8 +764,6 @@ class IcalTest(LiveServerTestCase):
|
|||||||
|
|
||||||
username.send_keys("EventTest")
|
username.send_keys("EventTest")
|
||||||
password.send_keys("EventTestPassword")
|
password.send_keys("EventTestPassword")
|
||||||
self.browser.execute_script(
|
|
||||||
"return jQuery('#g-recaptcha-response').val('PASSED')")
|
|
||||||
submit.click()
|
submit.click()
|
||||||
|
|
||||||
self.assertEqual(self.live_server_url + n, self.browser.current_url)
|
self.assertEqual(self.live_server_url + n, self.browser.current_url)
|
||||||
|
|||||||
@@ -1,233 +1,329 @@
|
|||||||
|
import pytz
|
||||||
|
from django.conf import settings
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from RIGS import models
|
from RIGS import models
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta, datetime, time
|
||||||
from decimal import *
|
from decimal import *
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileTestCase(TestCase):
|
||||||
|
def test_str(self):
|
||||||
|
profile = models.Profile(first_name='Test', last_name='Case')
|
||||||
|
self.assertEqual(str(profile), 'Test Case')
|
||||||
|
profile.initials = 'TC'
|
||||||
|
self.assertEqual(str(profile), 'Test Case "TC"')
|
||||||
|
|
||||||
|
|
||||||
class VatRateTestCase(TestCase):
|
class VatRateTestCase(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
models.VatRate.objects.create(start_at='2014-03-01',rate=0.20,comment='test1')
|
models.VatRate.objects.create(start_at='2014-03-01', rate=0.20, comment='test1')
|
||||||
models.VatRate.objects.create(start_at='2016-03-01',rate=0.15,comment='test2')
|
models.VatRate.objects.create(start_at='2016-03-01', rate=0.15, comment='test2')
|
||||||
|
|
||||||
def test_find_correct(self):
|
def test_find_correct(self):
|
||||||
r = models.VatRate.objects.find_rate('2015-03-01')
|
r = models.VatRate.objects.find_rate('2015-03-01')
|
||||||
self.assertEqual(r.comment, 'test1')
|
self.assertEqual(r.comment, 'test1')
|
||||||
r = models.VatRate.objects.find_rate('2016-03-01')
|
r = models.VatRate.objects.find_rate('2016-03-01')
|
||||||
self.assertEqual(r.comment, 'test2')
|
self.assertEqual(r.comment, 'test2')
|
||||||
|
|
||||||
|
def test_percent_correct(self):
|
||||||
|
r = models.VatRate.objects.get(rate=0.20)
|
||||||
|
self.assertEqual(r.as_percent, 20)
|
||||||
|
|
||||||
def test_percent_correct(self):
|
|
||||||
r = models.VatRate.objects.get(rate=0.20)
|
|
||||||
self.assertEqual(r.as_percent, 20)
|
|
||||||
|
|
||||||
class EventTestCase(TestCase):
|
class EventTestCase(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.all_events = set(range(1, 18))
|
self.all_events = set(range(1, 18))
|
||||||
self.current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18)
|
self.current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18)
|
||||||
self.not_current_events = set(self.all_events) - set(self.current_events)
|
self.not_current_events = set(self.all_events) - set(self.current_events)
|
||||||
|
|
||||||
self.vatrate = models.VatRate.objects.create(start_at='2014-03-05',rate=0.20,comment='test1')
|
self.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
|
||||||
self.profile = models.Profile.objects.create(username="testuser1", email="1@test.com")
|
self.profile = models.Profile.objects.create(username="testuser1", email="1@test.com")
|
||||||
|
|
||||||
# produce 7 normal events - 5 current
|
# produce 7 normal events - 5 current
|
||||||
models.Event.objects.create(name="TE E1", start_date=date.today() + timedelta(days=6), description="start future no end")
|
models.Event.objects.create(name="TE E1", start_date=date.today() + timedelta(days=6),
|
||||||
models.Event.objects.create(name="TE E2", start_date=date.today(), description="start today no end")
|
description="start future no end")
|
||||||
models.Event.objects.create(name="TE E3", start_date=date.today(), end_date=date.today(), description="start today with end today")
|
models.Event.objects.create(name="TE E2", start_date=date.today(), description="start today no end")
|
||||||
models.Event.objects.create(name="TE E4", start_date='2014-03-20', description="start past no end")
|
models.Event.objects.create(name="TE E3", start_date=date.today(), end_date=date.today(),
|
||||||
models.Event.objects.create(name="TE E5", start_date='2014-03-20', end_date='2014-03-21', description="start past with end past")
|
description="start today with end today")
|
||||||
models.Event.objects.create(name="TE E6", start_date=date.today()-timedelta(days=2), end_date=date.today()+timedelta(days=2), description="start past, end future")
|
models.Event.objects.create(name="TE E4", start_date='2014-03-20', description="start past no end")
|
||||||
models.Event.objects.create(name="TE E7", start_date=date.today()+timedelta(days=2), end_date=date.today()+timedelta(days=2), description="start + end in future")
|
models.Event.objects.create(name="TE E5", start_date='2014-03-20', end_date='2014-03-21',
|
||||||
|
description="start past with end past")
|
||||||
|
models.Event.objects.create(name="TE E6", start_date=date.today() - timedelta(days=2),
|
||||||
|
end_date=date.today() + timedelta(days=2), description="start past, end future")
|
||||||
|
models.Event.objects.create(name="TE E7", start_date=date.today() + timedelta(days=2),
|
||||||
|
end_date=date.today() + timedelta(days=2), description="start + end in future")
|
||||||
|
|
||||||
# 2 cancelled - 1 current
|
# 2 cancelled - 1 current
|
||||||
models.Event.objects.create(name="TE E8", start_date=date.today()+timedelta(days=2), end_date=date.today()+timedelta(days=2), status=models.Event.CANCELLED, description="cancelled in future")
|
models.Event.objects.create(name="TE E8", start_date=date.today() + timedelta(days=2),
|
||||||
models.Event.objects.create(name="TE E9", start_date=date.today()-timedelta(days=1), end_date=date.today()+timedelta(days=2), status=models.Event.CANCELLED, description="cancelled and started")
|
end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED,
|
||||||
|
description="cancelled in future")
|
||||||
|
models.Event.objects.create(name="TE E9", start_date=date.today() - timedelta(days=1),
|
||||||
|
end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED,
|
||||||
|
description="cancelled and started")
|
||||||
|
|
||||||
# 5 dry hire - 3 current
|
# 5 dry hire - 3 current
|
||||||
models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True, description="dryhire today")
|
models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True, description="dryhire today")
|
||||||
models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True, checked_in_by=self.profile, description="dryhire today, checked in")
|
models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True, checked_in_by=self.profile,
|
||||||
models.Event.objects.create(name="TE E12", start_date=date.today()-timedelta(days=1), dry_hire=True, status=models.Event.BOOKED, description="dryhire past")
|
description="dryhire today, checked in")
|
||||||
models.Event.objects.create(name="TE E13", start_date=date.today()-timedelta(days=2), dry_hire=True, checked_in_by=self.profile, description="dryhire past checked in")
|
models.Event.objects.create(name="TE E12", start_date=date.today() - timedelta(days=1), dry_hire=True,
|
||||||
models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True, status=models.Event.CANCELLED, description="dryhire today cancelled")
|
status=models.Event.BOOKED, description="dryhire past")
|
||||||
|
models.Event.objects.create(name="TE E13", start_date=date.today() - timedelta(days=2), dry_hire=True,
|
||||||
|
checked_in_by=self.profile, description="dryhire past checked in")
|
||||||
|
models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True,
|
||||||
|
status=models.Event.CANCELLED, description="dryhire today cancelled")
|
||||||
|
|
||||||
# 4 non rig - 3 current
|
# 4 non rig - 3 current
|
||||||
models.Event.objects.create(name="TE E15", start_date=date.today(), is_rig=False, description="non rig today")
|
models.Event.objects.create(name="TE E15", start_date=date.today(), is_rig=False, description="non rig today")
|
||||||
models.Event.objects.create(name="TE E16", start_date=date.today()+timedelta(days=1), is_rig=False, description="non rig tomorrow")
|
models.Event.objects.create(name="TE E16", start_date=date.today() + timedelta(days=1), is_rig=False,
|
||||||
models.Event.objects.create(name="TE E17", start_date=date.today()-timedelta(days=1), is_rig=False, description="non rig yesterday")
|
description="non rig tomorrow")
|
||||||
models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False, status=models.Event.CANCELLED, description="non rig today cancelled")
|
models.Event.objects.create(name="TE E17", start_date=date.today() - timedelta(days=1), is_rig=False,
|
||||||
|
description="non rig yesterday")
|
||||||
|
models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False, status=models.Event.CANCELLED,
|
||||||
|
description="non rig today cancelled")
|
||||||
|
|
||||||
def test_count(self):
|
def test_count(self):
|
||||||
# Santiy check we have the expected events created
|
# Santiy check we have the expected events created
|
||||||
self.assertEqual(models.Event.objects.count(), 18, "Incorrect number of events, check setup")
|
self.assertEqual(models.Event.objects.count(), 18, "Incorrect number of events, check setup")
|
||||||
|
|
||||||
def test_rig_count(self):
|
def test_rig_count(self):
|
||||||
# by my count this is 7
|
# by my count this is 7
|
||||||
self.assertEqual(models.Event.objects.rig_count(), 8)
|
self.assertEqual(models.Event.objects.rig_count(), 8)
|
||||||
|
|
||||||
def test_current_events(self):
|
def test_current_events(self):
|
||||||
current_events = models.Event.objects.current_events()
|
current_events = models.Event.objects.current_events()
|
||||||
self.assertEqual(len(current_events), len(self.current_events))
|
self.assertEqual(len(current_events), len(self.current_events))
|
||||||
for eid in self.current_events:
|
for eid in self.current_events:
|
||||||
self.assertIn(models.Event.objects.get(name="TE E%d"%eid), current_events)
|
self.assertIn(models.Event.objects.get(name="TE E%d" % eid), current_events)
|
||||||
|
|
||||||
for eid in self.not_current_events:
|
for eid in self.not_current_events:
|
||||||
self.assertNotIn(models.Event.objects.get(name="TE E%d"%eid), current_events)
|
self.assertNotIn(models.Event.objects.get(name="TE E%d" % eid), current_events)
|
||||||
|
|
||||||
def test_related_venue(self):
|
def test_related_venue(self):
|
||||||
v1 = models.Venue.objects.create(name="TE V1")
|
v1 = models.Venue.objects.create(name="TE V1")
|
||||||
v2 = models.Venue.objects.create(name="TE V2")
|
v2 = models.Venue.objects.create(name="TE V2")
|
||||||
events = models.Event.objects.all()
|
events = models.Event.objects.all()
|
||||||
for event in events[:2]:
|
for event in events[:2]:
|
||||||
event.venue = v1
|
event.venue = v1
|
||||||
event.save()
|
event.save()
|
||||||
for event in events[3:4]:
|
for event in events[3:4]:
|
||||||
event.venue = v2
|
event.venue = v2
|
||||||
event.save()
|
event.save()
|
||||||
|
|
||||||
events = models.Event.objects.all()
|
events = models.Event.objects.all()
|
||||||
self.assertItemsEqual(events[:2], v1.latest_events)
|
self.assertItemsEqual(events[:2], v1.latest_events)
|
||||||
self.assertItemsEqual(events[3:4], v2.latest_events)
|
self.assertItemsEqual(events[3:4], v2.latest_events)
|
||||||
|
|
||||||
def test_related_vatrate(self):
|
def test_related_vatrate(self):
|
||||||
self.assertEqual(self.vatrate, models.Event.objects.all()[0].vat_rate)
|
self.assertEqual(self.vatrate, models.Event.objects.all()[0].vat_rate)
|
||||||
|
|
||||||
def test_related_person(self):
|
def test_related_person(self):
|
||||||
p1 = models.Person.objects.create(name="TE P1")
|
p1 = models.Person.objects.create(name="TE P1")
|
||||||
p2 = models.Person.objects.create(name="TE P2")
|
p2 = models.Person.objects.create(name="TE P2")
|
||||||
|
|
||||||
events = models.Event.objects.all()
|
events = models.Event.objects.all()
|
||||||
for event in events[:2]:
|
for event in events[:2]:
|
||||||
event.person = p1
|
event.person = p1
|
||||||
event.save()
|
event.save()
|
||||||
for event in events[3:4]:
|
for event in events[3:4]:
|
||||||
event.person = p2
|
event.person = p2
|
||||||
event.save()
|
event.save()
|
||||||
|
|
||||||
events = models.Event.objects.all()
|
events = models.Event.objects.all()
|
||||||
self.assertItemsEqual(events[:2], p1.latest_events)
|
self.assertItemsEqual(events[:2], p1.latest_events)
|
||||||
self.assertItemsEqual(events[3:4], p2.latest_events)
|
self.assertItemsEqual(events[3:4], p2.latest_events)
|
||||||
|
|
||||||
def test_related_organisation(self):
|
def test_related_organisation(self):
|
||||||
o1 = models.Organisation.objects.create(name="TE O1")
|
o1 = models.Organisation.objects.create(name="TE O1")
|
||||||
o2 = models.Organisation.objects.create(name="TE O2")
|
o2 = models.Organisation.objects.create(name="TE O2")
|
||||||
|
|
||||||
events = models.Event.objects.all()
|
events = models.Event.objects.all()
|
||||||
for event in events[:2]:
|
for event in events[:2]:
|
||||||
event.organisation = o1
|
event.organisation = o1
|
||||||
event.save()
|
event.save()
|
||||||
for event in events[3:4]:
|
for event in events[3:4]:
|
||||||
event.organisation = o2
|
event.organisation = o2
|
||||||
event.save()
|
event.save()
|
||||||
|
|
||||||
events = models.Event.objects.all()
|
events = models.Event.objects.all()
|
||||||
self.assertItemsEqual(events[:2], o1.latest_events)
|
self.assertItemsEqual(events[:2], o1.latest_events)
|
||||||
self.assertItemsEqual(events[3:4], o2.latest_events)
|
self.assertItemsEqual(events[3:4], o2.latest_events)
|
||||||
|
|
||||||
def test_organisation_person_join(self):
|
def test_organisation_person_join(self):
|
||||||
p1 = models.Person.objects.create(name="TE P1")
|
p1 = models.Person.objects.create(name="TE P1")
|
||||||
p2 = models.Person.objects.create(name="TE P2")
|
p2 = models.Person.objects.create(name="TE P2")
|
||||||
o1 = models.Organisation.objects.create(name="TE O1")
|
o1 = models.Organisation.objects.create(name="TE O1")
|
||||||
o2 = models.Organisation.objects.create(name="TE O2")
|
o2 = models.Organisation.objects.create(name="TE O2")
|
||||||
|
|
||||||
events = models.Event.objects.all()
|
events = models.Event.objects.all()
|
||||||
# p1 in o1 + o2, p2 in o1
|
# p1 in o1 + o2, p2 in o1
|
||||||
for event in events[:2]:
|
for event in events[:2]:
|
||||||
event.person = p1
|
event.person = p1
|
||||||
event.organisation = o1
|
event.organisation = o1
|
||||||
event.save()
|
event.save()
|
||||||
for event in events[3:4]:
|
for event in events[3:4]:
|
||||||
event.person = p1
|
event.person = p1
|
||||||
event.organisation = o2
|
event.organisation = o2
|
||||||
event.save()
|
event.save()
|
||||||
for event in events[5:7]:
|
for event in events[5:7]:
|
||||||
event.person = p2
|
event.person = p2
|
||||||
event.organisation = o1
|
event.organisation = o1
|
||||||
event.save()
|
event.save()
|
||||||
|
|
||||||
events = models.Event.objects.all()
|
events = models.Event.objects.all()
|
||||||
|
|
||||||
# Check person's organisations
|
# Check person's organisations
|
||||||
self.assertIn((o1,2), p1.organisations)
|
self.assertIn((o1, 2), p1.organisations)
|
||||||
self.assertIn((o2,1), p1.organisations)
|
self.assertIn((o2, 1), p1.organisations)
|
||||||
self.assertIn((o1,2), p2.organisations)
|
self.assertIn((o1, 2), p2.organisations)
|
||||||
self.assertEqual(len(p2.organisations), 1)
|
self.assertEqual(len(p2.organisations), 1)
|
||||||
|
|
||||||
# Check organisation's persons
|
# Check organisation's persons
|
||||||
self.assertIn((p1,2), o1.persons)
|
self.assertIn((p1, 2), o1.persons)
|
||||||
self.assertIn((p2,2), o1.persons)
|
self.assertIn((p2, 2), o1.persons)
|
||||||
self.assertIn((p1,1), o2.persons)
|
self.assertIn((p1, 1), o2.persons)
|
||||||
self.assertEqual(len(o2.persons),1)
|
self.assertEqual(len(o2.persons), 1)
|
||||||
|
|
||||||
def test_cancelled_property(self):
|
def test_cancelled_property(self):
|
||||||
event = models.Event.objects.all()[0]
|
event = models.Event.objects.all()[0]
|
||||||
event.status = models.Event.CANCELLED
|
event.status = models.Event.CANCELLED
|
||||||
event.save()
|
event.save()
|
||||||
event = models.Event.objects.all()[0]
|
event = models.Event.objects.all()[0]
|
||||||
self.assertEqual(event.status, models.Event.CANCELLED)
|
self.assertEqual(event.status, models.Event.CANCELLED)
|
||||||
self.assertTrue(event.cancelled)
|
self.assertTrue(event.cancelled)
|
||||||
event.status = models.Event.PROVISIONAL
|
event.status = models.Event.PROVISIONAL
|
||||||
event.save()
|
event.save()
|
||||||
|
|
||||||
|
def test_confirmed_property(self):
|
||||||
|
event = models.Event.objects.all()[0]
|
||||||
|
event.status = models.Event.CONFIRMED
|
||||||
|
event.save()
|
||||||
|
event = models.Event.objects.all()[0]
|
||||||
|
self.assertEqual(event.status, models.Event.CONFIRMED)
|
||||||
|
self.assertTrue(event.confirmed)
|
||||||
|
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))
|
||||||
|
|
||||||
def test_confirmed_property(self):
|
|
||||||
event = models.Event.objects.all()[0]
|
|
||||||
event.status = models.Event.CONFIRMED
|
|
||||||
event.save()
|
|
||||||
event = models.Event.objects.all()[0]
|
|
||||||
self.assertEqual(event.status, models.Event.CONFIRMED)
|
|
||||||
self.assertTrue(event.confirmed)
|
|
||||||
event.status = models.Event.PROVISIONAL
|
|
||||||
event.save()
|
|
||||||
|
|
||||||
class EventItemTestCase(TestCase):
|
class EventItemTestCase(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.e1 = models.Event.objects.create(name="TI E1", start_date=date.today())
|
self.e1 = models.Event.objects.create(name="TI E1", start_date=date.today())
|
||||||
self.e2 = models.Event.objects.create(name="TI E2", start_date=date.today())
|
self.e2 = models.Event.objects.create(name="TI E2", start_date=date.today())
|
||||||
|
|
||||||
def test_item_cost(self):
|
def test_item_cost(self):
|
||||||
item = models.EventItem.objects.create(event=self.e1, name="TI I1", quantity=1, cost=1.00, order=1)
|
item = models.EventItem.objects.create(event=self.e1, name="TI I1", quantity=1, cost=1.00, order=1)
|
||||||
self.assertEqual(item.total_cost, 1.00)
|
self.assertEqual(item.total_cost, 1.00)
|
||||||
|
|
||||||
item.cost = 2.50
|
item.cost = 2.50
|
||||||
self.assertEqual(item.total_cost, 2.50)
|
self.assertEqual(item.total_cost, 2.50)
|
||||||
|
|
||||||
item.quantity = 4
|
item.quantity = 4
|
||||||
self.assertEqual(item.total_cost, 10.00)
|
self.assertEqual(item.total_cost, 10.00)
|
||||||
|
|
||||||
# need to tidy up
|
# need to tidy up
|
||||||
item.delete()
|
item.delete()
|
||||||
|
|
||||||
def test_item_order(self):
|
def test_item_order(self):
|
||||||
i1 = models.EventItem.objects.create(event=self.e1, name="TI I1", quantity=1, cost=1.00, order=1)
|
i1 = models.EventItem.objects.create(event=self.e1, name="TI I1", quantity=1, cost=1.00, order=1)
|
||||||
i2 = models.EventItem.objects.create(event=self.e1, name="TI I2", quantity=1, cost=1.00, order=2)
|
i2 = models.EventItem.objects.create(event=self.e1, name="TI I2", quantity=1, cost=1.00, order=2)
|
||||||
|
|
||||||
|
items = self.e1.items.all()
|
||||||
|
self.assertListEqual([i1, i2], list(items))
|
||||||
|
|
||||||
items = self.e1.items.all()
|
|
||||||
self.assertListEqual([i1, i2], list(items))
|
|
||||||
|
|
||||||
class EventPricingTestCase(TestCase):
|
class EventPricingTestCase(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
models.VatRate.objects.create(rate=0.20, comment="TP V1", start_at='2013-01-01')
|
models.VatRate.objects.create(rate=0.20, comment="TP V1", start_at='2013-01-01')
|
||||||
models.VatRate.objects.create(rate=0.10, comment="TP V2", start_at=date.today()-timedelta(days=1))
|
models.VatRate.objects.create(rate=0.10, comment="TP V2", start_at=date.today() - timedelta(days=1))
|
||||||
self.e1 = models.Event.objects.create(name="TP E1", start_date=date.today()-timedelta(days=2))
|
self.e1 = models.Event.objects.create(name="TP E1", start_date=date.today() - timedelta(days=2))
|
||||||
self.e2 = models.Event.objects.create(name="TP E2", start_date=date.today())
|
self.e2 = models.Event.objects.create(name="TP E2", start_date=date.today())
|
||||||
|
|
||||||
# Create some items E1, total 70.40
|
# Create some items E1, total 70.40
|
||||||
# Create some items E2, total 381.20
|
# Create some items E2, total 381.20
|
||||||
self.i1 = models.EventItem.objects.create(event=self.e1, name="TP I1", quantity=1, cost=50.00, order=1)
|
self.i1 = models.EventItem.objects.create(event=self.e1, name="TP I1", quantity=1, cost=50.00, order=1)
|
||||||
self.i2 = models.EventItem.objects.create(event=self.e1, name="TP I2", quantity=2, cost=3.20, order=2)
|
self.i2 = models.EventItem.objects.create(event=self.e1, name="TP I2", quantity=2, cost=3.20, order=2)
|
||||||
self.i3 = models.EventItem.objects.create(event=self.e1, name="TP I3", quantity=7, cost=2.00, order=3)
|
self.i3 = models.EventItem.objects.create(event=self.e1, name="TP I3", quantity=7, cost=2.00, order=3)
|
||||||
self.i4 = models.EventItem.objects.create(event=self.e2, name="TP I4", quantity=2, cost=190.60, order=1)
|
self.i4 = models.EventItem.objects.create(event=self.e2, name="TP I4", quantity=2, cost=190.60, order=1)
|
||||||
|
|
||||||
# Decimal type is needed here as that is what is returned from the model.
|
# Decimal type is needed here as that is what is returned from the model.
|
||||||
# Using anything else results in a failure due to floating point arritmetic
|
# Using anything else results in a failure due to floating point arritmetic
|
||||||
def test_sum_totals(self):
|
def test_sum_totals(self):
|
||||||
self.assertEqual(self.e1.sum_total, Decimal('70.40'))
|
self.assertEqual(self.e1.sum_total, Decimal('70.40'))
|
||||||
self.assertEqual(self.e2.sum_total, Decimal('381.20'))
|
self.assertEqual(self.e2.sum_total, Decimal('381.20'))
|
||||||
|
|
||||||
def test_vat_rate(self):
|
def test_vat_rate(self):
|
||||||
self.assertEqual(self.e1.vat_rate.rate, Decimal('0.20'))
|
self.assertEqual(self.e1.vat_rate.rate, Decimal('0.20'))
|
||||||
self.assertEqual(self.e2.vat_rate.rate, Decimal('0.10'))
|
self.assertEqual(self.e2.vat_rate.rate, Decimal('0.10'))
|
||||||
|
|
||||||
def test_vat_ammount(self):
|
def test_vat_ammount(self):
|
||||||
self.assertEqual(self.e1.vat, Decimal('14.08'))
|
self.assertEqual(self.e1.vat, Decimal('14.08'))
|
||||||
self.assertEqual(self.e2.vat, Decimal('38.12'))
|
self.assertEqual(self.e2.vat, Decimal('38.12'))
|
||||||
|
|
||||||
def test_grand_total(self):
|
def test_grand_total(self):
|
||||||
self.assertEqual(self.e1.total, Decimal('84.48'))
|
self.assertEqual(self.e1.total, Decimal('84.48'))
|
||||||
self.assertEqual(self.e2.total, Decimal('419.32'))
|
self.assertEqual(self.e2.total, Decimal('419.32'))
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ urlpatterns = patterns('',
|
|||||||
# Rigboard
|
# Rigboard
|
||||||
url(r'^rigboard/$', login_required(rigboard.RigboardIndex.as_view()), name='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/$', 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/archive/$', RedirectView.as_view(permanent=True,pattern_name='event_archive')),
|
||||||
url(r'^rigboard/activity/$',
|
url(r'^rigboard/activity/$',
|
||||||
permission_required_with_403('RIGS.view_event')(versioning.ActivityTable.as_view()),
|
permission_required_with_403('RIGS.view_event')(versioning.ActivityTable.as_view()),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from reversion.models import Version
|
|||||||
from django.contrib.contenttypes.models import ContentType # Used to lookup the content_type
|
from django.contrib.contenttypes.models import ContentType # Used to lookup the content_type
|
||||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||||
from django.db.models import ForeignKey, IntegerField, EmailField, TextField
|
from django.db.models import ForeignKey, IntegerField, EmailField, TextField
|
||||||
|
from diff_match_patch import diff_match_patch
|
||||||
|
|
||||||
from RIGS import models, forms
|
from RIGS import models, forms
|
||||||
import datetime
|
import datetime
|
||||||
@@ -64,6 +65,25 @@ def model_compare(oldObj, newObj, excluded_keys=[]):
|
|||||||
return True
|
return True
|
||||||
return False
|
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 = []
|
changes = []
|
||||||
|
|
||||||
for thisField in theFields:
|
for thisField in theFields:
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ def login(request, **kwargs):
|
|||||||
else:
|
else:
|
||||||
from django.contrib.auth.views import login
|
from django.contrib.auth.views import login
|
||||||
|
|
||||||
return login(request, authentication_form=forms.LoginForm)
|
return login(request)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Called from a modal window (e.g. when an item is submitted to an event/invoice).
|
Called from a modal window (e.g. when an item is submitted to an event/invoice).
|
||||||
|
|||||||
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
@@ -1,4 +1,5 @@
|
|||||||
beautifulsoup4==4.4.1
|
beautifulsoup4==4.4.1
|
||||||
|
diff-match-patch==20121119
|
||||||
dj-database-url==0.3.0
|
dj-database-url==0.3.0
|
||||||
dj-static==0.0.6
|
dj-static==0.0.6
|
||||||
Django==1.8.2
|
Django==1.8.2
|
||||||
@@ -21,7 +22,7 @@ python-dateutil==2.4.2
|
|||||||
pytz==2015.4
|
pytz==2015.4
|
||||||
raven==5.8.1
|
raven==5.8.1
|
||||||
reportlab==3.1.44
|
reportlab==3.1.44
|
||||||
selenium==2.46.0
|
selenium==2.53.1
|
||||||
simplejson==3.7.2
|
simplejson==3.7.2
|
||||||
six==1.9.0
|
six==1.9.0
|
||||||
sqlparse==0.1.15
|
sqlparse==0.1.15
|
||||||
|
|||||||
@@ -14,14 +14,16 @@
|
|||||||
|
|
||||||
<link rel="icon" type="image/png" href="{% static "imgs/pyrigs-avatar.png" %}">
|
<link rel="icon" type="image/png" href="{% static "imgs/pyrigs-avatar.png" %}">
|
||||||
<link rel="apple-touch-icon" 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" %}">
|
<link rel="stylesheet" type="text/css" href="{% static "css/screen.css" %}">
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{% endblock %}
|
{% 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 src="https://cdn.ravenjs.com/1.3.0/jquery,native/raven.min.js"></script>
|
||||||
<script>Raven.config('{% sentry_public_dsn %}').install()</script>
|
<script>Raven.config('{% sentry_public_dsn %}').install()</script>
|
||||||
{% block preload_js %}
|
{% block preload_js %}
|
||||||
@@ -46,32 +48,40 @@
|
|||||||
<div class="navbar-collapse">
|
<div class="navbar-collapse">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/">Home</a></li>
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Rigboard<b class="caret"></b></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Rigboard<b class="caret"></b></a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="{% url 'rigboard' %}"><span class="glyphicon glyphicon-list"></span> Rigboard</a></li>
|
<li><a href="{% url 'rigboard' %}"><span class="glyphicon glyphicon-list"></span>
|
||||||
<li><a href="{% url 'event_archive' %}"><span class="glyphicon glyphicon-book"></span> Archive</a></li>
|
Rigboard</a></li>
|
||||||
<li><a href="{% url 'web_calendar' %}"><span class="glyphicon glyphicon-calendar"></span> Calendar</a></li>
|
<li><a href="{% url 'event_archive' %}"><span class="glyphicon glyphicon-book"></span>
|
||||||
{% if perms.RIGS.view_event %}
|
Archive</a></li>
|
||||||
<li><a href="{% url 'activity_table' %}"><span class="glyphicon glyphicon-random"></span> Recent Changes</a></li>
|
<li><a href="{% url 'web_calendar' %}"><span class="glyphicon glyphicon-calendar"></span>
|
||||||
{% endif %}
|
Calendar</a></li>
|
||||||
{% if perms.RIGS.add_event %}
|
{% if perms.RIGS.view_event %}
|
||||||
<li><a href="{% url 'event_create' %}"><span class="glyphicon glyphicon-plus"></span> New Event</a></li>
|
<li><a href="{% url 'activity_table' %}"><span
|
||||||
{% endif %}
|
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>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.RIGS.view_invoice %}
|
{% if perms.RIGS.view_invoice %}
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Invoices<b class="caret"></b></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Invoices<b class="caret"></b></a>
|
||||||
<ul class="dropdown-menu">
|
<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 %}
|
{% 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 %}
|
{% 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>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -144,10 +154,6 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<div class="col-sm-12 text-center">
|
|
||||||
Reminder: Please consider carefully before booking rigs at this moment
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -182,7 +188,7 @@
|
|||||||
jQuery(document).on('click', '.modal-href', function (e) {
|
jQuery(document).on('click', '.modal-href', function (e) {
|
||||||
$link = jQuery(this);
|
$link = jQuery(this);
|
||||||
// Anti modal inception
|
// Anti modal inception
|
||||||
if($link.parents('#modal').length == 0) {
|
if ($link.parents('#modal').length == 0) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
modaltarget = $link.data('target');
|
modaltarget = $link.data('target');
|
||||||
modalobject = "";
|
modalobject = "";
|
||||||
@@ -194,11 +200,11 @@
|
|||||||
|
|
||||||
|
|
||||||
var easter_egg = new Konami();
|
var easter_egg = new Konami();
|
||||||
easter_egg.code = function() {
|
easter_egg.code = function () {
|
||||||
var s = document.createElement('script');
|
var s = document.createElement('script');
|
||||||
s.type='text/javascript';
|
s.type = 'text/javascript';
|
||||||
document.body.appendChild(s);
|
document.body.appendChild(s);
|
||||||
s.src='{% static "js/asteroids.min.js"%}';
|
s.src = '{% static "js/asteroids.min.js"%}';
|
||||||
ga('send', 'event', 'easter_egg', 'activated');
|
ga('send', 'event', 'easter_egg', 'activated');
|
||||||
}
|
}
|
||||||
easter_egg.load();
|
easter_egg.load();
|
||||||
|
|||||||
@@ -6,18 +6,12 @@
|
|||||||
<form action="{% url 'login' %}" method="post" role="form">{% csrf_token %}
|
<form action="{% url 'login' %}" method="post" role="form">{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="id_username">{{ form.username.label }}</label>
|
<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>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="{{ form.password.id_for_label }}">{{ form.password.label }}</label>
|
<label for="{{ form.password.id_for_label }}">{{ form.password.label }}</label>
|
||||||
{% render_field form.password class+="form-control" placeholder=form.password.label %}
|
{% render_field form.password class+="form-control" placeholder=form.password.label %}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.captcha.id_for_label }}">{{ form.captcha.label }}</label>
|
|
||||||
<div class="text-center">
|
|
||||||
{{ form.captcha }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a href="{% url 'registration_register' %}" class="btn">Register</a>
|
<a href="{% url 'registration_register' %}" class="btn">Register</a>
|
||||||
<a href="{% url 'password_reset' %}" class="btn">Forgotten Password</a>
|
<a href="{% url 'password_reset' %}" class="btn">Forgotten Password</a>
|
||||||
<input type="submit" value="Login" class="btn btn-primary"/>
|
<input type="submit" value="Login" class="btn btn-primary"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user