Compare commits

..

43 Commits

Author SHA1 Message Date
8c0c0941c2 Update event authorisation status chip with more statusi
Closes #446
2021-09-23 11:39:54 +01:00
abb0e35690 Uncomment headless flag for old style tests 2021-09-18 10:04:34 +01:00
bec0d4aee5 Aronafail 2021-09-18 09:53:23 +01:00
f1e43b707e Bypass hCaptcha in automated testing 2021-09-18 09:47:21 +01:00
796f5b44b0 Navbar works properly again 2021-09-13 16:14:18 +01:00
6458f016f0 Switch to hCapatcha 2021-09-13 16:14:05 +01:00
9ca953423f Revamp registration form 2021-09-13 16:13:47 +01:00
dependabot[bot]
4c5d958c6d Build(deps): Bump sqlparse from 0.4.1 to 0.4.2 (#442)
Bumps [sqlparse](https://github.com/andialbrecht/sqlparse) from 0.4.1 to 0.4.2.
- [Release notes](https://github.com/andialbrecht/sqlparse/releases)
- [Changelog](https://github.com/andialbrecht/sqlparse/blob/master/CHANGELOG)
- [Commits](https://github.com/andialbrecht/sqlparse/compare/0.4.1...0.4.2)

---
updated-dependencies:
- dependency-name: sqlparse
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-11 10:56:42 +01:00
85ca7b0880 Fix event button on invoice detail page
Was very confusing at first when I got a random event from several years ago!
2021-09-11 10:40:53 +01:00
44f9509eda Make very long asset children lists scroll 2021-09-08 15:41:14 +01:00
a2be4cbe5e Add some missing links to asset detail 2021-09-08 15:31:30 +01:00
dependabot[bot]
bb2f369ab5 Build(deps): Bump pillow from 8.2.0 to 8.3.2 (#441)
Bumps [pillow](https://github.com/python-pillow/Pillow) from 8.2.0 to 8.3.2.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/8.2.0...8.3.2)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-08 12:48:44 +01:00
2fdb2f260f FEAT: Add ability to generate label images for cable assets
To come SoonTM: ability to generate a A4 page of labels at once
2021-09-07 14:54:01 +01:00
6de3cb5d8c Allow filling out of electrical checks for large events 2021-09-02 12:11:05 +01:00
7c38af66f6 May fix windows/chrome RA name chooser issue
No idea tbh
2021-08-31 19:47:49 +01:00
f1a624ec8f Improve RA detail layout slightly 2021-08-31 19:39:24 +01:00
ab01beb2cd Add title links to ra/ec detail 2021-08-31 19:33:03 +01:00
11636809ca Add link to subhire insurance form on event detail 2021-08-19 15:48:56 +01:00
d7458f6366 Account for null power MICs in event checklist detail 2021-08-16 20:28:30 +01:00
febf9cf3ed curses! 2021-08-05 12:07:23 +01:00
3322a5ddf8 Add badge to nav for number of waiting invoices
Might slightly help us stop leaving them waiting for far too long...
2021-08-05 11:37:10 +01:00
dependabot[bot]
673bee4215 Build(deps): Bump django from 3.1.8 to 3.1.12 (#436)
Bumps [django](https://github.com/django/django) from 3.1.8 to 3.1.12.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/3.1.8...3.1.12)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-29 17:17:19 +01:00
dependabot[bot]
bab31107f7 Build(deps): Bump pillow from 8.1.2 to 8.2.0 (#434)
Bumps [pillow](https://github.com/python-pillow/Pillow) from 8.1.2 to 8.2.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/8.1.2...8.2.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-29 17:04:03 +01:00
dependabot[bot]
2d8473b698 Build(deps): Bump urllib3 from 1.26.4 to 1.26.5 (#432)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.4 to 1.26.5.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.4...1.26.5)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-29 16:48:50 +01:00
d81ecd9015 Minor fixes 2021-06-01 15:43:09 +01:00
b42c583897 FIX: Update detail test to match template change 2021-05-29 21:34:30 +01:00
57e966826e Redesign invoice detail page
Closes #431
2021-05-29 21:11:42 +01:00
6a5de4a9d6 Format all dates in event table the same way
Why did I think the old way was a good idea!
2021-05-19 11:17:51 +01:00
56bbf4c17c FIX #426: Override autofill styles in dark mode
Not super pretty, but you can at least read it!
2021-04-19 16:09:55 +01:00
dependabot[bot]
698f0be281 Build(deps): Bump django-debug-toolbar from 3.2 to 3.2.1 (#430)
Bumps [django-debug-toolbar](https://github.com/jazzband/django-debug-toolbar) from 3.2 to 3.2.1.
- [Release notes](https://github.com/jazzband/django-debug-toolbar/releases)
- [Changelog](https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst)
- [Commits](https://github.com/jazzband/django-debug-toolbar/compare/3.2...3.2.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-19 16:09:40 +01:00
dependabot[bot]
483f06e96f Build(deps): Bump django from 3.1.7 to 3.1.8 (#429)
Bumps [django](https://github.com/django/django) from 3.1.7 to 3.1.8.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/3.1.7...3.1.8)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-19 15:42:51 +01:00
dependabot[bot]
22193f3c39 Build(deps): Bump urllib3 from 1.26.3 to 1.26.4 (#428)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.3 to 1.26.4.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.3...1.26.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-15 16:45:31 +01:00
dependabot[bot]
59b63fe7aa Build(deps): Bump lxml from 4.6.2 to 4.6.3 (#427)
Bumps [lxml](https://github.com/lxml/lxml) from 4.6.2 to 4.6.3.
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-4.6.2...lxml-4.6.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-08 20:11:26 +01:00
5976ce9ea2 FIX Event form not displaying properly on creation error
Turns out that bit of code was needed ya goof
2021-04-08 19:32:28 +01:00
780d05e27c FIX: Rewrite event form hide/show logic.
Should be much more readable now. Closes #421
2021-03-31 18:46:16 +01:00
8cfa4bd79d Oh No (#425) 2021-03-25 14:27:14 +00:00
36f83ee59b Might fix CI 2021-03-15 12:18:30 +00:00
6d768832f4 Navbar layout wrangling 2021-03-15 11:59:37 +00:00
38da8642fa Add a bit of left/right padding to icons by default 2021-03-08 11:28:39 +00:00
f75e1d5bfc Use user-slash instead of (badly kerned) exclamation in Rigboard 2021-03-03 13:24:41 +00:00
3f959f8d56 Fix fix cabletype migration 2021-03-02 12:36:17 +00:00
b63a01120b Fix migrations 2021-03-02 12:15:56 +00:00
911336ceec More optimisation and cleanup (#420) 2021-03-02 11:29:57 +00:00
47 changed files with 933 additions and 812 deletions

View File

@@ -17,7 +17,7 @@ jobs:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 3.9 python-version: 3.9.1
- uses: actions/cache@v2 - uses: actions/cache@v2
id: pcache id: pcache
with: with:
@@ -27,8 +27,7 @@ jobs:
${{ runner.os }}-pipenv- ${{ runner.os }}-pipenv-
- name: Install Dependencies - name: Install Dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip pipenv
pip install pipenv
pipenv install -d pipenv install -d
# if: steps.pcache.outputs.cache-hit != 'true' # if: steps.pcache.outputs.cache-hit != 'true'
- name: Cache Static Files - name: Cache Static Files

13
Pipfile
View File

@@ -19,11 +19,10 @@ cssselect = "~=1.1.0"
cssutils = "~=1.0.2" cssutils = "~=1.0.2"
dj-database-url = "~=0.5.0" dj-database-url = "~=0.5.0"
dj-static = "~=0.0.6" dj-static = "~=0.0.6"
Django = "~=3.1.5" Django = "~=3.1.12"
django-debug-toolbar = "~=3.2" django-debug-toolbar = "~=3.2"
django-filter = "~=2.4.0" django-filter = "~=2.4.0"
django-ical = "~=1.7.1" django-ical = "~=1.7.1"
django-recaptcha = "~=2.0.6"
django-recurrence = "~=1.10.3" django-recurrence = "~=1.10.3"
django-registration-redux = "~=2.9" django-registration-redux = "~=2.9"
django-reversion = "~=3.0.9" django-reversion = "~=3.0.9"
@@ -35,11 +34,11 @@ gunicorn = "~=20.0.4"
icalendar = "~=4.0.7" icalendar = "~=4.0.7"
idna = "~=2.10" idna = "~=2.10"
importlib-metadata = "~=3.4.0" importlib-metadata = "~=3.4.0"
lxml = "~=4.6.2" lxml = "~=4.6.3"
Markdown = "~=3.3.3" Markdown = "~=3.3.3"
msgpack = "~=1.0.2" msgpack = "~=1.0.2"
pep517 = "~=0.9.1" pep517 = "~=0.9.1"
Pillow = "~=8.1.0" Pillow = "~=8.3.2"
premailer = "~=3.7.0" premailer = "~=3.7.0"
progress = "~=1.5" progress = "~=1.5"
psutil = "~=5.8.0" psutil = "~=5.8.0"
@@ -57,12 +56,12 @@ retrying = "~=1.3.3"
simplejson = "~=3.17.2" simplejson = "~=3.17.2"
six = "~=1.15.0" six = "~=1.15.0"
soupsieve = "~=2.1" soupsieve = "~=2.1"
sqlparse = "~=0.4.1" sqlparse = "~=0.4.2"
static3 = "~=0.7.0" static3 = "~=0.7.0"
svg2rlg = "~=0.3" svg2rlg = "~=0.3"
tini = "~=3.0.1" tini = "~=3.0.1"
tornado = "~=6.1" tornado = "~=6.1"
urllib3 = "~=1.26.2" urllib3 = "~=1.26.5"
whitenoise = "~=5.2.0" whitenoise = "~=5.2.0"
yolk = "~=0.4.3" yolk = "~=0.4.3"
"z3c.rml" = "~=4.1.2" "z3c.rml" = "~=4.1.2"
@@ -77,6 +76,8 @@ zipp = "~=3.4.0"
"zope.schema" = "~=6.0.1" "zope.schema" = "~=6.0.1"
sentry-sdk = "*" sentry-sdk = "*"
diff-match-patch = "*" diff-match-patch = "*"
python-barcode = "*"
django-hCaptcha = "*"
[dev-packages] [dev-packages]
selenium = "~=3.141.0" selenium = "~=3.141.0"

776
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -65,8 +65,8 @@ INSTALLED_APPS = (
'debug_toolbar', 'debug_toolbar',
'registration', 'registration',
'reversion', 'reversion',
'captcha',
'widget_tweaks', 'widget_tweaks',
'hcaptcha',
) )
MIDDLEWARE = ( MIDDLEWARE = (
@@ -186,12 +186,13 @@ LOGOUT_URL = '/user/logout/'
ACCOUNT_ACTIVATION_DAYS = 7 ACCOUNT_ACTIVATION_DAYS = 7
# reCAPTCHA settings # CAPTCHA settings
RECAPTCHA_PUBLIC_KEY = env('RECAPTCHA_PUBLIC_KEY', default="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI") # If not set, use development key if DEBUG or CI:
RECAPTCHA_PRIVATE_KEY = env('RECAPTCHA_PUBLIC_KEY', default="6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe") # If not set, use development key HCAPTCHA_SITEKEY = '10000000-ffff-ffff-ffff-000000000001'
NOCAPTCHA = True HCAPTCHA_SECRET = '0x0000000000000000000000000000000000000000'
else:
SILENCED_SYSTEM_CHECKS = ['captcha.recaptcha_test_key_error'] HCAPTCHA_SITEKEY = env('HCAPTCHA_SITEKEY')
HCAPTCHA_SECRET = env('HCAPTCHA_SECRET')
# Email # Email
EMAILER_TEST = False EMAILER_TEST = False

View File

@@ -55,7 +55,13 @@ class InvoiceDetail(generic.DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(InvoiceDetail, self).get_context_data(**kwargs) context = super(InvoiceDetail, self).get_context_data(**kwargs)
context['page_title'] = "Invoice {} ({})".format(self.object.display_id, self.object.invoice_date.strftime("%d/%m/%Y")) context['page_title'] = "Invoice {} ({}) ".format(self.object.display_id, self.object.invoice_date.strftime("%d/%m/%Y"))
if self.object.void:
context['page_title'] += "<span class='badge badge-warning float-right'>VOID</span>"
elif self.object.is_closed:
context['page_title'] += "<span class='badge badge-success float-right'>PAID</span>"
else:
context['page_title'] += "<span class='badge badge-info float-right'>OUTSTANDING</span>"
return context return context
@@ -173,24 +179,7 @@ class InvoiceWaiting(generic.ListView):
return context return context
def get_queryset(self): def get_queryset(self):
return self.get_objects() return self.model.objects.waiting_invoices()
def get_objects(self):
# TODO find a way to select items
events = self.model.objects.filter(
(
Q(start_date__lte=datetime.date.today(), end_date__isnull=True) | # Starts before with no end
Q(end_date__lte=datetime.date.today()) # Has end date, finishes before
) & Q(invoice__isnull=True) & # Has not already been invoiced
Q(is_rig=True) # Is a rig (not non-rig)
).order_by('start_date') \
.select_related('person',
'organisation',
'venue', 'mic') \
.prefetch_related('items')
return events
class InvoiceEvent(generic.View): class InvoiceEvent(generic.View):

View File

@@ -70,6 +70,11 @@ class EventRiskAssessmentDetail(generic.DetailView):
model = models.RiskAssessment model = models.RiskAssessment
template_name = 'risk_assessment_detail.html' template_name = 'risk_assessment_detail.html'
def get_context_data(self, **kwargs):
context = super(EventRiskAssessmentDetail, self).get_context_data(**kwargs)
context['page_title'] = "Risk Assessment for Event <a href='{}'>{} {}</a>".format(self.object.event.get_absolute_url(), self.object.event.display_id, self.object.event.name)
return context
class EventRiskAssessmentList(generic.ListView): class EventRiskAssessmentList(generic.ListView):
paginate_by = 20 paginate_by = 20
@@ -107,7 +112,7 @@ class EventChecklistDetail(generic.DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(EventChecklistDetail, self).get_context_data(**kwargs) context = super(EventChecklistDetail, self).get_context_data(**kwargs)
context['page_title'] = "Event Checklist for Event {} {}".format(self.object.event.display_id, self.object.event.name) context['page_title'] = "Event Checklist for Event <a href='{}'>{} {}</a>".format(self.object.event.get_absolute_url(), self.object.event.display_id, self.object.event.name)
return context return context

View File

@@ -0,0 +1,67 @@
# Generated by Django 3.1.7 on 2021-03-02 11:48
from django.db import migrations
def postgres_migration_prep(apps, schema_editor):
model = apps.get_model("RIGS", "Event")
for field in ["auth_request_to", "collector", "description", "notes", "purchase_order"]:
filter_param = {"{}__isnull".format(field): True}
update_param = {field: ""}
model.objects.filter(**filter_param).update(**update_param)
model = apps.get_model("RIGS", "EventAuthorisation")
for field in ["account_code", "uni_id"]:
filter_param = {"{}__isnull".format(field): True}
update_param = {field: ""}
model.objects.filter(**filter_param).update(**update_param)
model = apps.get_model("RIGS", "EventChecklist")
for field in ["extinguishers_location", "hs_location", "w1_description", "w2_description", "w3_description"]:
filter_param = {"{}__isnull".format(field): True}
update_param = {field: ""}
model.objects.filter(**filter_param).update(**update_param)
model = apps.get_model("RIGS", "EventItem")
for field in ["description"]:
filter_param = {"{}__isnull".format(field): True}
update_param = {field: ""}
model.objects.filter(**filter_param).update(**update_param)
model = apps.get_model("RIGS", "Organisation")
for field in ["address", "email", "notes", "phone"]:
filter_param = {"{}__isnull".format(field): True}
update_param = {field: ""}
model.objects.filter(**filter_param).update(**update_param)
model = apps.get_model("RIGS", "Payment")
for field in ["method"]:
filter_param = {"{}__isnull".format(field): True}
update_param = {field: ""}
model.objects.filter(**filter_param).update(**update_param)
model = apps.get_model("RIGS", "Person")
for field in ["address", "email", "notes", "phone"]:
filter_param = {"{}__isnull".format(field): True}
update_param = {field: ""}
model.objects.filter(**filter_param).update(**update_param)
model = apps.get_model("RIGS", "Profile")
for field in ["phone"]:
filter_param = {"{}__isnull".format(field): True}
update_param = {field: ""}
model.objects.filter(**filter_param).update(**update_param)
model = apps.get_model("RIGS", "RiskAssessment")
for field in ["general_notes", "persons_responsible_structures", "power_notes", "rigging_plan", "sound_notes"]:
filter_param = {"{}__isnull".format(field): True}
update_param = {field: ""}
model.objects.filter(**filter_param).update(**update_param)
model = apps.get_model("RIGS", "Venue")
for field in ["address", "email", "notes", "phone"]:
filter_param = {"{}__isnull".format(field): True}
update_param = {field: ""}
model.objects.filter(**filter_param).update(**update_param)
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0039_auto_20210123_1910'),
]
operations = [
migrations.RunPython(postgres_migration_prep, migrations.RunPython.noop)
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 3.1.5 on 2021-02-06 10:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0039_auto_20210123_1910'),
]
operations = [
migrations.AddField(
model_name='profile',
name='dark_theme',
field=models.BooleanField(default=False),
),
]

View File

@@ -1,4 +1,4 @@
# Generated by Django 3.1.5 on 2021-02-08 16:03 # Generated by Django 3.1.7 on 2021-03-02 12:04
import RIGS.models import RIGS.models
from django.db import migrations, models from django.db import migrations, models
@@ -7,10 +7,27 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('RIGS', '0040_profile_dark_theme'), ('RIGS', '0040_auto_20210302_1148'),
] ]
operations = [ operations = [
migrations.RemoveField(
model_name='event',
name='meet_info',
),
migrations.RemoveField(
model_name='event',
name='payment_method',
),
migrations.RemoveField(
model_name='event',
name='payment_received',
),
migrations.AddField(
model_name='profile',
name='dark_theme',
field=models.BooleanField(default=False),
),
migrations.AlterField( migrations.AlterField(
model_name='event', model_name='event',
name='auth_request_to', name='auth_request_to',
@@ -26,26 +43,11 @@ class Migration(migrations.Migration):
name='description', name='description',
field=models.TextField(blank=True, default=''), field=models.TextField(blank=True, default=''),
), ),
migrations.AlterField(
model_name='event',
name='meet_info',
field=models.CharField(blank=True, default='', max_length=255),
),
migrations.AlterField( migrations.AlterField(
model_name='event', model_name='event',
name='notes', name='notes',
field=models.TextField(blank=True, default=''), field=models.TextField(blank=True, default=''),
), ),
migrations.AlterField(
model_name='event',
name='payment_method',
field=models.CharField(blank=True, default='', max_length=255),
),
migrations.AlterField(
model_name='event',
name='payment_received',
field=models.CharField(blank=True, default='', max_length=255),
),
migrations.AlterField( migrations.AlterField(
model_name='event', model_name='event',
name='purchase_order', name='purchase_order',
@@ -144,7 +146,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='profile', model_name='profile',
name='phone', name='phone',
field=models.CharField(default='', max_length=13, null=True), field=models.CharField(blank=True, default='', max_length=13),
), ),
migrations.AlterField( migrations.AlterField(
model_name='riskassessment', model_name='riskassessment',

View File

@@ -1,18 +0,0 @@
# Generated by Django 3.1.7 on 2021-03-02 11:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0041_auto_20210208_1603'),
]
operations = [
migrations.AlterField(
model_name='profile',
name='phone',
field=models.CharField(blank=True, default='', max_length=13),
),
]

View File

@@ -278,6 +278,19 @@ class EventManager(models.Manager):
).count() ).count()
return event_count return event_count
def waiting_invoices(self):
events = self.filter(
(
models.Q(start_date__lte=datetime.date.today(), end_date__isnull=True) | # Starts before with no end
models.Q(end_date__lte=datetime.date.today()) # Or has end date, finishes before
) & models.Q(invoice__isnull=True) & # Has not already been invoiced
models.Q(is_rig=True) # Is a rig (not non-rig)
).order_by('start_date') \
.select_related('person', 'organisation', 'venue', 'mic') \
.prefetch_related('items')
return events
@reversion.register(follow=['items']) @reversion.register(follow=['items'])
class Event(models.Model, RevisionMixin): class Event(models.Model, RevisionMixin):
@@ -312,7 +325,6 @@ class Event(models.Model, RevisionMixin):
end_time = models.TimeField(blank=True, null=True) end_time = models.TimeField(blank=True, null=True)
access_at = models.DateTimeField(blank=True, null=True) access_at = models.DateTimeField(blank=True, null=True)
meet_at = models.DateTimeField(blank=True, null=True) meet_at = models.DateTimeField(blank=True, null=True)
meet_info = models.CharField(max_length=255, blank=True, default='')
# Crew management # Crew management
checked_in_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_checked_in', blank=True, null=True, checked_in_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_checked_in', blank=True, null=True,
@@ -321,8 +333,6 @@ class Event(models.Model, RevisionMixin):
verbose_name="MIC", on_delete=models.CASCADE) verbose_name="MIC", on_delete=models.CASCADE)
# Monies # Monies
payment_method = models.CharField(max_length=255, blank=True, default='')
payment_received = models.CharField(max_length=255, blank=True, default='')
purchase_order = models.CharField(max_length=255, blank=True, default='', verbose_name='PO') purchase_order = models.CharField(max_length=255, blank=True, default='', verbose_name='PO')
collector = models.CharField(max_length=255, blank=True, default='', verbose_name='collected by') collector = models.CharField(max_length=255, blank=True, default='', verbose_name='collected by')

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View File

@@ -1,9 +1,10 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static %} {% load static %}
{% load invoices_waiting from filters %}
{% block titleheader %} {% block titleheader %}
<a class="navbar-brand" href="/">RIGS</a> <a class="navbar-brand" style="margin-left: auto; margin-right: auto;" href="/">RIGS</a>
{% endblock %} {% endblock %}
{% block titleelements %} {% block titleelements %}
@@ -45,11 +46,11 @@
{% if perms.RIGS.view_invoice %} {% if perms.RIGS.view_invoice %}
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownInvoices" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownInvoices" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Invoices Invoices <span class="badge badge-danger badge-pill">{% invoices_waiting %}</span>
</a> </a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownInvoices"> <div class="dropdown-menu" aria-labelledby="navbarDropdownInvoices">
{% if perms.RIGS.add_invoice %} {% if perms.RIGS.add_invoice %}
<a class="dropdown-item" href="{% url 'invoice_waiting' %}"><span class="fas fa-briefcase text-danger"></span> Waiting</a> <a class="dropdown-item text-nowrap" href="{% url 'invoice_waiting' %}"><span class="fas fa-briefcase text-danger"></span> Waiting <span class="badge badge-danger badge-pill">{% invoices_waiting %}</span></a>
{% endif %} {% endif %}
<a class="dropdown-item" href="{% url 'invoice_list' %}"><span class="fas fa-pound-sign text-warning"></span> Outstanding</a> <a class="dropdown-item" href="{% url 'invoice_list' %}"><span class="fas fa-pound-sign text-warning"></span> Outstanding</a>
<a class="dropdown-item" href="{% url 'invoice_archive' %}"><span class="fas fa-book"></span> Archive</a> <a class="dropdown-item" href="{% url 'invoice_archive' %}"><span class="fas fa-book"></span> Archive</a>

View File

@@ -32,7 +32,11 @@
</dd> </dd>
<dt class="col-6">{{ object|help_text:'power_mic' }}</dt> <dt class="col-6">{{ object|help_text:'power_mic' }}</dt>
<dd class="col-6"> <dd class="col-6">
{% if object.power_mic %}
<a href="{% url 'profile_detail' object.power_mic.pk %}">{{ object.power_mic.name }}</a> <a href="{% url 'profile_detail' object.power_mic.pk %}">{{ object.power_mic.name }}</a>
{% else %}
None
{% endif %}
</dd> </dd>
</dl> </dl>
<p>List vehicles and their drivers</p> <p>List vehicles and their drivers</p>

View File

@@ -244,12 +244,19 @@
</div> </div>
</div> </div>
</div> </div>
{% elif event.riskassessment.event_size == 1 %} {% else %}
<div class="row my-3" id="size-1"> <div class="row my-3" id="size-1">
<div class="col-12"> <div class="col-12">
{% if event.riskassessment.event_size == 1 %}
<div class="card border-warning"> <div class="card border-warning">
<div class="card-header">Electrical Checks <small>for Medium TEC Events </small></div> <div class="card-header">Electrical Checks <small>for Medium TEC Events </small></div>
<div class="card-body"> <div class="card-body">
{% else %}
<div class="card border-danger">
<div class="card-header">Electrical Checks <small>for Large TEC Events</small></div>
<div class="card-body">
<div class="alert alert-danger"><strong>Here be dragons. Ensure you have appeased the Power Gods before continuing... (If you didn't check with a Supervisor, <em>you cannot continue your event!</em>)</strong></div>
{% endif %}
{% include 'partials/checklist_checkbox.html' with formitem=form.source_rcd %} {% include 'partials/checklist_checkbox.html' with formitem=form.source_rcd %}
{% include 'partials/checklist_checkbox.html' with formitem=form.labelling %} {% include 'partials/checklist_checkbox.html' with formitem=form.labelling %}
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %} {% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
@@ -339,17 +346,6 @@
</div> </div>
</div> </div>
</div> </div>
{% else %}
<div class="row my-3" id="size-2">
<div class="col-12">
<div class="card border-danger">
<div class="card-header">Electrical Checks <small>for Large TEC Events</small></div>
<div class="card-body">
<p>Outside the scope of this assessment. <strong>I really hope you checked with a supervisor...</strong></p>
</div>
</div>
</div>
</div>
{% endif %} {% endif %}
<div class="row mt-3"> <div class="row mt-3">
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">

View File

@@ -1,6 +1,4 @@
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
{% load linkornone from filters %}
{% load namewithnotes from filters %}
{% block content %} {% block content %}
<div class="row my-3 py-3"> <div class="row my-3 py-3">
@@ -14,50 +12,7 @@
{% if object.is_rig and perms.RIGS.view_event %} {% if object.is_rig and perms.RIGS.view_event %}
{# only need contact details for a rig #} {# only need contact details for a rig #}
<div class="col-md-6"> <div class="col-md-6">
{% if event.person %} {% include 'partials/contact_details.html' %}
<div class="card card-default mb-3">
<div class="card-header">Contact Details</div>
<div class="card-body">
<dl class="row">
<dt class="col-sm-6">Person</dt>
<dd class="col-sm-6">
{% if object.person %}
<a href="{% url 'person_detail' object.person.pk %}" class="modal-href">
{{ object.person|namewithnotes:'person_detail' }}
</a>
{% endif %}
</dd>
<dt class="col-sm-6">Email</dt>
<dd class="col-sm-6">{{ object.person.email|linkornone:'mailto' }}</dd>
<dt class="col-sm-6">Phone Number</dt>
<dd class="col-sm-6">{{ object.person.phone|linkornone:'tel' }}</dd>
</dl>
</div>
</div>
{% endif %}
{% if event.organisation %}
<div class="card card-default">
<div class="card-header">Organisation</div>
<div class="card-body">
<dl class="row">
<dt class="col-sm-6">Organisation</dt>
<dd class="col-sm-6">
{% if object.organisation %}
<a href="{% url 'organisation_detail' object.organisation.pk %}" class="modal-href">
{{ object.organisation|namewithnotes:'organisation_detail' }}
</a>
{% endif %}
</dd>
<dt class="col-sm-6">Email</dt>
<dd class="col-sm-6">{{ object.organisation.email|linkornone:'mailto' }}</dd>
<dt class="col-sm-6">Phone Number</dt>
<dd class="col-sm-6">{{ object.organisation.phone|linkornone:'tel' }}</dd>
<dt class="col-sm-6">Has SU Account</dt>
<dd class="col-sm-6">{{ event.organisation.union_account|yesno|capfirst }}</dd>
</dl>
</div>
</div>
{% endif %}
</div> </div>
{% endif %} {% endif %}
<div class="col-md-6"> <div class="col-md-6">

View File

@@ -27,18 +27,26 @@
const matches = window.matchMedia("(prefers-reduced-motion: reduce)").matches || window.matchMedia("(update: slow)").matches; const matches = window.matchMedia("(prefers-reduced-motion: reduce)").matches || window.matchMedia("(update: slow)").matches;
$(document).ready(function () { $(document).ready(function () {
dur = matches ? 0 : 500; dur = matches ? 0 : 500;
{% if not object.pk and not form.errors %} {% if object.pk %}
$('.form-hws').slideUp(dur, function () { // Editing
$('.form-is_rig').slideUp(dur); {% if not object.is_rig %}
});
{% elif not object.pk and form.errors %}
if ($('#{{form.is_rig.auto_id}}').attr('checked') !== 'checked') {
$('.form-is_rig').hide(); $('.form-is_rig').hide();
} {% endif %}
{% endif %} //Creation
{% if not object.pk %} {% else %}
// If there were errors, apply the previous Rig/not-Rig selection
{% if form.errors %}
$('.form-hws').show();
if ($('#{{form.is_rig.auto_id}}').attr('checked') !== 'checked') {
$('.form-is_rig').hide();
}
{% else %}
//Initial hide
$('.form-hws').slideUp(dur);
{% endif %}
//Button handling
$('#is_rig-selector button').on('click', function () { $('#is_rig-selector button').on('click', function () {
$('.form-non_rig').slideDown(dur); $('.form-non_rig').slideDown(dur); //Non rig stuff also needed for rig, so always slide down
if ($(this).data('is_rig') === 1) { if ($(this).data('is_rig') === 1) {
$('#{{form.is_rig.auto_id}}').prop('checked', true); $('#{{form.is_rig.auto_id}}').prop('checked', true);
if ($('.form-non_rig').is(':hidden')) { if ($('.form-non_rig').is(':hidden')) {
@@ -48,7 +56,6 @@
} }
$('.form-hws, .form-hws .form-is_rig').css('overflow', 'visible'); $('.form-hws, .form-hws .form-is_rig').css('overflow', 'visible');
} else { } else {
$('#{{form.is_rig.auto_id}}').prop('checked', false); $('#{{form.is_rig.auto_id}}').prop('checked', false);
$('.form-is_rig').slideUp(dur); $('.form-is_rig').slideUp(dur);
} }
@@ -62,13 +69,6 @@
$('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').tooltip();
}) })
</script> </script>
<noscript>
<style>
.form-hws {
display: inherit !important;
}
</style>
</noscript>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -2,102 +2,88 @@
{% load button from filters %} {% load button from filters %}
{% block content %} {% block content %}
<div class="col-sm-12"> <div class="row py-4">
<div class="row justify-content-end py-3"> <div class="col-sm-12 text-right px-0">
<div class="col-sm-4 text-right"> <div class="btn-group">
<div class="btn-group btn-page"> <a href="{% url 'event_detail' object.event.pk %}" class="btn btn-primary">Open Event Page <span class="fas fa-eye"></span></a>
<a href="{% url 'invoice_delete' object.pk %}" class="btn btn-danger" title="Delete Invoice"> <a href="{% url 'invoice_delete' object.pk %}" class="btn btn-danger" title="Delete Invoice">
<span class="fas fa-times"></span> <span <span class="fas fa-times"></span> <span
class="d-none d-sm-inline">Delete</span> class="d-none d-sm-inline">Delete</span>
</a> </a>
<a href="{% url 'invoice_void' object.pk %}" class="btn btn-warning" title="Void Invoice"> <a href="{% url 'invoice_void' object.pk %}" class="btn btn-warning" title="Void Invoice">
<span class="fas fa-ban"></span> <span <span class="fas fa-ban"></span> <span
class="d-none d-sm-inline">Void</span> class="d-none d-sm-inline">Void</span>
</a> </a>
{% button 'print' url='invoice_print' pk=object.pk %} {% button 'print' url='invoice_print' pk=object.pk %}
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="card card-default">
<div class="card-header">Invoice Details<span class="float-right">
{% if object.void %}(VOID){% elif object.is_closed %}(PAID){% else %}(OUTSTANDING){% endif %}
</span></div>
<div class="card-body">
{% if object.event.organisation %}
{{ object.event.organisation.name }}<br/>
{{ object.event.organisation.address|linebreaksbr }}
{% else %}
{{ object.event.person.name }}<br/>
{{ object.event.person.address|linebreaksbr }}
{% endif %}
</div>
</div>
</div>
<div class="col-sm-6">
{% include 'partials/event_details.html' %}
</div>
{% if object.event.internal %}
<div class="col-sm-6">
{% include 'partials/auth_details.html' %}
</div>
{% endif %}
</div>
<div class="row py-4">
<div class="col-sm-6">
<div class="card card-default">
<div class="card-body">
<div class="text-right py-3">
<a href="{% url 'payment_create' %}?invoice={{ object.pk }}"
class="btn btn-success modal-href"
data-target="#{{ form.person.id_for_label }}">
<span class="fas fa-plus"></span> Add
</a>
</div>
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Amount</th>
<th scope="col">Method</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for payment in object.payment_set.all %}
<tr>
<th scope="row">{{ payment.date }}</th>
<td>{{ payment.amount|floatformat:2 }}</td>
<td>{{ payment.get_method_display }}</td>
<td>
<a href="{% url 'payment_delete' payment.pk %}" class="btn btn-small btn-danger"><span class="fas fa-times"></span></a>
</td>
</tr>
{% endfor %}
<tr>
<td class="text-right"><strong>Balance:</strong></td>
<td>{{ object.balance|floatformat:2 }}</td>
<td></td>
<td></td>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="card">
{% with object.event as object %}
{% include 'item_table.html' %}
{% endwith %}
</div>
</div>
</div>
<div class="col-12 text-right">
{% include 'partials/last_edited.html' with target="invoice_history" %}
</div> </div>
</div> </div>
<div>
<div class="row py-4">
{% with object.event as object %}
<div class="col-sm-6">
{% include 'partials/contact_details.html' %}
</div>
<div class="col-sm-6">
{% include 'partials/event_details.html' %}
</div>
{% if object.event.internal %}
<div class="col-sm-6">
{% include 'partials/auth_details.html' %}
</div>
{% endif %}
{% endwith %}
</div>
<div class="row py-4">
<div class="col-sm-6">
<div class="card card-default">
<div class="card-body">
<div class="text-right py-3">
<a href="{% url 'payment_create' %}?invoice={{ object.pk }}"
class="btn btn-success modal-href"
data-target="#{{ form.person.id_for_label }}">
<span class="fas fa-plus"></span> Add
</a>
</div>
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Amount</th>
<th scope="col">Method</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for payment in object.payment_set.all %}
<tr>
<th scope="row">{{ payment.date }}</th>
<td>{{ payment.amount|floatformat:2 }}</td>
<td>{{ payment.get_method_display }}</td>
<td>
<a href="{% url 'payment_delete' payment.pk %}" class="btn btn-small btn-danger"><span class="fas fa-times"></span></a>
</td>
</tr>
{% endfor %}
<tr>
<td class="text-right"><strong>Balance:</strong></td>
<td>{{ object.balance|floatformat:2 }}</td>
<td></td>
<td></td>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="card">
{% with object.event as object %}
{% include 'item_table.html' %}
{% endwith %}
</div>
</div>
</div>
<div class="col-12 text-right">
{% include 'partials/last_edited.html' with target="invoice_history" %}
</div>
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,47 @@
{% load linkornone from filters %}
{% load namewithnotes from filters %}
{% if object.person %}
<div class="card card-default mb-3">
<div class="card-header">Person Details</div>
<div class="card-body">
<dl class="row">
<dt class="col-sm-6">Person</dt>
<dd class="col-sm-6">
{% if object.person %}
<a href="{% url 'person_detail' object.person.pk %}" class="modal-href">
{{ object.person|namewithnotes:'person_detail' }}
</a>
{% endif %}
</dd>
<dt class="col-sm-6">Email</dt>
<dd class="col-sm-6">{{ object.person.email|linkornone:'mailto' }}</dd>
<dt class="col-sm-6">Phone Number</dt>
<dd class="col-sm-6">{{ object.person.phone|linkornone:'tel' }}</dd>
</dl>
</div>
</div>
{% endif %}
{% if object.organisation %}
<div class="card card-default">
<div class="card-header">Organisation Details</div>
<div class="card-body">
<dl class="row">
<dt class="col-sm-6">Organisation</dt>
<dd class="col-sm-6">
{% if object.organisation %}
<a href="{% url 'organisation_detail' object.organisation.pk %}" class="modal-href">
{{ object.organisation|namewithnotes:'organisation_detail' }}
</a>
{% endif %}
</dd>
<dt class="col-sm-6">Email</dt>
<dd class="col-sm-6">{{ object.organisation.email|linkornone:'mailto' }}</dd>
<dt class="col-sm-6">Phone Number</dt>
<dd class="col-sm-6">{{ object.organisation.phone|linkornone:'tel' }}</dd>
<dt class="col-sm-6">Has SU Account</dt>
<dd class="col-sm-6">{{ event.organisation.union_account|yesno|capfirst }}</dd>
</dl>
</div>
</div>
{% endif %}

View File

@@ -47,5 +47,7 @@
class="fas fa-pound-sign"></span> class="fas fa-pound-sign"></span>
<span class="d-none d-sm-inline">Invoice</span></a> <span class="d-none d-sm-inline">Invoice</span></a>
{% endif %} {% endif %}
<a href="https://docs.google.com/forms/d/e/1FAIpQLSf-TBOuJZCTYc2L8DWdAaC3_Werq0ulsUs8-6G85I6pA9WVsg/viewform" class="btn btn-danger"><span class="fas fa-file-invoice-dollar"></span> <span class="d-none d-sm-inline">Subhire Insurance Form</span></a>
{% endif %} {% endif %}
</div> </div>

View File

@@ -6,6 +6,10 @@
<span class="badge badge-success">PO: {{ event.purchase_order }}</span> <span class="badge badge-success">PO: {{ event.purchase_order }}</span>
{% elif event.authorised %} {% elif event.authorised %}
<span class="badge badge-success">Authorisation: Complete <span class="fas fa-check"></span></span> <span class="badge badge-success">Authorisation: Complete <span class="fas fa-check"></span></span>
{% elif event.authorisation and event.authorisation.amount != event.total and event.authorisation.last_edited_at > event.auth_request_at %}
<span class="badge badge-warning"> Authorisation: Issue <span class="fas fa-exclamation-circle"></span></span>
{% elif event.auth_request_to %}
<span class="badge badge-info"> Authorisation: Sent <span class="fas fa-paper-plane"></span></span>
{% else %} {% else %}
<span class="badge badge-danger">Authorisation: <span class="fas fa-times"></span></span> <span class="badge badge-danger">Authorisation: <span class="fas fa-times"></span></span>
{% endif %} {% endif %}

View File

@@ -30,25 +30,25 @@
<th scope="row" id="event_number">{{ event.display_id }}</th> <th scope="row" id="event_number">{{ event.display_id }}</th>
<!--Dates & Times--> <!--Dates & Times-->
<td id="event_dates"> <td id="event_dates">
<span class="text-nowrap">Start: <strong>{{ event.start_date|date:"D d/m/Y" }}</strong> <span class="text-nowrap">Start: <strong>{{ event.start_date|date:"D d/m/Y" }}
{% if event.has_start_time %} {% if event.has_start_time %}
{{ event.start_time|date:"H:i" }} {{ event.start_time|date:"H:i" }}
{% endif %} {% endif %}</strong>
</span> </span>
{% if event.end_date %} {% if event.end_date %}
<br> <br>
<span class="text-nowrap">End: {% if event.end_date != event.start_date %}<strong>{{ event.end_date|date:"D d/m/Y" }}</strong>{% endif %} <span class="text-nowrap">End: {% if event.end_date != event.start_date %}<strong>{{ event.end_date|date:"D d/m/Y" }}{% endif %}
{% if event.has_end_time %} {% if event.has_end_time %}
{{ event.end_time|date:"H:i" }} {{ event.end_time|date:"H:i" }}
{% endif %} {% endif %}</strong>
</span> </span>
{% endif %} {% endif %}
{% if not event.cancelled %} {% if not event.cancelled %}
{% if event.meet_at %} {% if event.meet_at %}
<br><span>Crew meet: <strong>{{ event.meet_at|date:"H:i" }}</strong> {{ event.meet_at|date:"(d/m/Y)" }}</span> <br><span class="text-nowrap">Meet: <strong>{{ event.meet_at|date:"D d/m/Y H:i" }}</strong></span>
{% endif %} {% endif %}
{% if event.access_at %} {% if event.access_at %}
<br><span>Access at: <strong>{{ event.access_at|date:"H:i" }}</strong> {{ event.access_at|date:"(d/m/Y)" }}</span> <br><span class="text-nowrap">Access: <strong>{{ event.access_at|date:" D d/m/Y H:i" }}</strong></span>
{% endif %} {% endif %}
{% endif %} {% endif %}
</td> </td>
@@ -67,9 +67,9 @@
</h4> </h4>
{% if event.is_rig and not event.cancelled %} {% if event.is_rig and not event.cancelled %}
<h5> <h5>
{{ event.person.name }} <a href="{{ event.person.get_absolute_url }}">{{ event.person.name }}</a>
{% if event.organisation %} {% if event.organisation %}
for {{ event.organisation.name }} for <a href="{{ event.organisation.get_absolute_url }}">{{ event.organisation.name }}</a>
{% endif %} {% endif %}
</h5> </h5>
{% endif %} {% endif %}
@@ -90,7 +90,7 @@
</a> </a>
{% endif %} {% endif %}
{% elif event.is_rig %} {% elif event.is_rig %}
<span class="fas fa-exclamation"></span> <span class="fas fa-user-slash"></span>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>

View File

@@ -1,5 +1,4 @@
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
{% block title %}Risk Assessment for Event N{{ object.event.pk|stringformat:"05d" }} {{ object.event.name }}{% endblock %}
{% load help_text from filters %} {% load help_text from filters %}
{% load yesnoi from filters %} {% load yesnoi from filters %}
{% load linkornone from filters %} {% load linkornone from filters %}
@@ -7,7 +6,6 @@
{% block content %} {% block content %}
<div class="row py-3"> <div class="row py-3">
<div class="col-12"> <div class="col-12">
<h3>Risk Assessment for Event N{{ object.event.pk|stringformat:"05d" }} {{ object.event.name }}</h3>
<div class="card card-default mb-3"> <div class="card card-default mb-3">
<div class="card-header">General</div> <div class="card-header">General</div>
<div class="card-body"> <div class="card-body">
@@ -51,11 +49,11 @@
<dd class="col-sm-6"> <dd class="col-sm-6">
{{ object.power_mic.name|default:'None' }} {{ object.power_mic.name|default:'None' }}
</dd> </dd>
<dt class="col-sm-6">{{ object|help_text:'generators' }}</dt> <dt class="col-sm-6">{{ object|help_text:'outside' }}</dt>
<dd class="col-sm-6"> <dd class="col-sm-6">
{{ object.outside|yesnoi:'invert' }} {{ object.outside|yesnoi:'invert' }}
</dd> </dd>
<dt class="col-sm-6">{{ object|help_text:'outside' }}</dt> <dt class="col-sm-6">{{ object|help_text:'generators' }}</dt>
<dd class="col-sm-6"> <dd class="col-sm-6">
{{ object.generators|yesnoi:'invert' }} {{ object.generators|yesnoi:'invert' }}
</dd> </dd>
@@ -97,58 +95,64 @@
</dl> </dl>
</div> </div>
</div> </div>
<div class="card card-default mb-3"> <div class="row">
<div class="card-header">Site Details</div> <div class="col-lg-6 col-12">
<div class="card-body"> <div class="card card-default mb-3">
<dl class="row"> <div class="card-header">Site Details</div>
<dt class="col-sm-6">{{ object|help_text:'known_venue' }}</dt> <div class="card-body">
<dd class="col-sm-6"> <dl class="row">
{{ object.known_venue|yesnoi:'invert' }} <dt class="col-10">{{ object|help_text:'known_venue' }}</dt>
</dd> <dd class="col-2">
<dt class="col-sm-6">{{ object|help_text:'safe_loading'|safe }}</dt> {{ object.known_venue|yesnoi:'invert' }}
<dd class="col-sm-6"> </dd>
{{ object.safe_loading|yesnoi:'invert' }} <dt class="col-10">{{ object|help_text:'safe_loading'|safe }}</dt>
</dd> <dd class="col-2">
<dt class="col-sm-6">{{ object|help_text:'safe_storage' }}</dt> {{ object.safe_loading|yesnoi:'invert' }}
<dd class="col-sm-6"> </dd>
{{ object.safe_storage|yesnoi:'invert' }} <dt class="col-10">{{ object|help_text:'safe_storage' }}</dt>
</dd> <dd class="col-2">
<dt class="col-sm-6">{{ object|help_text:'area_outside_of_control' }}</dt> {{ object.safe_storage|yesnoi:'invert' }}
<dd class="col-sm-6"> </dd>
{{ object.area_outside_of_control|yesnoi:'invert' }} <dt class="col-10">{{ object|help_text:'area_outside_of_control' }}</dt>
</dd> <dd class="col-2">
<dt class="col-sm-6">{{ object|help_text:'barrier_required' }}</dt> {{ object.area_outside_of_control|yesnoi:'invert' }}
<dd class="col-sm-6"> </dd>
{{ object.barrier_required|yesnoi:'invert' }} <dt class="col-10">{{ object|help_text:'barrier_required' }}</dt>
</dd> <dd class="col-2">
<dt class="col-sm-6">{{ object|help_text:'nonstandard_emergency_procedure' }}</dt> {{ object.barrier_required|yesnoi:'invert' }}
<dd class="col-sm-6"> </dd>
{{ object.nonstandard_emergency_procedure|yesnoi:'invert' }} <dt class="col-10">{{ object|help_text:'nonstandard_emergency_procedure' }}</dt>
</dd> <dd class="col-2">
</dl> {{ object.nonstandard_emergency_procedure|yesnoi:'invert' }}
</dd>
</dl>
</div>
</div>
</div> </div>
</div> <div class="col-lg-6 col-12">
<div class="card card-default mb-3"> <div class="card card-default mb-3">
<div class="card-header">Structures</div> <div class="card-header">Structures</div>
<div class="card-body"> <div class="card-body">
<dl class="row"> <dl class="row">
<dt class="col-sm-6">{{ object|help_text:'special_structures' }}</dt> <dt class="col-10">{{ object|help_text:'special_structures' }}</dt>
<dd class="col-sm-6"> <dd class="col-2">
{{ object.special_structures|yesnoi:'invert' }} {{ object.special_structures|yesnoi:'invert' }}
</dd> </dd>
<dt class="col-sm-6">{{ object|help_text:'suspended_structures' }}</dt> <dt class="col-10">{{ object|help_text:'suspended_structures' }}</dt>
<dd class="col-sm-6"> <dd class="col-2">
{{ object.suspended_structures|yesnoi:'invert' }} {{ object.suspended_structures|yesnoi:'invert' }}
</dd> </dd>
<dt class="col-sm-6">{{ object|help_text:'persons_responsible_structures' }}</dt> <dt class="col-12">{{ object|help_text:'persons_responsible_structures' }}</dt>
<dd class="col-sm-6"> <dd class="col-12">
{{ object.persons_responsible_structures.name|default:'N/A'|linebreaks }} {{ object.persons_responsible_structures.name|default:'N/A'|linebreaks }}
</dd> </dd>
<dt class="col-6">{{ object|help_text:'rigging_plan'|safe }}</dt> <dt class="col-12">{{ object|help_text:'rigging_plan'|safe }}</dt>
<dd class="col-6"> <dd class="col-12">
{{ object.rigging_plan|linkornone }} {{ object.rigging_plan|linkornone|default:'N/A' }}
</dd> </dd>
</dl> </dl>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -11,13 +11,12 @@
{% block preload_js %} {% block preload_js %}
{{ block.super }} {{ block.super }}
<script src="{% static 'js/selects.js' %}" async></script> <script src="{% static 'js/selects.js' %}"></script>
{% endblock %} {% endblock %}
{% block js %} {% block js %}
{{ block.super }} {{ block.super }}
<script src="{% static 'js/autocompleter.js' %}"></script> <script src="{% static 'js/autocompleter.js' %}"></script>
<script src="{% static 'js/tooltip.js' %}"></script>
<script> <script>
function parseBool(str) { function parseBool(str) {

View File

@@ -217,3 +217,8 @@ def button(type, url=None, pk=None, clazz="", icon=None, text="", id=None, style
elif type == 'submit': elif type == 'submit':
return {'submit': True, 'class': 'btn-primary', 'icon': 'fa-save', 'text': 'Save', 'id': id, 'style': style} return {'submit': True, 'class': 'btn-primary', 'icon': 'fa-save', 'text': 'Save', 'id': id, 'style': style}
return {'target': url, 'pk': pk, 'class': clazz, 'icon': icon, 'text': text, 'id': id, 'style': style} return {'target': url, 'pk': pk, 'class': clazz, 'icon': icon, 'text': text, 'id': id, 'style': style}
@register.simple_tag
def invoices_waiting():
return len(models.Event.objects.waiting_invoices())

View File

@@ -53,7 +53,7 @@ class EventDetail(BasePage):
# TODO Refactor into regions to match template fragmentation # TODO Refactor into regions to match template fragmentation
_event_name_selector = (By.XPATH, '//h2') _event_name_selector = (By.XPATH, '//h2')
_person_panel_selector = (By.XPATH, '//div[contains(text(), "Contact Details")]/..') _person_panel_selector = (By.XPATH, '//div[contains(text(), "Person Details")]/..')
_name_selector = (By.XPATH, '//dt[text()="Person"]/following-sibling::dd[1]') _name_selector = (By.XPATH, '//dt[text()="Person"]/following-sibling::dd[1]')
_email_selector = (By.XPATH, '//dt[text()="Email"]/following-sibling::dd[1]') _email_selector = (By.XPATH, '//dt[text()="Email"]/following-sibling::dd[1]')
_phone_selector = (By.XPATH, '//dt[text()="Phone Number"]/following-sibling::dd[1]') _phone_selector = (By.XPATH, '//dt[text()="Phone Number"]/following-sibling::dd[1]')

View File

@@ -8,9 +8,9 @@ def add_default(apps, schema_editor):
Connector = apps.get_model('assets', 'Connector') Connector = apps.get_model('assets', 'Connector')
for cable_type in CableType.objects.all(): for cable_type in CableType.objects.all():
if cable_type.plug is None: if cable_type.plug is None:
cable_type.plug = Connector.first() cable_type.plug = Connector.objects.first()
if cable_type.socket is None: if cable_type.socket is None:
cable_type.socket = Connector.first() cable_type.socket = Connector.objects.first()
cable_type.save() cable_type.save()

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.1.7 on 2021-03-02 12:01
from django.db import migrations
def postgres_migration_prep(apps, schema_editor):
model = apps.get_model("assets", "Supplier")
fields = ["address", "email", "notes", "phone"]
for field in fields:
filter_param = {"{}__isnull".format(field): True}
update_param = {field: ""}
model.objects.filter(**filter_param).update(**update_param)
class Migration(migrations.Migration):
dependencies = [
('assets', '0019_fix_cabletype'),
]
operations = [
migrations.RunPython(postgres_migration_prep, migrations.RunPython.noop)
]

View File

@@ -1,4 +1,4 @@
# Generated by Django 3.1.5 on 2021-02-08 16:03 # Generated by Django 3.1.7 on 2021-03-02 12:04
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
@@ -7,7 +7,7 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('assets', '0019_fix_cabletype'), ('assets', '0020_auto_20210302_1201'),
] ]
operations = [ operations = [

View File

@@ -49,7 +49,7 @@ class Supplier(models.Model, RevisionMixin):
ordering = ['name'] ordering = ['name']
def get_absolute_url(self): def get_absolute_url(self):
return reverse('supplier_list') return reverse('supplier_detail', kwargs={'pk': self.pk})
def __str__(self): def __str__(self):
return self.name return self.name
@@ -82,6 +82,9 @@ class CableType(models.Model):
else: else:
return "Unknown" return "Unknown"
def get_absolute_url(self):
return reverse('cable_type_detail', kwargs={'pk': self.pk})
def get_available_asset_id(wanted_prefix=""): def get_available_asset_id(wanted_prefix=""):
sql = """ sql = """

View File

@@ -2,6 +2,9 @@
{% load widget_tweaks %} {% load widget_tweaks %}
{% block content %} {% block content %}
<div class="row justify-content-end">
{% include 'partials/asset_buttons.html' %}
</div>
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
{% include 'partials/asset_detail_form.html' %} {% include 'partials/asset_detail_form.html' %}

View File

@@ -34,7 +34,7 @@
}) })
.ajaxSelectPicker({ .ajaxSelectPicker({
ajax: { ajax: {
url: '{% url 'asset_search_json' %}', url: "{% url 'asset_search_json' %}",
type: "GET", type: "GET",
data: function () { data: function () {
let params = { let params = {

View File

@@ -11,7 +11,10 @@
<div class="btn-group"> <div class="btn-group">
{% button 'edit' url='asset_update' pk=object.asset_id %} {% button 'edit' url='asset_update' pk=object.asset_id %}
{% button 'duplicate' url='asset_duplicate' pk=object.asset_id %} {% button 'duplicate' url='asset_duplicate' pk=object.asset_id %}
<a type="button" class="btn btn-info" href="{% url 'asset_audit' object.asset_id %}"><i class="fas fa-certificate"></i> Audit</a> <a type="button" class="btn btn-info" href="{% url 'asset_audit' object.asset_id %}"><span class="fas fa-certificate"></span> Audit</a>
{% if object.is_cable %}
<a type="button" class="btn btn-primary" href="{% url 'generate_label' object.asset_id %}"><span class="fas fa-barcode"></span> Generate Label</a>
{% endif %}
</div> </div>
{% endif %} {% endif %}
{% if create or edit or duplicate %} {% if create or edit or duplicate %}

View File

@@ -17,11 +17,9 @@
{% else %} {% else %}
<dl> <dl>
<dt>Cable Type</dt> <dt>Cable Type</dt>
<dd>{{ object.cable_type|default_if_none:'-' }}</dd> <dd>{% if object.cable_type %}<a href="{{object.cable_type.get_absolute_url}}">{{ object.cable_type }}</a>{%else%}-{%endif%}</dd>
<dt>Length</dt> <dt>Length</dt>
<dd>{{ object.length|default_if_none:'-' }}m</dd> <dd>{{ object.length|default_if_none:'-' }}m</dd>
<dt>Cross Sectional Area</dt> <dt>Cross Sectional Area</dt>
<dd>{{ object.csa|default_if_none:'-' }}mm²</dd> <dd>{{ object.csa|default_if_none:'-' }}mm²</dd>
</dl> </dl>

View File

@@ -28,13 +28,13 @@
<dt>Children</dt> <dt>Children</dt>
{% if object.asset_parent.all %} {% if object.asset_parent.all %}
<div style="max-height: 200px; overflow-y: auto; -webkit-overflow-scrolling: touch; ">
{% for child in object.asset_parent.all %} {% for child in object.asset_parent.all %}
<dd> <dd>
<a href="{% url 'asset_detail' child.asset_id %}"> <a href="{% url 'asset_detail' child.asset_id %}">{{ child }}</a>
{{ child.asset_id }} - {{ child.description }}
</a>
</dd> </dd>
{% endfor %} {% endfor %}
</div>
{% else %} {% else %}
<dd><span>-</span></dd> <dd><span>-</span></dd>
{% endif %} {% endif %}

View File

@@ -1,4 +1,5 @@
{% load widget_tweaks %} {% load widget_tweaks %}
{% load linkornone from filters %}
<div class="card mb-2"> <div class="card mb-2">
<div class="card-header"> <div class="card-header">
Purchase Details Purchase Details
@@ -51,14 +52,11 @@
{% else %} {% else %}
<dl> <dl>
<dt>Purchased From</dt> <dt>Purchased From</dt>
<dd>{{ object.purchased_from|default_if_none:'-' }}</dd> <dd>{% if object.purchased_from %}<a href="{{object.purchased_from.get_absolute_url}}">{{ object.purchased_from }}</a>{%else%}-{%endif%}</dd>
<dt>Purchase Price</dt> <dt>Purchase Price</dt>
<dd>£{{ object.purchase_price|default_if_none:'-' }}</dd> <dd>£{{ object.purchase_price|default_if_none:'-' }}</dd>
<dt>Salvage Value</dt> <dt>Salvage Value</dt>
<dd>£{{ object.salvage_value|default_if_none:'-' }}</dd> <dd>£{{ object.salvage_value|default_if_none:'-' }}</dd>
<dt>Date Acquired</dt> <dt>Date Acquired</dt>
<dd>{{ object.date_acquired|default_if_none:'-' }}</dd> <dd>{{ object.date_acquired|default_if_none:'-' }}</dd>
{% if object.date_sold %} {% if object.date_sold %}

View File

@@ -16,6 +16,7 @@ urlpatterns = [
(views.AssetEdit.as_view()), name='asset_update'), (views.AssetEdit.as_view()), name='asset_update'),
path('asset/id/<str:pk>/duplicate/', permission_required_with_403('assets.add_asset') path('asset/id/<str:pk>/duplicate/', permission_required_with_403('assets.add_asset')
(views.AssetDuplicate.as_view()), name='asset_duplicate'), (views.AssetDuplicate.as_view()), name='asset_duplicate'),
path('asset/id/<str:pk>/label', login_required(views.GenerateLabel.as_view()), name='generate_label'),
path('cabletype/list/', login_required(views.CableTypeList.as_view()), name='cable_type_list'), path('cabletype/list/', login_required(views.CableTypeList.as_view()), name='cable_type_list'),
path('cabletype/create/', permission_required_with_403('assets.add_cable_type')(views.CableTypeCreate.as_view()), name='cable_type_create'), path('cabletype/create/', permission_required_with_403('assets.add_cable_type')(views.CableTypeCreate.as_view()), name='cable_type_create'),

View File

@@ -1,4 +1,5 @@
import simplejson import simplejson
import random
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.core import serializers from django.core import serializers
@@ -9,6 +10,11 @@ from django.utils import timezone
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views import generic from django.views import generic
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import get_object_or_404
from PIL import Image, ImageDraw, ImageFont
from barcode import Code39
from barcode.writer import ImageWriter
from PyRIGS.views import GenericListView, GenericDetailView, GenericUpdateView, GenericCreateView, ModalURLMixin, \ from PyRIGS.views import GenericListView, GenericDetailView, GenericUpdateView, GenericCreateView, ModalURLMixin, \
is_ajax, OEmbedView is_ajax, OEmbedView
@@ -338,3 +344,37 @@ class CableTypeUpdate(generic.UpdateView):
def get_success_url(self): def get_success_url(self):
return reverse("cable_type_detail", kwargs={"pk": self.object.pk}) return reverse("cable_type_detail", kwargs={"pk": self.object.pk})
class GenerateLabel(generic.View):
def get(self, request, pk):
black = (0, 0, 0)
white = (255, 255, 255)
size = (700, 200)
font = ImageFont.truetype("static/fonts/OpenSans-Regular.tff", 20)
obj = get_object_or_404(models.Asset, asset_id=pk)
asset_id = "Asset: {}".format(obj.asset_id)
length = "Length: {}m".format(obj.length)
csa = "CSA: {}mm²".format(obj.csa)
image = Image.new("RGB", size, white)
logo = Image.open("static/imgs/square_logo.png")
draw = ImageDraw.Draw(image)
draw.text((210, 140), asset_id, fill=black, font=font)
draw.text((210, 170), length, fill=black, font=font)
draw.text((350, 170), csa, fill=black, font=font)
draw.multiline_text((500, 140), "TEC PA & Lighting\n(0115) 84 68720", fill=black, font=font)
barcode = Code39(str(obj.asset_id), writer=ImageWriter())
logo_size = (200, 200)
image.paste(logo.resize(logo_size, Image.ANTIALIAS))
barcode_image = barcode.render(writer_options={"quiet_zone": 0, "write_text": False})
width, height = barcode_image.size
image.paste(barcode_image.crop((0, 0, width, 135)), (int(((size[0] + logo_size[0]) - width) / 2), 0))
response = HttpResponse(content_type="image/png")
image.save(response, "PNG")
return response

View File

@@ -119,4 +119,18 @@
background: #222; background: #222;
color: $gray-100; color: $gray-100;
} }
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus {
border: 1px solid $info;
-webkit-text-fill-color: white;
-webkit-box-shadow: 0 0 0px 1000px rgba($info, .3) inset;
transition: background-color 5000s ease-in-out 0s;
}
} }

View File

@@ -177,6 +177,11 @@ svg {
white-space: no-wrap; white-space: no-wrap;
} }
span.fas {
padding-left: 0.1em !important;
padding-right: 0.1em !important;
}
html.embedded { html.embedded {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -15,7 +15,6 @@
<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 rel="preload" href="{% static 'fonts/fa-solid-900.woff2' %}" as="font" type="font/woff2" crossorigin>
<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 %}
@@ -32,26 +31,26 @@
<a class="skip-link" href='#main'>Skip to content</a> <a class="skip-link" href='#main'>Skip to content</a>
{% include "analytics.html" %} {% include "analytics.html" %}
{% block navbar %} {% block navbar %}
<nav class="navbar navbar-expand-lg navbar-dark bg-dark flex-nowrap" role="navigation"> <nav class="navbar navbar-expand-lg navbar-dark bg-dark" role="navigation">
<a class="navbar-brand" href="{% if request.user.is_authenticated %}https://members.nottinghamtec.co.uk{%else%}https://nottinghamtec.co.uk{%endif%}">
<img src="{% static 'imgs/logo.webp' %}" width="40" height="40" alt="TEC's Logo: Serif 'TEC' vertically next to a blue box with the words 'PA and Lighting', surrounded by graduated rings">
</a>
<div class="container"> <div class="container">
{% block titleheader %} <a class="navbar-brand" style="position: absolute; left:0.5em; top: 2px;" href="{% if request.user.is_authenticated %}https://members.nottinghamtec.co.uk{%else%}https://nottinghamtec.co.uk{%endif%}">
{% endblock %} <img src="{% static 'imgs/logo.webp' %}" width="40" height="40" alt="TEC's Logo: Serif 'TEC' vertically next to a blue box with the words 'PA and Lighting', surrounded by graduated rings" id="logo">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> </a>
<span class="navbar-toggler-icon"></span> {% block titleheader %}
</button> {% endblock %}
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <button class="navbar-toggler ml-auto" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation" onclick="document.getElementById('logo').classList.toggle('d-none');">
<ul class="navbar-nav"> <span class="navbar-toggler-icon"></span>
{% block titleelements %} </button>
{% endblock %} <div class="collapse navbar-collapse justify-content-between" id="navbarSupportedContent">
</ul> <ul class="navbar-nav">
<ul class="navbar-nav ml-auto"> {% block titleelements %}
{% block titleelements_right %} {% endblock %}
{% endblock %} </ul>
</ul> <ul class="navbar-nav align-self-end">
</div> {% block titleelements_right %}
{% endblock %}
</ul>
</div>
</div> </div>
</nav> </nav>
{% endblock %} {% endblock %}

View File

@@ -1,10 +1,10 @@
{% if user.is_authenticated %} {% if user.is_authenticated %}
<form id="searchForm" class="form-inline flex-nowrap mx-md-3 px-2 border border-light rounded" role="form" method="GET" action="{% url 'event_archive' %}"> <form id="searchForm" class="form-inline flex-nowrap mx-md-3 px-2 border border-light rounded w-75" role="form" method="GET" action="{% url 'event_archive' %}">
<div class="input-group input-group-sm flex-nowrap"> <div class="input-group input-group-sm flex-nowrap">
<div class="input-group-prepend"> <div class="input-group-prepend">
<input id="id_search_input" type="search" name="q" class="form-control form-control-sm" placeholder="Search..." value="{{ request.GET.q }}" /> <input id="id_search_input" type="search" name="q" class="form-control form-control-sm" placeholder="Search..." value="{{ request.GET.q }}" />
</div> </div>
<select id="search-options" class="custom-select form-control" style="border-top-right-radius: 0px; border-bottom-right-radius: 0px; width: 20ch;"> <select id="search-options" class="custom-select form-control" style="border-top-right-radius: 0px; border-bottom-right-radius: 0px; width: 15ch;">
<option selected data-action="{% url 'event_archive' %}" href="#">Events</option> <option selected data-action="{% url 'event_archive' %}" href="#">Events</option>
<option data-action="{% url 'person_list' %}" href="#">People</option> <option data-action="{% url 'person_list' %}" href="#">People</option>
<option data-action="{% url 'organisation_list' %}" href="#">Organisations</option> <option data-action="{% url 'organisation_list' %}" href="#">Organisations</option>
@@ -17,7 +17,7 @@
</select> </select>
</div> </div>
<button class="btn btn-info form-control form-control-sm btn-sm w-25" style="border-top-left-radius: 0px;border-bottom-left-radius: 0px;"><span class="fas fa-search"></span><span class="sr-only"> Search</span></button> <button class="btn btn-info form-control form-control-sm btn-sm w-25" style="border-top-left-radius: 0px;border-bottom-left-radius: 0px;"><span class="fas fa-search"></span><span class="sr-only"> Search</span></button>
<a href="{% url 'search_help' %}" class="nav-link modal-href ml-2"><span class="fas fa-question-circle"></span></a> <a href="{% url 'search_help' %}" class="nav-link modal-href ml-1"><span class="fas fa-question-circle"></span></a>
</form> </form>
{% endif %} {% endif %}

View File

@@ -1,29 +1,36 @@
{% extends 'base_rigs.html' %} {% extends 'base_rigs.html' %}
{% load widget_tweaks %} {% load widget_tweaks %}
{% load static %}
{% block title %}Registration{% endblock %} {% block title %}Registration{% endblock %}
{% block content %} {% block content %}
<div class="col-sm-10 col-sm-offset-1"> <div style="background-image: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url({% static 'imgs/wof2014-1-small.jpg' %}); background-repeat: no-repeat; background-size: cover; width: 100vw; height: 100vh; position: relative; left: 50%; right: 50%; margin-left: -50vw; margin-right: -50vw; margin-top: -24px; padding-top: 24px;">
<h3>New User Registration</h3> <div class="container">
{% if form.errors or supplement_form.errors %} <div class="card">
<div class="alert alert-danger"> <h3 class="card-header">New User Registration</h3>
{{form.errors}} <div class="card-body">
{{supplement_form.errors}} {% if form.errors or supplement_form.errors %}
</div> <div class="alert alert-danger">
{% endif %} {{form.errors}}
{{supplement_form.errors}}
<div class="col-sm-8 col-sm-offset-2"> </div>
<form action="" method="post" class="" role="form">{% csrf_token %} {% endif %}
{% for field in form %} <div class="col-sm-8 col-sm-offset-2">
<div class="form-group"> <form method="post" role="form">{% csrf_token %}
<label for="{{ field.id_for_label }}" class="col-form-label col-sm-4">{{ field.label }}</label> {% for field in form %}
<div class="controls col-sm-8"> <div class="form-group form-row">
{% render_field field class+="form-control" placeholder=field.label %} <label for="{{ field.id_for_label }}" class="col-form-label col-sm-4">{{ field.label }}</label>
</div> <div class="controls col-sm-8">
</div> {% render_field field class+="form-control" placeholder=field.label %}
{% endfor %} </div>
<p><input type="submit" value="Register" class="btn btn-primary pull-right"></p> </div>
</form> {% endfor %}
<p><input type="submit" value="Register" class="btn btn-primary pull-right"></p>
</form>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -1,15 +1,24 @@
from captcha.fields import ReCaptchaField from hcaptcha.fields import hCaptchaField
from django import forms from django import forms
from django.contrib.auth.forms import (AuthenticationForm, PasswordResetForm, from django.contrib.auth.forms import (AuthenticationForm, PasswordResetForm,
UserChangeForm, UserCreationForm) UserChangeForm, UserCreationForm)
from django.conf import settings
from registration.forms import RegistrationFormUniqueEmail from registration.forms import RegistrationFormUniqueEmail
from RIGS import models from RIGS import models
class CaptchaField(hCaptchaField):
def validate(self, value):
# Skip validation if we're testing FIXME: Arona, y u so lazy
if settings.HCAPTCHA_SITEKEY != '10000000-ffff-ffff-ffff-000000000001':
super().validate(value)
# Registration # Registration
class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail): class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
captcha = ReCaptchaField() hcaptcha = CaptchaField()
class Meta: class Meta:
model = models.Profile model = models.Profile
@@ -41,7 +50,7 @@ class EmbeddedAuthenticationForm(CheckApprovedForm):
class PasswordReset(PasswordResetForm): class PasswordReset(PasswordResetForm):
captcha = ReCaptchaField(label='Captcha') hcaptcha = CaptchaField()
class ProfileCreationForm(UserCreationForm): class ProfileCreationForm(UserCreationForm):

View File

@@ -1,4 +1,4 @@
<li class="nav-item dropdown" id="user"> <li class="nav-item dropdown align-self-end" id="user">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Hi {{ user.first_name }} Hi {{ user.first_name }}

View File

@@ -1,8 +1,10 @@
import os import os
import re import re
import time
from django.core import mail from django.core import mail
from django.test import LiveServerTestCase from django.test import LiveServerTestCase
from django.test.utils import override_settings
from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.keys import Keys
from PyRIGS.tests.base import create_browser from PyRIGS.tests.base import create_browser
@@ -13,14 +15,12 @@ from RIGS import models
class UserRegistrationTest(LiveServerTestCase): class UserRegistrationTest(LiveServerTestCase):
def setUp(self): def setUp(self):
self.browser = create_browser() self.browser = create_browser()
self.browser.implicitly_wait(5) # Set implicit wait session wide
self.browser.implicitly_wait(3) # Set implicit wait session wide
os.environ['RECAPTCHA_TESTING'] = 'True'
def tearDown(self): def tearDown(self):
self.browser.quit() self.browser.quit()
os.environ['RECAPTCHA_TESTING'] = 'False'
@override_settings(DEBUG=True)
def test_registration(self): def test_registration(self):
# Navigate to the registration page # Navigate to the registration page
self.browser.get(self.live_server_url + '/user/register/') self.browser.get(self.live_server_url + '/user/register/')
@@ -61,9 +61,11 @@ class UserRegistrationTest(LiveServerTestCase):
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( time.sleep(1)
"return function() {jQuery('#g-recaptcha-response').val('PASSED'); return 0}()") self.browser.switch_to.frame(self.browser.find_element_by_tag_name("iframe"))
self.browser.find_element_by_id('anchor').click()
self.browser.switch_to.default_content()
time.sleep(3)
# Submit incorrect form # Submit incorrect form
submit = self.browser.find_element_by_xpath("//input[@type='submit']") submit = self.browser.find_element_by_xpath("//input[@type='submit']")
submit.click() submit.click()
@@ -85,9 +87,6 @@ class UserRegistrationTest(LiveServerTestCase):
# Correct error # Correct error
password1.send_keys('correcthorsebatterystaple') password1.send_keys('correcthorsebatterystaple')
password2.send_keys('correcthorsebatterystaple') password2.send_keys('correcthorsebatterystaple')
self.browser.execute_script("console.log('Hello, world!')")
self.browser.execute_script(
"return function() {jQuery('#g-recaptcha-response').val('PASSED'); return 0}()")
# Submit again # Submit again
password2.send_keys(Keys.ENTER) password2.send_keys(Keys.ENTER)
@@ -126,8 +125,6 @@ class UserRegistrationTest(LiveServerTestCase):
# Expected to fail as not approved # Expected to fail as not approved
username.send_keys('TestUsername') username.send_keys('TestUsername')
password.send_keys('correcthorsebatterystaple') password.send_keys('correcthorsebatterystaple')
self.browser.execute_script(
"return function() {jQuery('#g-recaptcha-response').val('PASSED'); return 0}()")
password.send_keys(Keys.ENTER) password.send_keys(Keys.ENTER)
# Test approval # Test approval
@@ -149,8 +146,6 @@ class UserRegistrationTest(LiveServerTestCase):
username.send_keys('TestUsername') username.send_keys('TestUsername')
password = self.browser.find_element_by_id('id_password') password = self.browser.find_element_by_id('id_password')
password.send_keys('correcthorsebatterystaple') password.send_keys('correcthorsebatterystaple')
self.browser.execute_script(
"return function() {jQuery('#g-recaptcha-response').val('PASSED'); return 0}()")
password.send_keys(Keys.ENTER) password.send_keys(Keys.ENTER)
# Check we are logged in # Check we are logged in