Compare commits

..

22 Commits

Author SHA1 Message Date
3e114484fe Merge branch 'master' into assets-testing 2020-02-08 13:12:27 +00:00
ea696deb1e Merge branch master into assets-testing
# Conflicts:
#	RIGS/test_functional.py
2020-02-07 22:18:21 +00:00
b8a4a70133 Fix broken search test 2020-02-07 21:57:32 +00:00
3028c053d4 Test visibility of form errors
And fix supplier form not displaying errors correctly!
2020-02-07 21:53:15 +00:00
3e16075b34 404 tests and test that everything requires authentication 2020-02-07 21:36:08 +00:00
762af5a1eb Asset search tests 2020-02-07 20:43:29 +00:00
d312634f68 Asset duplicate tests
Also fixed some random bugs
2020-02-07 20:00:11 +00:00
ca553137b6 Add supplier create/edit tests 2020-02-07 18:50:12 +00:00
1c577cdbee Helps if I add the migration... 2020-02-07 17:37:21 +00:00
eaeb56f9f9 Add supplier list page + tests
Also fix the supplier page not being ordered alphabetically
2020-02-07 17:35:16 +00:00
b8d9d24e2c Add delete sample data test for completeness
Chasing that sweet 100% coverage...
2020-02-07 00:17:28 +00:00
731f9865c6 Delete unused code
Much less effort way to increase coverage stats :D
2020-02-07 00:03:14 +00:00
be9d4115d0 Add access level tests 2020-02-06 23:18:08 +00:00
c6c8501640 Asset edit tests are here
A bit dodgy in places but par for the course for me :P
2020-02-06 22:33:26 +00:00
2641e80832 Basic asset create validation testing 2020-02-06 18:38:52 +00:00
7c30522906 Add cable asset creation test 2020-02-06 13:29:42 +00:00
b93bb29777 CHORE: Frickin pep8... 2020-02-05 20:32:21 +00:00
bf6d22bc38 Add non-cable asset creation test 2020-02-05 20:28:51 +00:00
Matthew Smith
5265ef835e Added Asset Create and Edit forms 2020-02-05 15:46:42 +00:00
39b22690f1 CHORE: pep8... 2020-02-05 13:33:14 +00:00
2a99e0912b FEAT: Adapt unit tests from RIGS to assets 2020-02-05 13:21:29 +00:00
Matthew Smith
a25c41150e Started POM and assets test 2020-01-23 18:29:18 +00:00
20 changed files with 62 additions and 228 deletions

View File

@@ -12,7 +12,6 @@ https://docs.djangoproject.com/en/1.7/ref/settings/
import os
import raven
import secrets
import datetime
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
@@ -45,9 +44,9 @@ if not DEBUG:
INTERNAL_IPS = ['127.0.0.1']
ADMINS = [('Tom Price', 'tomtom5152@gmail.com'), ('IT Manager', 'it@nottinghamtec.co.uk'), ('Arona Jones', 'arona.jones@nottinghamtec.co.uk')]
if DEBUG:
ADMINS.append(('Testing Superuser', 'superuser@example.com'))
ADMINS = (
('Tom Price', 'tomtom5152@gmail.com')
)
# Application definition
@@ -183,8 +182,6 @@ if not DEBUG or EMAILER_TEST:
else:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
EMAIL_COOLDOWN = datetime.timedelta(minutes=15)
# Internationalization
# https://docs.djangoproject.com/en/1.7/topics/i18n/

View File

