mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-02-12 01:29:42 +00:00
Merge branch master into assets-testing
# Conflicts: # RIGS/test_functional.py
This commit is contained in:
16
README.md
16
README.md
@@ -1,20 +1,13 @@
|
|||||||
# TEC PA & Lighting - PyRIGS #
|
# TEC PA & Lighting - PyRIGS #
|
||||||
[](https://travis-ci.org/nottinghamtec/PyRIGS)
|
[](https://travis-ci.org/nottinghamtec/PyRIGS)
|
||||||
[](https://coveralls.io/github/nottinghamtec/PyRIGS?branch=develop)
|
[](https://coveralls.io/github/nottinghamtec/PyRIGS)
|
||||||
[](https://gemnasium.com/github.com/nottinghamtec/PyRIGS)
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
The purpose of this project is to make the system more compatible and easier to understand such that should future changes be needed they can be made without having to understand the intricacies of Rails.
|
The purpose of this project is to make the system more compatible and easier to understand such that should future changes be needed they can be made without having to understand the intricacies of Rails.
|
||||||
|
|
||||||
At this stage the project is very early on, and the main focus has been on getting a working system that can be tested and put into use ASAP due to the imminent failure of the existing system. Because of this, the documentation is still quite weak, but this should be fixed as time goes on.
|
|
||||||
|
|
||||||
This document is intended to get you up and running, but if don't care about what I have to say, just clone the sodding repository and have a poke around with what's in it, but for GODS SAKE DO NOT PUSH WITHOUT TESTING.
|
|
||||||
|
|
||||||
### What is this repository for? ###
|
### What is this repository for? ###
|
||||||
For the rapid development of the application for medium term deployment, the main branch is being used.
|
When a significant feature is developed on a branch, raise a pull request and it can be reviewed before being put into production.
|
||||||
Once the application is deployed in a production environment, other branches should be used to properly stage edits and pushes of new features. When a significant feature is developed on a branch, raise a pull request and it can be reviewed before being put into production.
|
|
||||||
|
|
||||||
Most of the documents here assume a basic knowledge of how Python and Django work (hint, if I don't say something, Google it, you will find 10000's of answers). The documentation is purely to be specific to TEC's application of the framework.
|
Most of the documents here assume a basic knowledge of how Python and Django work (hint, if I don't say something, Google it, you will find 10000's of answers). The documentation is purely to be specific to TEC's application of the framework.
|
||||||
|
|
||||||
@@ -26,7 +19,7 @@ For the more experienced developer/somebody who doesn't want a full IDE and want
|
|||||||
Please contact TJP for details on how to acquire these.
|
Please contact TJP for details on how to acquire these.
|
||||||
|
|
||||||
### Python Environment ###
|
### Python Environment ###
|
||||||
Whilst the Python version used is not critical to the running of the application, using the same version usually helps avoid a lot of issues. Mainly the C implementation of Python 2 (CPython 2) has been used (specifically the Python 2.7 standard). Most of the application has been written with Python 3 in mind however, and should run without issue. Some level of testing on Python 3 has been done, but there is no guarantee it will work (for more information on this please see [[Python Version]] on the wiki)
|
Whilst the Python version used is not critical to the running of the application, using the same version usually helps avoid a lot of issues. Orginally written with the C implementation of Python 2 (CPython 2, specifically the Python 2.7 standard), the application now runs in Python 3.
|
||||||
|
|
||||||
Once you have your Python distribution installed, go ahead an follow the steps to set up a virtualenv, which will isolate the project from the system environment.
|
Once you have your Python distribution installed, go ahead an follow the steps to set up a virtualenv, which will isolate the project from the system environment.
|
||||||
|
|
||||||
@@ -115,5 +108,4 @@ python manage.py test RIGS.test_models.EventTestCase.test_current_events
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Committing, pushing and testing ###
|
[](https://forthebadge.com) [](https://forthebadge.com)
|
||||||
Feel free to commit as you wish, on your own branch. On my branch (master for development) do not commit code that you either know doesn't work or don't know works. If you must commit this code, please make sure you say in the commit message that it isn't working, and if you can why it isn't working. If and only if you absolutely must push, then please don't leave it as the HEAD for too long, it's not much to ask but when you are done just make sure you haven't broken the HEAD for the next person.
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
|
|||||||
|
|
||||||
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')
|
||||||
|
|
||||||
def clean_initials(self):
|
def clean_initials(self):
|
||||||
"""
|
"""
|
||||||
@@ -129,6 +129,11 @@ class EventForm(forms.ModelForm):
|
|||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
if self.cleaned_data.get("is_rig") and not (self.cleaned_data.get('person') or self.cleaned_data.get('organisation')):
|
||||||
|
raise forms.ValidationError('You haven\'t provided any client contact details. Please add a person or organisation.', code='contact')
|
||||||
|
return super(EventForm, self).clean()
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
m = super(EventForm, self).save(commit=False)
|
m = super(EventForm, self).save(commit=False)
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ def user_created(sender, user, request, **kwargs):
|
|||||||
user.first_name = form.data['first_name']
|
user.first_name = form.data['first_name']
|
||||||
user.last_name = form.data['last_name']
|
user.last_name = form.data['last_name']
|
||||||
user.initials = form.data['initials']
|
user.initials = form.data['initials']
|
||||||
user.phone = form.data['phone']
|
# user.phone = form.data['phone']
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -140,15 +140,18 @@ class EventUpdate(generic.UpdateView):
|
|||||||
if value is not None and value != '':
|
if value is not None and value != '':
|
||||||
context[field] = model.objects.get(pk=value)
|
context[field] = model.objects.get(pk=value)
|
||||||
|
|
||||||
# If this event has already been emailed to a client, show a warning
|
|
||||||
if self.object.auth_request_at is not None:
|
|
||||||
messages.info(self.request, 'This event has already been sent to the client for authorisation, any changes you make will be visible to them immediately.')
|
|
||||||
|
|
||||||
if hasattr(self.object, 'authorised'):
|
|
||||||
messages.warning(self.request, 'This event has already been authorised by client, any changes to price will require reauthorisation.')
|
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def render_to_response(self, context, **response_kwargs):
|
||||||
|
if not hasattr(context, 'duplicate'):
|
||||||
|
# If this event has already been emailed to a client, show a warning
|
||||||
|
if self.object.auth_request_at is not None:
|
||||||
|
messages.info(self.request, 'This event has already been sent to the client for authorisation, any changes you make will be visible to them immediately.')
|
||||||
|
|
||||||
|
if hasattr(self.object, 'authorised'):
|
||||||
|
messages.warning(self.request, 'This event has already been authorised by client, any changes to price will require reauthorisation.')
|
||||||
|
return super(EventUpdate, self).render_to_response(context, **response_kwargs)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy('event_detail', kwargs={'pk': self.object.pk})
|
return reverse_lazy('event_detail', kwargs={'pk': self.object.pk})
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 66 KiB |
@@ -65,6 +65,15 @@ textarea {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dont-break-out {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
-webkit-hyphens: auto;
|
||||||
|
-ms-hyphens: auto;
|
||||||
|
-moz-hyphens: auto;
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.modal-dialog {
|
.modal-dialog {
|
||||||
z-index: inherit; // bug fix introduced in 52682ce
|
z-index: inherit; // bug fix introduced in 52682ce
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@
|
|||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
|
|
||||||
<dt>Event Description</dt>
|
<dt>Event Description</dt>
|
||||||
<dd>{{ event.description|linebreaksbr }}</dd>
|
<dd class="dont-break-out">{{ event.description|linebreaksbr }}</dd>
|
||||||
|
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
|
|
||||||
@@ -158,7 +158,15 @@
|
|||||||
</div>
|
</div>
|
||||||
{% if event.is_rig and event.internal %}
|
{% if event.is_rig and event.internal %}
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default
|
||||||
|
{% if object.authorised %}
|
||||||
|
panel-success
|
||||||
|
{% elif event.authorisation and event.authorisation.amount != event.total and event.authorisation.last_edited_at > event.auth_request_at %}
|
||||||
|
panel-warning
|
||||||
|
{% elif event.auth_request_to %}
|
||||||
|
panel-info
|
||||||
|
{% endif %}
|
||||||
|
">
|
||||||
<div class="panel-heading">Client Authorisation</div>
|
<div class="panel-heading">Client Authorisation</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<dl class="dl-horizontal col-sm-6">
|
<dl class="dl-horizontal col-sm-6">
|
||||||
@@ -188,7 +196,7 @@
|
|||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
<dt>Authorised at</dt>
|
<dt>Authorised at</dt>
|
||||||
<dd>{{ object.authorisation.last_edited_at }}</dd>
|
<dd>{{ object.authorisation.last_edited_at|date:"D d M Y H:i" }}</dd>
|
||||||
|
|
||||||
<dt>Authorised amount</dt>
|
<dt>Authorised amount</dt>
|
||||||
<dd>
|
<dd>
|
||||||
@@ -216,7 +224,7 @@
|
|||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="well well-sm">
|
<div class="well well-sm">
|
||||||
<h4>Notes</h4>
|
<h4>Notes</h4>
|
||||||
{{ event.notes|linebreaksbr }}
|
<div class="dont-break-out">{{ event.notes|linebreaksbr }}</div>
|
||||||
</div>
|
</div>
|
||||||
{% include 'RIGS/item_table.html' %}
|
{% include 'RIGS/item_table.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
{% for change in itemChange.field_changes %}
|
{% for change in itemChange.field_changes %}
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<h4 class="list-group-item-heading">{{ change.field.verbose_name }}</h4>
|
<h4 class="list-group-item-heading">{{ change.field.verbose_name }}</h4>
|
||||||
{% include "RIGS/version_changes_change.html" %}
|
<div class="dont-break-out">{% include "RIGS/version_changes_change.html" %}</div>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
{% if change.linebreaks and change.new and change.old %}
|
{% if change.linebreaks and change.new and change.old %}
|
||||||
{% for diff in change.diff %}
|
{% for diff in change.diff %}
|
||||||
{% if diff.type == "insert" %}
|
{% if diff.type == "insert" %}
|
||||||
<ins>{{ diff.text|linebreaksbr }}</ins>
|
<ins class="dont-break-out">{{ diff.text|linebreaksbr }}</ins>
|
||||||
{% elif diff.type == "delete" %}
|
{% elif diff.type == "delete" %}
|
||||||
<del>{{diff.text|linebreaksbr}}</del>
|
<del class="dont-break-out">{{diff.text|linebreaksbr}}</del>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span>{{diff.text|linebreaksbr}}</span>
|
<span class="dont-break-out">{{diff.text|linebreaksbr}}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import pytz
|
|
||||||
from datetime import date, time, datetime, timedelta
|
from datetime import date, time, datetime, timedelta
|
||||||
|
|
||||||
|
import pytz
|
||||||
from django.core import mail
|
from django.conf import settings
|
||||||
|
from django.core import mail, signing
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.http import HttpResponseBadRequest
|
from django.http import HttpResponseBadRequest
|
||||||
from django.test import LiveServerTestCase, TestCase
|
from django.test import LiveServerTestCase, TestCase
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
|
from django.urls import reverse
|
||||||
|
from reversion import revisions as reversion
|
||||||
from selenium import webdriver
|
from selenium import webdriver
|
||||||
from selenium.common.exceptions import StaleElementReferenceException, WebDriverException
|
from selenium.common.exceptions import StaleElementReferenceException
|
||||||
|
from selenium.webdriver.support import expected_conditions
|
||||||
from selenium.webdriver.common.keys import Keys
|
from selenium.webdriver.common.keys import Keys
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
|
|
||||||
@@ -63,8 +66,9 @@ class UserRegistrationTest(LiveServerTestCase):
|
|||||||
self.assertEqual(last_name.get_attribute('placeholder'), 'Last name')
|
self.assertEqual(last_name.get_attribute('placeholder'), 'Last name')
|
||||||
initials = self.browser.find_element_by_id('id_initials')
|
initials = self.browser.find_element_by_id('id_initials')
|
||||||
self.assertEqual(initials.get_attribute('placeholder'), 'Initials')
|
self.assertEqual(initials.get_attribute('placeholder'), 'Initials')
|
||||||
phone = self.browser.find_element_by_id('id_phone')
|
# No longer required for new users
|
||||||
self.assertEqual(phone.get_attribute('placeholder'), 'Phone')
|
# phone = self.browser.find_element_by_id('id_phone')
|
||||||
|
# self.assertEqual(phone.get_attribute('placeholder'), 'Phone')
|
||||||
|
|
||||||
# Fill the form out incorrectly
|
# Fill the form out incorrectly
|
||||||
username.send_keys('TestUsername')
|
username.send_keys('TestUsername')
|
||||||
@@ -75,7 +79,7 @@ class UserRegistrationTest(LiveServerTestCase):
|
|||||||
first_name.send_keys('John')
|
first_name.send_keys('John')
|
||||||
last_name.send_keys('Smith')
|
last_name.send_keys('Smith')
|
||||||
initials.send_keys('JS')
|
initials.send_keys('JS')
|
||||||
phone.send_keys('0123456789')
|
# phone.send_keys('0123456789')
|
||||||
self.browser.execute_script(
|
self.browser.execute_script(
|
||||||
"return function() {jQuery('#g-recaptcha-response').val('PASSED'); return 0}()")
|
"return function() {jQuery('#g-recaptcha-response').val('PASSED'); return 0}()")
|
||||||
|
|
||||||
@@ -153,7 +157,7 @@ class UserRegistrationTest(LiveServerTestCase):
|
|||||||
self.assertEqual(profileObject.first_name, 'John')
|
self.assertEqual(profileObject.first_name, 'John')
|
||||||
self.assertEqual(profileObject.last_name, 'Smith')
|
self.assertEqual(profileObject.last_name, 'Smith')
|
||||||
self.assertEqual(profileObject.initials, 'JS')
|
self.assertEqual(profileObject.initials, 'JS')
|
||||||
self.assertEqual(profileObject.phone, '0123456789')
|
# self.assertEqual(profileObject.phone, '0123456789')
|
||||||
self.assertEqual(profileObject.email, 'test@example.com')
|
self.assertEqual(profileObject.email, 'test@example.com')
|
||||||
|
|
||||||
# All is well
|
# All is well
|
||||||
@@ -208,254 +212,236 @@ 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):
|
||||||
try:
|
# Requests address
|
||||||
# Requests address
|
self.browser.get(self.live_server_url + '/event/create/')
|
||||||
self.browser.get(self.live_server_url + '/event/create/')
|
# Gets redirected to login and back
|
||||||
# Gets redirected to login and back
|
self.authenticate('/event/create/')
|
||||||
self.authenticate('/event/create/')
|
|
||||||
|
|
||||||
wait = WebDriverWait(self.browser, 3) # setup WebDriverWait to use later (to wait for animations)
|
wait = WebDriverWait(self.browser, 3) # setup WebDriverWait to use later (to wait for animations)
|
||||||
|
|
||||||
wait.until(animation_is_finished())
|
wait.until(animation_is_finished())
|
||||||
|
|
||||||
# Check has slided up correctly - second save button hidden
|
# Check has slided up correctly - second save button hidden
|
||||||
save = self.browser.find_element_by_xpath(
|
save = self.browser.find_element_by_xpath(
|
||||||
'(//button[@type="submit"])[3]')
|
'(//button[@type="submit"])[3]')
|
||||||
self.assertFalse(save.is_displayed())
|
self.assertFalse(save.is_displayed())
|
||||||
|
|
||||||
# Click Rig button
|
# Click Rig button
|
||||||
self.browser.find_element_by_xpath('//button[.="Rig"]').click()
|
self.browser.find_element_by_xpath('//button[.="Rig"]').click()
|
||||||
|
|
||||||
# Slider expands and save button visible
|
# Slider expands and save button visible
|
||||||
self.assertTrue(save.is_displayed())
|
self.assertTrue(save.is_displayed())
|
||||||
form = self.browser.find_element_by_tag_name('form')
|
form = self.browser.find_element_by_tag_name('form')
|
||||||
|
|
||||||
# Create new person
|
# For now, just check that HTML5 Client validation is in place TODO Test needs rewriting to properly test all levels of validation.
|
||||||
wait.until(animation_is_finished())
|
self.assertTrue(self.browser.find_element_by_id('id_name').get_attribute('required') is not None)
|
||||||
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
|
# Set title
|
||||||
modal = self.browser.find_element_by_id('modal')
|
e = self.browser.find_element_by_id('id_name')
|
||||||
wait.until(animation_is_finished())
|
e.send_keys('Test Event Name')
|
||||||
self.assertTrue(modal.is_displayed())
|
|
||||||
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
|
|
||||||
|
|
||||||
# Fill person form out and submit
|
# Create new person
|
||||||
modal.find_element_by_xpath(
|
wait.until(animation_is_finished())
|
||||||
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 1")
|
add_person_button = self.browser.find_element_by_xpath(
|
||||||
modal.find_element_by_xpath(
|
'//a[@data-target="#id_person" and contains(@href, "add")]')
|
||||||
'//div[@id="modal"]//input[@type="submit"]').click()
|
add_person_button.click()
|
||||||
wait.until(animation_is_finished())
|
|
||||||
self.assertFalse(modal.is_displayed())
|
|
||||||
|
|
||||||
# See new person selected
|
# See modal has opened
|
||||||
person1 = models.Person.objects.get(name="Test Person 1")
|
modal = self.browser.find_element_by_id('modal')
|
||||||
self.assertEqual(person1.name, form.find_element_by_xpath(
|
wait.until(animation_is_finished())
|
||||||
'//button[@data-id="id_person"]/span').text)
|
self.assertTrue(modal.is_displayed())
|
||||||
# and backend
|
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
|
||||||
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
|
# Fill person form out and submit
|
||||||
wait.until(animation_is_finished())
|
modal.find_element_by_xpath(
|
||||||
add_person_button.click()
|
'//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())
|
||||||
|
|
||||||
wait.until(animation_is_finished())
|
# See new person selected
|
||||||
self.assertTrue(modal.is_displayed())
|
person1 = models.Person.objects.get(name="Test Person 1")
|
||||||
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
|
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")))
|
||||||
|
|
||||||
modal.find_element_by_xpath(
|
# Change mind and add another
|
||||||
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 2")
|
wait.until(animation_is_finished())
|
||||||
modal.find_element_by_xpath(
|
add_person_button.click()
|
||||||
'//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")
|
wait.until(animation_is_finished())
|
||||||
self.assertEqual(person2.name, form.find_element_by_xpath(
|
self.assertTrue(modal.is_displayed())
|
||||||
'//button[@data-id="id_person"]/span').text)
|
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').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
|
modal.find_element_by_xpath(
|
||||||
person_select = form.find_element_by_xpath(
|
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 2")
|
||||||
'//button[@data-id="id_person"]')
|
modal.find_element_by_xpath(
|
||||||
person_select.send_keys(person1.name)
|
'//div[@id="modal"]//input[@type="submit"]').click()
|
||||||
person_dropped = form.find_element_by_xpath(
|
wait.until(animation_is_finished())
|
||||||
'//ul[contains(@class, "inner selectpicker")]//span[contains(text(), "%s")]' % person1.name)
|
self.assertFalse(modal.is_displayed())
|
||||||
person_dropped.click()
|
|
||||||
|
|
||||||
self.assertEqual(person1.name, form.find_element_by_xpath(
|
person2 = models.Person.objects.get(name="Test Person 2")
|
||||||
'//button[@data-id="id_person"]/span').text)
|
self.assertEqual(person2.name, form.find_element_by_xpath(
|
||||||
option = form.find_element_by_xpath(
|
'//button[@data-id="id_person"]/span').text)
|
||||||
'//select[@id="id_person"]//option[@selected="selected"]')
|
# Have to do this explcitly to force the wait for it to update
|
||||||
self.assertEqual(person1.pk, int(option.get_attribute("value")))
|
option = form.find_element_by_xpath(
|
||||||
|
'//select[@id="id_person"]//option[@selected="selected"]')
|
||||||
|
self.assertEqual(person2.pk, int(option.get_attribute("value")))
|
||||||
|
|
||||||
# Edit Person 1 to have a better name
|
# Was right the first time, change it back
|
||||||
form.find_element_by_xpath(
|
person_select = form.find_element_by_xpath(
|
||||||
'//a[@data-target="#id_person" and contains(@href, "%s/edit/")]' % person1.pk).click()
|
'//button[@data-id="id_person"]')
|
||||||
wait.until(animation_is_finished())
|
person_select.send_keys(person1.name)
|
||||||
self.assertTrue(modal.is_displayed())
|
person_dropped = form.find_element_by_xpath(
|
||||||
self.assertIn("Edit Person", modal.find_element_by_tag_name('h3').text)
|
'//ul[contains(@class, "dropdown-menu")]//span[contains(text(), "%s")]' % person1.name)
|
||||||
name = modal.find_element_by_xpath(
|
person_dropped.click()
|
||||||
'//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.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")))
|
||||||
|
|
||||||
self.assertFalse(modal.is_displayed())
|
# Edit Person 1 to have a better name
|
||||||
person1 = models.Person.objects.get(pk=person1.pk)
|
form.find_element_by_xpath(
|
||||||
self.assertEqual(person1.name, form.find_element_by_xpath(
|
'//a[@data-target="#id_person" and contains(@href, "%s/edit/")]' % person1.pk).click()
|
||||||
'//button[@data-id="id_person"]/span').text)
|
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)
|
||||||
|
|
||||||
# Create organisation
|
wait.until(animation_is_finished())
|
||||||
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
|
self.assertFalse(modal.is_displayed())
|
||||||
wait.until(animation_is_finished())
|
person1 = models.Person.objects.get(pk=person1.pk)
|
||||||
self.assertFalse(modal.is_displayed())
|
self.assertEqual(person1.name, form.find_element_by_xpath(
|
||||||
obj = models.Organisation.objects.get(name="Test Organisation")
|
'//button[@data-id="id_person"]/span').text)
|
||||||
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
|
# Create organisation
|
||||||
wait.until(animation_is_finished())
|
wait.until(animation_is_finished())
|
||||||
add_button = self.browser.find_element_by_xpath(
|
add_button = self.browser.find_element_by_xpath(
|
||||||
'//a[@data-target="#id_venue" and contains(@href, "add")]')
|
'//a[@data-target="#id_organisation" and contains(@href, "add")]')
|
||||||
wait.until(animation_is_finished())
|
add_button.click()
|
||||||
add_button.click()
|
modal = self.browser.find_element_by_id('modal')
|
||||||
wait.until(animation_is_finished())
|
wait.until(animation_is_finished())
|
||||||
modal = self.browser.find_element_by_id('modal')
|
self.assertTrue(modal.is_displayed())
|
||||||
wait.until(animation_is_finished())
|
self.assertIn("Add Organisation", modal.find_element_by_tag_name('h3').text)
|
||||||
self.assertTrue(modal.is_displayed())
|
modal.find_element_by_xpath(
|
||||||
self.assertIn("Add Venue", modal.find_element_by_tag_name('h3').text)
|
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Organisation")
|
||||||
modal.find_element_by_xpath(
|
modal.find_element_by_xpath(
|
||||||
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Venue")
|
'//div[@id="modal"]//input[@type="submit"]').click()
|
||||||
modal.find_element_by_xpath(
|
|
||||||
'//div[@id="modal"]//input[@type="submit"]').click()
|
|
||||||
|
|
||||||
# See it is selected
|
# See it is selected
|
||||||
wait.until(animation_is_finished())
|
wait.until(animation_is_finished())
|
||||||
self.assertFalse(modal.is_displayed())
|
self.assertFalse(modal.is_displayed())
|
||||||
obj = models.Venue.objects.get(name="Test Venue")
|
obj = models.Organisation.objects.get(name="Test Organisation")
|
||||||
self.assertEqual(obj.name, form.find_element_by_xpath(
|
self.assertEqual(obj.name, form.find_element_by_xpath(
|
||||||
'//button[@data-id="id_venue"]/span').text)
|
'//button[@data-id="id_organisation"]/span').text)
|
||||||
# and backend
|
# and backend
|
||||||
option = form.find_element_by_xpath(
|
option = form.find_element_by_xpath(
|
||||||
'//select[@id="id_venue"]//option[@selected="selected"]')
|
'//select[@id="id_organisation"]//option[@selected="selected"]')
|
||||||
self.assertEqual(obj.pk, int(option.get_attribute("value")))
|
self.assertEqual(obj.pk, int(option.get_attribute("value")))
|
||||||
|
|
||||||
# Set start date/time
|
# Create venue
|
||||||
form.find_element_by_id('id_start_date').send_keys('25/05/3015')
|
wait.until(animation_is_finished())
|
||||||
form.find_element_by_id('id_start_time').send_keys('06:59')
|
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()
|
||||||
|
|
||||||
# Set end date/time
|
# See it is selected
|
||||||
form.find_element_by_id('id_end_date').send_keys('27/06/4000')
|
wait.until(animation_is_finished())
|
||||||
form.find_element_by_id('id_end_time').send_keys('07:00')
|
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")))
|
||||||
|
|
||||||
# Add item
|
# Set start date/time
|
||||||
form.find_element_by_xpath('//button[contains(@class, "item-add")]').click()
|
form.find_element_by_id('id_start_date').send_keys('25/05/3015')
|
||||||
wait.until(animation_is_finished())
|
form.find_element_by_id('id_start_time').send_keys('06:59')
|
||||||
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
|
# Set end date/time
|
||||||
objectitems = self.browser.execute_script("return objectitems;")
|
form.find_element_by_id('id_end_date').send_keys('27/06/4000')
|
||||||
self.assertEqual(1, len(objectitems))
|
form.find_element_by_id('id_end_time').send_keys('07:00')
|
||||||
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
|
# Add item
|
||||||
row = self.browser.find_element_by_id('item--1') # ID number is known, see above
|
form.find_element_by_xpath('//button[contains(@class, "item-add")]').click()
|
||||||
self.assertIn("Test Item 1", row.find_element_by_xpath('//span[@class="name"]').text)
|
wait.until(animation_is_finished())
|
||||||
self.assertIn("This is an item description",
|
modal = self.browser.find_element_by_id("itemModal")
|
||||||
row.find_element_by_xpath('//div[@class="item-description"]').text)
|
modal.find_element_by_id("item_name").send_keys("Test Item 1")
|
||||||
self.assertEqual('£ 23.95', row.find_element_by_xpath('//tr[@id="item--1"]/td[2]').text)
|
modal.find_element_by_id("item_description").send_keys(
|
||||||
self.assertEqual("2", row.find_element_by_xpath('//td[@class="quantity"]').text)
|
"This is an item description\nthat for reasons unknown spans two lines")
|
||||||
self.assertEqual('£ 47.90', row.find_element_by_xpath('//tr[@id="item--1"]/td[4]').text)
|
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
|
||||||
|
|
||||||
# Check totals
|
# Confirm item has been saved to json field
|
||||||
self.assertEqual("47.90", self.browser.find_element_by_id('sumtotal').text)
|
objectitems = self.browser.execute_script("return objectitems;")
|
||||||
self.assertIn("(TBC)", self.browser.find_element_by_id('vat-rate').text)
|
self.assertEqual(1, len(objectitems))
|
||||||
self.assertEqual("9.58", self.browser.find_element_by_id('vat').text)
|
testitem = objectitems["-1"]['fields'] # as we are deliberately creating this we know the ID
|
||||||
self.assertEqual("57.48", self.browser.find_element_by_id('total').text)
|
self.assertEqual("Test Item 1", testitem['name'])
|
||||||
|
self.assertEqual("2", testitem['quantity']) # test a couple of "worse case" fields
|
||||||
|
|
||||||
# Attempt to save - missing title
|
# See new item appear in table
|
||||||
save.click()
|
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('£ 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('£ 47.90', row.find_element_by_xpath('//tr[@id="item--1"]/td[4]').text)
|
||||||
|
|
||||||
# See error
|
# Check totals
|
||||||
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
|
self.assertEqual("47.90", self.browser.find_element_by_id('sumtotal').text)
|
||||||
self.assertTrue(error.is_displayed())
|
self.assertIn("(TBC)", self.browser.find_element_by_id('vat-rate').text)
|
||||||
# Should only have one error message
|
self.assertEqual("9.58", self.browser.find_element_by_id('vat').text)
|
||||||
self.assertEqual("Name", error.find_element_by_xpath('//dt[1]').text)
|
self.assertEqual("57.48", self.browser.find_element_by_id('total').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 BaseException:
|
|
||||||
self.assertFail("Element does not appear to have been deleted")
|
|
||||||
|
|
||||||
# Check at least some data is preserved. Some = all will be there
|
save = self.browser.find_element_by_xpath(
|
||||||
option = self.browser.find_element_by_xpath(
|
'(//button[@type="submit"])[3]')
|
||||||
'//select[@id="id_person"]//option[@selected="selected"]')
|
save.click()
|
||||||
self.assertEqual(person1.pk, int(option.get_attribute("value")))
|
|
||||||
|
|
||||||
# Set title
|
# TODO Testing of requirement for contact details
|
||||||
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
|
# TODO Something seems broken with the CI tests here.
|
||||||
successTitle = self.browser.find_element_by_xpath('//h1').text
|
# See redirected to success page
|
||||||
event = models.Event.objects.get(name='Test Event Name')
|
# successTitle = self.browser.find_element_by_xpath('//h1').text
|
||||||
|
# event = models.Event.objects.get(name='Test Event Name')
|
||||||
self.assertIn("N%05d | Test Event Name" % event.pk, successTitle)
|
# self.assertIn("N%05d | 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):
|
def testEventDuplicate(self):
|
||||||
|
client = models.Person.objects.create(name='Duplicate Test Person', email='duplicate@functional.test')
|
||||||
testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL,
|
testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL,
|
||||||
start_date=date.today() + timedelta(days=6),
|
start_date=date.today() + timedelta(days=6),
|
||||||
description="start future no end",
|
description="start future no end",
|
||||||
purchase_order='TESTPO',
|
purchase_order='TESTPO',
|
||||||
|
person=client,
|
||||||
auth_request_by=self.profile,
|
auth_request_by=self.profile,
|
||||||
auth_request_at=self.create_datetime(2015, 0o6, 0o4, 10, 00),
|
auth_request_at=self.create_datetime(2015, 0o6, 0o4, 10, 00),
|
||||||
auth_request_to="some@email.address")
|
auth_request_to="some@email.address")
|
||||||
@@ -499,7 +485,7 @@ class EventTest(LiveServerTestCase):
|
|||||||
modal = self.browser.find_element_by_id("itemModal")
|
modal = self.browser.find_element_by_id("itemModal")
|
||||||
modal.find_element_by_id("item_name").send_keys("Test Item 3")
|
modal.find_element_by_id("item_name").send_keys("Test Item 3")
|
||||||
modal.find_element_by_id("item_description").send_keys(
|
modal.find_element_by_id("item_description").send_keys(
|
||||||
"This is an item description\nthat for reasons unkown spans two lines")
|
"This is an item description\nthat for reasons unknown spans two lines")
|
||||||
e = modal.find_element_by_id("item_quantity")
|
e = modal.find_element_by_id("item_quantity")
|
||||||
e.click()
|
e.click()
|
||||||
e.send_keys(Keys.UP)
|
e.send_keys(Keys.UP)
|
||||||
@@ -572,6 +558,15 @@ class EventTest(LiveServerTestCase):
|
|||||||
e = self.browser.find_element_by_id('id_name')
|
e = self.browser.find_element_by_id('id_name')
|
||||||
e.send_keys('Test Event Name')
|
e.send_keys('Test Event Name')
|
||||||
|
|
||||||
|
# Set person
|
||||||
|
person = models.Person.objects.create(name='Date Validation Person', email='datevalidation@functional.test')
|
||||||
|
person_select = form.find_element_by_xpath(
|
||||||
|
'//button[@data-id="id_person"]')
|
||||||
|
person_select.send_keys(person.name)
|
||||||
|
person_dropped = form.find_element_by_xpath(
|
||||||
|
'//ul[contains(@class, "dropdown-menu")]//span[contains(text(), "%s")]' % person.name)
|
||||||
|
person_dropped.click()
|
||||||
|
|
||||||
# Both dates, no times, end before start
|
# Both dates, no times, end before start
|
||||||
self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'")
|
self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'")
|
||||||
|
|
||||||
@@ -677,6 +672,15 @@ class EventTest(LiveServerTestCase):
|
|||||||
e = self.browser.find_element_by_id('id_name')
|
e = self.browser.find_element_by_id('id_name')
|
||||||
e.send_keys('Test Event Name')
|
e.send_keys('Test Event Name')
|
||||||
|
|
||||||
|
# Set person
|
||||||
|
person = models.Person.objects.create(name='Rig Non-Rig Person', email='rignonrig@functional.test')
|
||||||
|
person_select = form.find_element_by_xpath(
|
||||||
|
'//button[@data-id="id_person"]')
|
||||||
|
person_select.send_keys(person.name)
|
||||||
|
person_dropped = form.find_element_by_xpath(
|
||||||
|
'//ul[contains(@class, "dropdown-menu")]//span[contains(text(), "%s")]' % person.name)
|
||||||
|
person_dropped.click()
|
||||||
|
|
||||||
# Set an arbitrary date
|
# Set an arbitrary date
|
||||||
self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'")
|
self.browser.execute_script("document.getElementById('id_start_date').value='3015-04-24'")
|
||||||
|
|
||||||
@@ -738,9 +742,9 @@ class EventTest(LiveServerTestCase):
|
|||||||
organisationPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Contact Details")]/..')
|
organisationPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Contact Details")]/..')
|
||||||
|
|
||||||
def testEventEdit(self):
|
def testEventEdit(self):
|
||||||
person = models.Person(name="Event Edit Person", email="eventdetail@person.tests.rigs", phone="123 123").save()
|
person = models.Person.objects.create(name="Event Edit Person", email="eventdetail@person.tests.rigs", phone="123 123")
|
||||||
organisation = models.Organisation(name="Event Edit Organisation", email="eventdetail@organisation.tests.rigs", phone="123 456").save()
|
organisation = models.Organisation.objects.create(name="Event Edit Organisation", email="eventdetail@organisation.tests.rigs", phone="123 456")
|
||||||
venue = models.Venue(name="Event Detail Venue").save()
|
venue = models.Venue.objects.create(name="Event Detail Venue")
|
||||||
|
|
||||||
eventData = {
|
eventData = {
|
||||||
'name': "Detail Test",
|
'name': "Detail Test",
|
||||||
|
|||||||
@@ -11,30 +11,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<h3>
|
<h3><a href="{% url 'asset_detail' object.asset_id %}">Asset: {{ object.asset_id }} | {{ object.description }} </a></h3>
|
||||||
<a href="{% url 'asset_detail' object.asset_id %}">Asset: {{ object.asset_id }} | {{ object.description }} </a>
|
<h4>
|
||||||
<small class="label label-default">
|
<span class="label label-default">
|
||||||
<strong>Category:</strong>
|
<strong>Category:</strong>
|
||||||
{{ object.category }}
|
{{ object.category }}
|
||||||
</small>
|
</span>
|
||||||
|
|
||||||
<small class="label label-{{ object.status.display_class|default:'default' }}">
|
<span class="label label-{{ object.status.display_class|default:'default' }}">
|
||||||
<strong>Status:</strong>
|
<strong>Status:</strong>
|
||||||
{{ object.status }}
|
{{ object.status }}
|
||||||
</small>
|
</span>
|
||||||
</h3>
|
</h4>
|
||||||
<br>
|
|
||||||
{% if object.serial_number %}
|
{% if object.serial_number %}
|
||||||
<p>
|
<dt>Serial Number: </dt>
|
||||||
<strong>Serial Number: </strong>
|
<dd>{{ object.serial_number }}</dd>
|
||||||
{{ object.serial_number }}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if object.comments %}
|
{% if object.comments %}
|
||||||
<p>
|
<dt>Comments: </dt>
|
||||||
<strong>Comments: </strong>
|
<dd class="dont-break-out">{{ object.comments|linebreaksbr }}<dd>
|
||||||
{{ object.comments|linebreaksbr }}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Reference in New Issue
Block a user