@@ -22,22 +22,13 @@ admin.site.register(models.Invoice)
admin.site.register(models.Payment)
def approve_user(modeladmin, request, queryset):
queryset.update(is_approved=True)
approve_user.short_description = "Approve selected users"
@admin.register(models.Profile)
class ProfileAdmin(UserAdmin):
# Don't know how to add 'is_approved' whilst preserving the default list...
list_filter = ('is_approved', 'is_active', 'is_staff', 'is_superuser', 'groups')
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {
'fields': ('first_name', 'last_name', 'email', 'initials', 'phone')}),
(_('Permissions'), {'fields': ('is_approved', 'is_active', 'is_staff', 'is_superuser',
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups', 'user_permissions')}),
(_('Important dates'), {
'fields': ('last_login', 'date_joined')}),
@@ -50,7 +41,6 @@ class ProfileAdmin(UserAdmin):
)
form = forms.ProfileChangeForm
add_form = forms.ProfileCreationForm
actions = [approve_user]
class AssociateAdmin(VersionAdmin):

View File

@@ -2,10 +2,8 @@ from django import forms
from django.utils import formats
from django.conf import settings
from django.core import serializers
from django.core.mail import EmailMessage, EmailMultiAlternatives
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, AuthenticationForm, PasswordResetForm
from registration.forms import RegistrationFormUniqueEmail
from django.contrib.auth.forms import AuthenticationForm
from captcha.fields import ReCaptchaField
import simplejson
@@ -35,16 +33,8 @@ class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
return self.cleaned_data['initials']
class CheckApprovedForm(AuthenticationForm):
def confirm_login_allowed(self, user):
if user.is_approved or user.is_superuser:
return AuthenticationForm.confirm_login_allowed(self, user)
else:
raise forms.ValidationError("Your account hasn't been approved by an administrator yet. Please check back in a few minutes!")
# Embedded Login form - remove the autofocus
class EmbeddedAuthenticationForm(CheckApprovedForm):
class EmbeddedAuthenticationForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['username'].widget.attrs.pop('autofocus', None)

View File

@@ -1,23 +0,0 @@
# Generated by Django 2.0.13 on 2020-01-10 14:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0035_auto_20191124_1319'),
]
operations = [
migrations.AddField(
model_name='profile',
name='is_approved',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='profile',
name='last_emailed',
field=models.DateTimeField(blank=True, null=True),
),
]

View File

@@ -1,19 +0,0 @@
# Generated by Django 2.0.13 on 2020-01-11 18:29
# This migration ensures that legacy Profiles from before approvals were implemented are automatically approved
from django.db import migrations
def approve_legacy(apps, schema_editor):
Profile = apps.get_model('RIGS', 'Profile')
for person in Profile.objects.all():
person.is_approved = True
person.save()
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0036_profile_is_approved'),
]
operations = [
migrations.RunPython(approve_legacy)
]

View File

@@ -27,8 +27,6 @@ class Profile(AbstractUser):
initials = models.CharField(max_length=5, unique=True, null=True, blank=False)
phone = models.CharField(max_length=13, null=True, blank=True)
api_key = models.CharField(max_length=40, blank=True, editable=False, null=True)
is_approved = models.BooleanField(default=False)
last_emailed = models.DateTimeField(blank=True, null=True) # Currently only populated by the admin approval email. TODO: Populate it each time we send any email, might need that...
@classmethod
def make_api_key(cls):
@@ -55,14 +53,6 @@ class Profile(AbstractUser):
def latest_events(self):
return self.event_mic.order_by('-start_date').select_related('person', 'organisation', 'venue', 'mic')
@classmethod
def admins(cls):
return Profile.objects.filter(email__in=[y for x in settings.ADMINS for y in x])
@classmethod
def users_awaiting_approval_count(cls):
return Profile.objects.filter(models.Q(is_approved=False)).count()
def __str__(self):
return self.name

View File

@@ -1,4 +1,3 @@
import datetime
import re
import urllib.request
import urllib.error
@@ -11,9 +10,6 @@ from django.conf import settings
from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.mail import EmailMessage, EmailMultiAlternatives
from django.template.loader import get_template
from django.urls import reverse
from django.utils import timezone
from registration.signals import user_activated
from premailer import Premailer
from z3c.rml import rml2pdf
@@ -106,35 +102,3 @@ def on_revision_commit(sender, instance, created, **kwargs):
post_save.connect(on_revision_commit, sender=models.EventAuthorisation)
def send_admin_awaiting_approval_email(user, request, **kwargs):
# Bit more controlled than just emailing all superusers
for admin in models.Profile.admins():
# Check we've ever emailed them before and if so, if cooldown has passed.
if admin.last_emailed is None or admin.last_emailed + settings.EMAIL_COOLDOWN <= timezone.now():
context = {
'request': request,
'link_suffix': reverse("admin:RIGS_profile_changelist") + '?is_approved__exact=0',
'number_of_users': models.Profile.users_awaiting_approval_count(),
'to_name': admin.first_name
}
email = EmailMultiAlternatives(
"%s new users awaiting approval on RIGS" % (context['number_of_users']),
get_template("RIGS/admin_awaiting_approval.txt").render(context),
to=[admin.email],
reply_to=[user.email],
)
css = staticfiles_storage.path('css/email.css')
html = Premailer(get_template("RIGS/admin_awaiting_approval.html").render(context),
external_styles=css).transform()
email.attach_alternative(html, 'text/html')
email.send()
# Update last sent
admin.last_emailed = timezone.now()
admin.save()
user_activated.connect(send_admin_awaiting_approval_email)

View File

@@ -1,9 +0,0 @@
{% extends 'base_client_email.html' %}
{% block content %}
<p>Hi {{ to_name|default_if_none:"Administrator" }},</p>
<p>{{ number_of_users|default_if_none:"Some" }} new users are awaiting administrator approval on RIGS. Click <a href="{{ request.scheme }}://{{ request.get_host }}{{ link_suffix }}">here</a> to approve them.</p>
<p>TEC PA &amp; Lighting</p>
{% endblock %}

View File

@@ -1,5 +0,0 @@
Hi {{ to_name|default_if_none:"Administrator" }},
{{ number_of_users|default_if_none:"Some" }} new users are awaiting administrator approval on RIGS. Use this link to approve them: {{ request.scheme }}://{{ request.get_host }}/{{ link_suffix }}
TEC PA & Lighting

View File

@@ -10,14 +10,12 @@
| {{ object.name }} {% if event.dry_hire %}<span class="badge">Dry Hire</span>{% endif %}
</h1>
</div>
{% if perms.RIGS.view_event %}
<div class="col-sm-12 text-right">
{% include 'RIGS/event_detail_buttons.html' %}
</div>
{% endif %}
{% endif %}
{% if object.is_rig and perms.RIGS.view_event %}
{% if object.is_rig %}
{# only need contact details for a rig #}
<div class="col-sm-12 col-md-6 col-lg-5">
<div class="panel panel-default">
@@ -74,7 +72,7 @@
{% endif %}
</div>
{% endif %}
<div class="col-sm-12 {% if event.is_rig and perms.RIGS.view_event %}col-md-6 col-lg-7{% endif %}">
<div class="col-sm-12 {% if event.is_rig %}col-md-6 col-lg-7{% endif %}">
<div class="panel panel-info">
<div class="panel-heading">Event Info</div>
<div class="panel-body">
@@ -149,7 +147,7 @@
<dd>{{ object.collector }}</dd>
{% endif %}
{% if event.is_rig and not event.internal and perms.RIGS.view_event %}
{% if event.is_rig and not event.internal %}
<dd>&nbsp;</dd>
<dt>PO</dt>
<dd>{{ object.purchase_order }}</dd>
@@ -158,7 +156,7 @@
</div>
</div>
</div>
{% if event.is_rig and event.internal and perms.RIGS.view_event %}
{% if event.is_rig and event.internal %}
<div class="col-sm-12">
<div class="panel panel-default
{% if object.authorised %}
@@ -214,7 +212,7 @@
</div>
<div>
{% endif %}
{% if not request.is_ajax and perms.RIGS.view_event %}
{% if not request.is_ajax %}
<div class="col-sm-12 text-right">
{% include 'RIGS/event_detail_buttons.html' %}
</div>
@@ -224,23 +222,21 @@
<div class="panel panel-default">
<div class="panel-heading">Event Details</div>
<div class="panel-body">
{% if perms.RIGS.view_event %}
<div class="well well-sm">
<h4>Notes</h4>
<div class="dont-break-out">{{ event.notes|linebreaksbr }}</div>
</div>
{% endif %}
{% include 'RIGS/item_table.html' %}
</div>
</div>
</div>
{% if not request.is_ajax and perms.RIGS.view_event %}
{% if not request.is_ajax %}
<div class="col-sm-12 text-right">
{% include 'RIGS/event_detail_buttons.html' %}
</div>
{% endif %}
{% endif %}
{% if not request.is_ajax and perms.RIGS.view_event %}
{% if not request.is_ajax %}
<div class="col-sm-12 text-right">
<div>
<a href="{% url 'event_history' object.pk %}" title="View Revision History">
@@ -255,16 +251,12 @@
{% if request.is_ajax %}
{% block footer %}
<div class="row">
{% if perms.RIGS.view_event %}
<div class="col-sm-10 align-left">
<a href="{% url 'event_history' object.pk %}" title="View Revision History">
Last edited at {{ object.last_edited_at|default:'never' }} by {{ object.last_edited_by.name|default:'nobody' }}
</a>
</div>
<div class="col-sm-2">
{% else %}
<div class="col-sm-12">
{% endif %}
<div class="pull-right">
<a href="{% url 'event_detail' object.pk %}" class="btn btn-primary">Open Event Page <span
class="glyphicon glyphicon-eye"></span></a>

View File

@@ -6,7 +6,7 @@
<div class="row">
<div class="col-sm-12">
<a href="/">
<span class="source"> R<small>ig</small> I<small>nformation</small> G<small>athering</small> S<small>ystem</small></span>
<span class="source"> R<small>ig</small> I<small>nformation</small> G<small>athering</small> S<small>ystem</small></span>
</a>
</div>
@@ -20,9 +20,9 @@
<span class="glyphicon glyphicon-exclamation-sign"></span>
{% endif %}
</span>
<h3>
<a href="{% url 'event_detail' object.pk %}">
<a {% if perms.RIGS.view_event %}href="{% url 'event_detail' object.pk %}"{% endif %}>
{% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }}{% endif %}
| {{ object.name }} </a>
{% if object.venue %}
@@ -72,7 +72,7 @@
</p>
</div>
<div class="col-xs-6">
{% if object.meet_at %}
<p>
<strong>Crew meet:</strong>
@@ -97,7 +97,7 @@
{{ object.description|linebreaksbr }}
</p>
{% endif %}
</table>
</div>
</div>

View File

@@ -33,7 +33,7 @@
</td>
<td>
<h4>
<a href="{% url 'event_detail' event.pk %}">
<a {% if perms.RIGS.view_event %}href="{% url 'event_detail' event.pk %}" {% endif %}>
{{ event.name }}
</a>
{% if event.venue %}

View File

@@ -11,7 +11,7 @@
</div>
<div class="row">
<div class="col-sm-{% if perms.RIGS.view_event %}6{% else %}12{% endif %}">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="list-group-item-heading">Quick Links</h4>
@@ -26,11 +26,10 @@
<a class="list-group-item" href="https://forum.nottinghamtec.co.uk" target="_blank"><span class="glyphicon glyphicon-link"></span> TEC Forum</a>
<a class="list-group-item" href="//members.nottinghamtec.co.uk/wiki" target="_blank"><span class="glyphicon glyphicon-link"></span> TEC Wiki</a>
{% if perms.RIGS.view_event %}
<a class="list-group-item" href="http://members.nottinghamtec.co.uk/wiki/images/2/22/Event_Risk_Assesment.pdf" target="_blank"><span class="glyphicon glyphicon-link"></span> Pre-Event Risk Assessment</a>
<a class="list-group-item" href="//members.nottinghamtec.co.uk/price" target="_blank"><span class="glyphicon glyphicon-link"></span> Price List</a>
<a class="list-group-item" href="https://goo.gl/forms/jdPWov8PCNPoXtbn2" target="_blank"><span class="glyphicon glyphicon-link"></span> Subhire Insurance Form</a>
{% endif %}
</div>
</div>
@@ -74,7 +73,7 @@
</div>
{% if perms.RIGS.view_event %}
<div class="col-sm-6">
{% include 'RIGS/activity_feed.html' %}
{% include 'RIGS/activity_feed.html' %}
</div>
{% endif %}
</div>

View File

@@ -6,21 +6,17 @@
<em class="description">{{item.description|linebreaksbr}}</em>
</div>
</td>
{% if perms.RIGS.view_event %}
<td>£&nbsp;<span class="cost">{{item.cost|floatformat:2}}</span></td>
{% endif %}
<td class="quantity">{{item.quantity}}</td>
{% if perms.RIGS.view_event %}
<td>£&nbsp;<span class="sub-total" data-subtotal="{{item.total_cost}}">{{item.total_cost|floatformat:2}}</span></td>
{% endif %}
{% if edit %}
<td class="vert-align text-right">
<button type="button" class="item-edit btn btn-xs btn-default"
<button type="button" class="item-edit btn btn-xs btn-default"
data-pk="{{item.pk}}"
data-toggle="modal" data-target="#itemModal">
<span class="glyphicon glyphicon-edit"></span>
</button>
<button type="button" class="item-delete btn btn-xs btn-danger"
<button type="button" class="item-delete btn btn-xs btn-danger"
data-pk="{{item.pk}}">
<span class="glyphicon glyphicon-remove"></span>
</button>

View File

@@ -3,13 +3,9 @@
<thead>
<tr>
<td>Item</td>
{% if perms.RIGS.view_event %}
<td>Price</td>
{% endif %}
<td>Quantity</td>
{% if perms.RIGS.view_event %}
<td>Sub-total</td>
{% endif %}
{% if edit %}
<td class="text-right">
<button type="button" class="btn btn-default btn-xs item-add"
@@ -26,7 +22,6 @@
{% include 'RIGS/item_row.html' %}
{% endfor %}
</tbody>
{% if perms.RIGS.view_event %}
<tfoot>
<tr>
<td rowspan="3" colspan="2"></td>
@@ -48,7 +43,6 @@
<td colspan="2">£ <span id="total">{{object.total|default:0|floatformat:2}}</span></td>
</tr>
</tfoot>
{% endif %}
</table>
</div>
<table class="hidden invisible">

View File

@@ -141,41 +141,18 @@ class UserRegistrationTest(LiveServerTestCase):
self.assertEqual(password.get_attribute('placeholder'), 'Password')
self.assertEqual(password.get_attribute('type'), 'password')
# Expected to fail as not approved
username.send_keys('TestUsername')
password.send_keys('correcthorsebatterystaple')
self.browser.execute_script(
"return function() {jQuery('#g-recaptcha-response').val('PASSED'); return 0}()")
password.send_keys(Keys.ENTER)
# Test approval
profileObject = models.Profile.objects.all()[0]
self.assertFalse(profileObject.is_approved)
# Read what the error is
alert = self.browser.find_element_by_css_selector(
'div.alert-danger').text
self.assertIn("approved", alert)
# Approve the user so we can proceed
profileObject.is_approved = True
profileObject.save()
# Retry login
self.browser.get(self.live_server_url + '/user/login')
username = self.browser.find_element_by_id('id_username')
username.send_keys('TestUsername')
password = self.browser.find_element_by_id('id_password')
password.send_keys('correcthorsebatterystaple')
self.browser.execute_script(
"return function() {jQuery('#g-recaptcha-response').val('PASSED'); return 0}()")
password.send_keys(Keys.ENTER)
# Check we are logged in
udd = self.browser.find_element_by_class_name('navbar').text
self.assertIn('Hi John', udd)
# Check all the data actually got saved
profileObject = models.Profile.objects.all()[0]
self.assertEqual(profileObject.username, 'TestUsername')
self.assertEqual(profileObject.first_name, 'John')
self.assertEqual(profileObject.last_name, 'Smith')

View File

@@ -6,7 +6,7 @@ from RIGS import models, views, rigboard, finance, ical, versioning, forms
from django.views.generic import RedirectView
from django.views.decorators.clickjacking import xframe_options_exempt
from PyRIGS.decorators import permission_required_with_403, has_oembed
from PyRIGS.decorators import permission_required_with_403
from PyRIGS.decorators import api_key_required
urlpatterns = [
@@ -87,7 +87,8 @@ urlpatterns = [
permission_required_with_403('RIGS.view_event')(versioning.ActivityFeed.as_view()),
name='activity_feed'),
url(r'^event/(?P<pk>\d+)/$', has_oembed(oembed_view="event_oembed")(
url(r'^event/(?P<pk>\d+)/$',
permission_required_with_403('RIGS.view_event', oembed_view="event_oembed")(
rigboard.EventDetail.as_view()),
name='event_detail'),
url(r'^event/(?P<pk>\d+)/embed/$',

View File

@@ -41,7 +41,7 @@ def login(request, **kwargs):
else:
from django.contrib.auth.views import login
return login(request, authentication_form=forms.CheckApprovedForm)
return login(request)
# This view should be exempt from requiring CSRF token.

View File

@@ -1,41 +1,41 @@
beautifulsoup4==4.8.2
contextlib2==0.6.0.post1
diff-match-patch==20181111
beautifulsoup4==4.6.0
contextlib2==0.5.5
diff-match-patch==20121119
dj-database-url==0.5.0
dj-static==0.0.6
Django==3.0.3
django-filter==2.2.0
django-widget-tweaks==1.4.5
django-debug-toolbar==2.2
django-ical==1.7.0
django-recaptcha==2.0.6
django-registration-redux==2.7
django-reversion==3.0.7
Django==2.0.13
django-filter==2.0.0
django-widget-tweaks==1.4.3
django-debug-toolbar==1.9.1
django-ical==1.4
django-recaptcha==1.4.0
django-registration-redux==2.4
django-reversion==2.0.13
django-toolbelt==0.0.1
premailer==3.6.1
premailer==3.2.0
git+git://github.com/jazzband/django-widget-tweaks.git@1.4.2
gunicorn==20.0.4
icalendar==4.0.4
lxml==4.5.0
Markdown==3.2.1
Pillow==7.0.0
psycopg2==2.8.4
Pygments==2.5.2
gunicorn==19.8.1
icalendar==4.0.1
lxml==4.2.1
Markdown==2.6.11
Pillow==6.2.0
psycopg2==2.7.4
Pygments==2.2.0
PyPDF2==1.26.0
python-dateutil==2.8.1
pytz==2019.3
raven==6.10.0
reportlab==3.5.34
selenium==3.141.0
simplejson==3.17.0
six==1.14.0
sqlparse==0.3.0
python-dateutil==2.7.3
pytz==2018.4
raven==6.8.0
reportlab==3.4.0
selenium==3.12.0
simplejson==3.15.0
six==1.11.0
sqlparse==0.2.4
static3==0.7.0
svg2rlg==0.3
yolk==0.4.3
whitenoise==5.0.1
z3c.rml==3.9.1
zope.event==4.4
zope.interface==4.7.1
zope.schema==4.9.3
whitenoise==4.1.2
z3c.rml==3.5.0
zope.event==4.3.0
zope.interface==4.5.0
zope.schema==4.5.0
pypom==2.2.0

View File

@@ -5,6 +5,6 @@
{% block content %}
<div class="alert alert-success">
<h2>Activation Complete</h2>
<p>Your user account is now awaiting administrator approval. Won't be long!</p>
<p>You user account is now fully registered. Enjoy RIGS</p>
</div>
{% endblock %}
{% endblock %}