mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-02-12 17:49:41 +00:00
Compare commits
41 Commits
769d983e3d
...
revert-594
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ded31adbb4 | ||
|
|
6c8eb380fd | ||
|
3123d3899c
|
|||
|
|
c93c04ec6e | ||
| 6bf8d56ce8 | |||
|
8246071b8c
|
|||
|
06fa1a3b1b
|
|||
|
70abfaf2ae
|
|||
|
02a5a94fbb
|
|||
|
|
ddce9752c6 | ||
|
|
5a16e06bed | ||
|
|
5160a61a62 | ||
|
|
607f282ef6 | ||
|
|
d71bb81edf | ||
|
|
f46915233e | ||
|
|
0c900d2447 | ||
|
953b691cc2
|
|||
|
17fa447861
|
|||
|
|
409125c8a3 | ||
|
|
03d996eb66 | ||
|
|
1e40916a94 | ||
|
|
eae3f762b7 | ||
|
|
7a387e8724 | ||
|
|
4f42219821 | ||
|
d7d2f93295
|
|||
| 2a2ce742b0 | |||
|
|
68d3605230 | ||
|
1fff150566
|
|||
|
240ff25c63
|
|||
|
e265ad58a6
|
|||
|
1a32ef424b
|
|||
|
|
44c92fc859 | ||
|
|
1af182eaa1 | ||
|
|
425a697743 | ||
|
|
b7011e368b | ||
| 7d2f8d2dc8 | |||
| 5a54092771 | |||
|
|
e8a8f2bf0d | ||
|
|
3984c17605 | ||
|
|
b2209eef4e | ||
|
e7c4f7f73d
|
8
.github/workflows/django.yml
vendored
8
.github/workflows/django.yml
vendored
@@ -14,9 +14,9 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
PYTHONDONTWRITEBYTECODE: 1
|
PYTHONDONTWRITEBYTECODE: 1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: 3.9
|
python-version: 3.9
|
||||||
cache: 'pipenv'
|
cache: 'pipenv'
|
||||||
@@ -27,7 +27,7 @@ jobs:
|
|||||||
# if: steps.pcache.outputs.cache-hit != 'true'
|
# if: steps.pcache.outputs.cache-hit != 'true'
|
||||||
- name: Cache Static Files
|
- name: Cache Static Files
|
||||||
id: static-cache
|
id: static-cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: 'pipeline/built_assets'
|
path: 'pipeline/built_assets'
|
||||||
key: ${{ hashFiles('package-lock.json') }}-${{ hashFiles('pipeline/source_assets') }}
|
key: ${{ hashFiles('package-lock.json') }}-${{ hashFiles('pipeline/source_assets') }}
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
pipenv run python3 manage.py collectstatic --noinput
|
pipenv run python3 manage.py collectstatic --noinput
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: pipenv run pytest -n auto --cov
|
run: pipenv run pytest -n auto --cov
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v4
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: failure-screenshots ${{ matrix.test-group }}
|
name: failure-screenshots ${{ matrix.test-group }}
|
||||||
|
|||||||
14
Pipfile
14
Pipfile
@@ -28,18 +28,18 @@ django-reversion = "~=3.0.9"
|
|||||||
django-widget-tweaks = "~=1.4.8"
|
django-widget-tweaks = "~=1.4.8"
|
||||||
django-htmlmin = "~=0.11.0"
|
django-htmlmin = "~=0.11.0"
|
||||||
envparse = "*"
|
envparse = "*"
|
||||||
gunicorn = "~=20.0.4"
|
gunicorn = "~=22.0.0"
|
||||||
icalendar = "~=4.0.7"
|
icalendar = "~=4.0.7"
|
||||||
idna = "~=2.10"
|
idna = "~=3.7"
|
||||||
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 = "~=9.3.0"
|
Pillow = "~=10.0.1"
|
||||||
premailer = "~=3.7.0"
|
premailer = "~=3.7.0"
|
||||||
progress = "~=1.5"
|
progress = "~=1.5"
|
||||||
psutil = "~=5.8.0"
|
psutil = "~=5.8.0"
|
||||||
psycopg2 = "~=2.8.6"
|
psycopg2 = "~=2.8.6"
|
||||||
Pygments = "~=2.7.4"
|
Pygments = "~=2.15.0"
|
||||||
pyparsing = "~=2.4.7"
|
pyparsing = "~=2.4.7"
|
||||||
PyPDF2 = "~=1.27.5"
|
PyPDF2 = "~=1.27.5"
|
||||||
PyPOM = "~=2.2.4"
|
PyPOM = "~=2.2.4"
|
||||||
@@ -47,17 +47,17 @@ python-dateutil = "~=2.8.1"
|
|||||||
pytoml = "~=0.1.21"
|
pytoml = "~=0.1.21"
|
||||||
pytz = "~=2020.5"
|
pytz = "~=2020.5"
|
||||||
reportlab = "*"
|
reportlab = "*"
|
||||||
requests = "~=2.31.0"
|
requests = "~=2.32.0"
|
||||||
retrying = "~=1.3.3"
|
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.2"
|
sqlparse = "~=0.5.0"
|
||||||
static3 = "~=0.7.0"
|
static3 = "~=0.7.0"
|
||||||
svg2rlg = "~=0.3"
|
svg2rlg = "~=0.3"
|
||||||
tini = "~=3.0.1"
|
tini = "~=3.0.1"
|
||||||
tornado = "~=6.3"
|
tornado = "~=6.3"
|
||||||
urllib3 = "~=1.26.5"
|
urllib3 = "~=1.26.19"
|
||||||
whitenoise = "~=5.2.0"
|
whitenoise = "~=5.2.0"
|
||||||
yolk = "~=0.4.3"
|
yolk = "~=0.4.3"
|
||||||
zipp = "~=3.4.0"
|
zipp = "~=3.4.0"
|
||||||
|
|||||||
811
Pipfile.lock
generated
811
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,8 @@ if DEBUG:
|
|||||||
ALLOWED_HOSTS.append('localhost')
|
ALLOWED_HOSTS.append('localhost')
|
||||||
ALLOWED_HOSTS.append('example.com')
|
ALLOWED_HOSTS.append('example.com')
|
||||||
ALLOWED_HOSTS.append('127.0.0.1')
|
ALLOWED_HOSTS.append('127.0.0.1')
|
||||||
|
ALLOWED_HOSTS.append('.app.github.dev')
|
||||||
|
CSRF_TRUSTED_ORIGINS = ALLOWED_HOSTS
|
||||||
|
|
||||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||||
if not DEBUG:
|
if not DEBUG:
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class Profile(AbstractUser):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def users_awaiting_approval_count(cls):
|
def users_awaiting_approval_count(cls):
|
||||||
# last_login = None ensures we only pick up genuinely new users, not those that have been deactivated for inactivity
|
# last_login = None ensures we only pick up genuinely new users, not those that have been deactivated for inactivity
|
||||||
return Profile.objects.filter(is_approved=False, last_login=None).count()
|
return Profile.objects.filter(is_approved=False, last_login=None, date_joined_date=timezone.now().date()).count()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import urllib.error
|
|||||||
import urllib.parse
|
import urllib.parse
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
import datetime
|
||||||
|
|
||||||
from PyPDF2 import PdfFileReader, PdfFileMerger
|
from PyPDF2 import PdfFileReader, PdfFileMerger
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -110,7 +111,7 @@ def send_admin_awaiting_approval_email(user, request, **kwargs):
|
|||||||
if admin.last_emailed is None or admin.last_emailed + settings.EMAIL_COOLDOWN <= timezone.now():
|
if admin.last_emailed is None or admin.last_emailed + settings.EMAIL_COOLDOWN <= timezone.now():
|
||||||
context = {
|
context = {
|
||||||
'request': request,
|
'request': request,
|
||||||
'link_suffix': reverse("admin:RIGS_profile_changelist") + '?is_approved__exact=0',
|
'link_suffix': reverse("admin:RIGS_profile_changelist") + f'?is_approved__exact=0&date_joined__date={timezone.now().date()}',
|
||||||
'number_of_users': models.Profile.users_awaiting_approval_count(),
|
'number_of_users': models.Profile.users_awaiting_approval_count(),
|
||||||
'to_name': admin.first_name
|
'to_name': admin.first_name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
var calendarEl = document.getElementById('calendar');
|
var calendarEl = document.getElementById('calendar');
|
||||||
|
|
||||||
calendar = new FullCalendar.Calendar(calendarEl, {
|
calendar = new FullCalendar.Calendar(calendarEl, {
|
||||||
|
firstDay: 1,
|
||||||
themeSystem: 'bootstrap',
|
themeSystem: 'bootstrap',
|
||||||
aspectRatio: 1.5,
|
aspectRatio: 1.5,
|
||||||
eventTimeFormat: {
|
eventTimeFormat: {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12" style="container-type: inline-size;">
|
||||||
{% with object_list as events %}
|
{% with object_list as events %}
|
||||||
{% include 'partials/event_table.html' %}
|
{% include 'partials/event_table.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|||||||
@@ -166,7 +166,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-12 text-right">
|
<div class="col-12 text-right">
|
||||||
{% button 'edit' url='pt_edit' pk=object.pk %}
|
{% button 'edit' url='pt_edit' pk=object.pk %}
|
||||||
{% button 'view' url='event_detail' pk=object.pk text="Event" %}
|
{% button 'view' url='event_detail' pk=object.event.pk text="Event" %}
|
||||||
{% include 'partials/review_status.html' with perm=perms.RIGS.review_power review='pt_review' %}
|
{% include 'partials/review_status.html' with perm=perms.RIGS.review_power review='pt_review' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 text-right">
|
<div class="col-12 text-right">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<h5 class="py-3"><a class="btn btn-info" data-toggle="collapse" href="#values" aria-expanded="false" aria-controls="values">View Threshold Values</a></h5>
|
<h5 class="py-3"><a class="btn btn-info" data-toggle="collapse" href="#values" aria-expanded="false" aria-controls="values">View Threshold Values</a></h5>
|
||||||
<div class="row collapse" id="values">
|
<div class="row collapse" id="values">
|
||||||
<div class="table-responsive">
|
<div class="col-md-6 col-sm-12">
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -33,17 +33,20 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Distro</th>
|
<th scope="row">Distro</th>
|
||||||
<th scope="row">Max PSSC (kA)</th>
|
<th scope="row">Max PSCC with Single Phase Supply (kA)</th>
|
||||||
|
<th scope="row">Max PSCC with Three Phase Supply (kA)</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Intel & Toblerone distros</td>
|
<td>Intel & Toblerone distros</td>
|
||||||
<td>6</td>
|
<td>6</td>
|
||||||
|
<td>3</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>All other distros</td>
|
<td>All other distros</td>
|
||||||
<td>10</td>
|
<td>10</td>
|
||||||
|
<td>5</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div>
|
<div id="event_status">
|
||||||
<span class="badge badge-{% if event.confirmed %}success{% elif event.cancelled %}dark{% else %}warning{% endif %}">Status: {{ event.get_status_display }}</span>
|
<span class="badge badge-{% if event.confirmed %}success{% elif event.cancelled %}dark{% else %}warning{% endif %}">Status: {{ event.get_status_display }}</span>
|
||||||
{% if event.is_rig %}
|
{% if event.is_rig %}
|
||||||
{% if event.sum_total > 0 %}
|
{% if event.sum_total > 0 %}
|
||||||
|
|||||||
@@ -1,105 +1,195 @@
|
|||||||
{% load namewithnotes from filters %}
|
{% load namewithnotes from filters %}
|
||||||
{% load markdown_tags %}
|
{% load markdown_tags %}
|
||||||
<div class="table-responsive">
|
<style>
|
||||||
<table class="table mb-0" id="event_table">
|
#event_table {
|
||||||
<thead>
|
display: grid;
|
||||||
<tr>
|
grid-template-columns: max-content min-content minmax(max-content, 1fr) max-content;
|
||||||
<th scope="col">#</th>
|
column-gap: 1em;
|
||||||
<th scope="col">Dates & Times</th>
|
}
|
||||||
<th scope="col">Event Details</th>
|
.eventgrid {
|
||||||
<th scope="col">MIC</th>
|
display: inherit;
|
||||||
</tr>
|
grid-column: 1/5;
|
||||||
</thead>
|
grid-template-columns: subgrid;
|
||||||
<tbody>
|
padding: 1em;
|
||||||
{% for event in events %}
|
dt, dd { display: block; float: left; }
|
||||||
<tr class="{% if event.cancelled %}
|
dt { clear: both; }
|
||||||
table-secondary
|
dd { float: right; }
|
||||||
{% elif not event.is_rig %}
|
}
|
||||||
table-info
|
.grid-header {
|
||||||
{% elif not event.mic %}
|
border-bottom: 1px solid grey;
|
||||||
table-danger
|
border-top: 1px solid grey;
|
||||||
{% elif event.confirmed and event.authorised %}
|
}
|
||||||
{% if event.dry_hire or event.riskassessment %}
|
#event_status {
|
||||||
table-success
|
grid-column-start: 3;
|
||||||
{% else %}
|
}
|
||||||
table-warning
|
#event_mic {
|
||||||
{% endif %}
|
grid-row-start: 1;
|
||||||
{% else %}
|
grid-column-start: 4;
|
||||||
table-warning
|
}
|
||||||
{% endif %}" {% if event.cancelled %}style="opacity: 50% !important;"{% endif %} id="event_row">
|
.c-none {
|
||||||
<!---Number-->
|
display: none;
|
||||||
<th scope="row" id="event_number">{{ event.display_id }}</th>
|
}
|
||||||
<!--Dates & Times-->
|
.c-inline {
|
||||||
<td id="event_dates" style="text-align: justify;">
|
display: inline;
|
||||||
{% if not event.cancelled %}
|
}
|
||||||
{% if event.meet_at %}
|
@container (width <= 500px) {
|
||||||
<span class="text-nowrap">Meet: <strong>{{ event.meet_at|date:"D d/m/Y H:i" }}</strong></span>
|
#event_table {
|
||||||
{% endif %}
|
grid-template-columns: 1fr !important;
|
||||||
{% if event.access_at %}
|
}
|
||||||
<br><span class="text-nowrap">Access: <strong>{{ event.access_at|date:"D d/m/Y H:i" }}</strong></span>
|
.eventgrid {
|
||||||
{% endif %}
|
grid-column: 1/1 !important;
|
||||||
{% endif %}
|
padding: 0.5em;
|
||||||
<span class="text-nowrap">Start: <strong>{{ event.start_date|date:"D d/m/Y" }}
|
}
|
||||||
{% if event.has_start_time %}
|
.grid-header {
|
||||||
{{ event.start_time|date:"H:i" }}
|
display: none;
|
||||||
{% endif %}</strong>
|
}
|
||||||
</span>
|
#event_dates {
|
||||||
{% if event.end_date %}
|
order: 2;
|
||||||
<br>
|
}
|
||||||
<span class="text-nowrap">End: {% if event.end_date != event.start_date %}<strong>{{ event.end_date|date:"D d/m/Y" }}{% endif %}
|
#event_status {
|
||||||
{% if event.has_end_time %}
|
order: 3;
|
||||||
{{ event.end_time|date:"H:i" }}
|
}
|
||||||
{% endif %}</strong>
|
#event_mic {
|
||||||
</span>
|
grid-row-start: auto;
|
||||||
{% endif %}
|
grid-column-start: 4;
|
||||||
</td>
|
}
|
||||||
<!---Details-->
|
}
|
||||||
<td id="event_details" class="w-100">
|
@container (width <= 700px) {
|
||||||
<h4>
|
#event_table {
|
||||||
<a href="{% url 'event_detail' event.pk %}">
|
grid-template-columns: max-content;
|
||||||
{{ event.name }}
|
column-gap: 0.5em;
|
||||||
</a>
|
}
|
||||||
{% if event.venue %}
|
.eventgrid {
|
||||||
<small>at {{ event.venue|namewithnotes:'venue_detail' }}</small>
|
grid-column: 1/3;
|
||||||
{% endif %}
|
border: 1px solid grey;
|
||||||
{% if event.dry_hire %}
|
}
|
||||||
<span class="badge badge-secondary">Dry Hire</span>
|
#event_dates {
|
||||||
{% endif %}
|
grid-row: 2;
|
||||||
</h4>
|
grid-column: 1;
|
||||||
{% if event.is_rig and not event.cancelled %}
|
}
|
||||||
<h5>
|
#event_number {
|
||||||
<a href="{{ event.person.get_absolute_url }}">{{ event.person.name }}</a>
|
grid-row: 1;
|
||||||
{% if event.organisation %}
|
grid-column: 1;
|
||||||
for <a href="{{ event.organisation.get_absolute_url }}">{{ event.organisation.name }}</a>
|
}
|
||||||
{% endif %}
|
#event_mic {
|
||||||
</h5>
|
grid-column: 2;
|
||||||
{% endif %}
|
}
|
||||||
{% if not event.cancelled and event.description %}
|
#event_status {
|
||||||
<p>{{ event.description|markdown }}</p>
|
grid-column: span 2;
|
||||||
{% endif %}
|
}
|
||||||
{% include 'partials/event_status.html' %}
|
.grid-header, .c-md-none {
|
||||||
</td>
|
display: none;
|
||||||
<!---MIC-->
|
}
|
||||||
<td id="event_mic" class="text-nowrap">
|
}
|
||||||
{% if event.mic %}
|
@container (width > 700px) {
|
||||||
{% if perms.RIGS.view_profile %}
|
.c-lg-block {
|
||||||
<a href="{% url 'profile_detail' event.mic.pk %}" class="modal-href">
|
display: block;
|
||||||
{% endif %}
|
}
|
||||||
<img src="{{ event.mic.profile_picture }}" class="event-mic-photo"/>
|
.c-lg-inline {
|
||||||
{{ event.mic }}
|
display: inline;
|
||||||
{% if perms.RIGS.view_profile %}
|
}
|
||||||
</a>
|
.c-lg-none, .c-md-none {
|
||||||
{% endif %}
|
display: none;
|
||||||
{% elif event.is_rig %}
|
}
|
||||||
<span class="fas fa-user-slash"></span>
|
}
|
||||||
{% endif %}
|
</style>
|
||||||
</td>
|
<div id="event_table">
|
||||||
</tr>
|
<div class="eventgrid grid-header font-weight-bold">
|
||||||
{% empty %}
|
<div id="event_number">#</div>
|
||||||
<tr class="bg-warning">
|
<div id="event_dates">Dates & Times</div>
|
||||||
<td colspan="4">No events found</td>
|
<div>Event Details</div>
|
||||||
</tr>
|
<div id="event_mic">MIC</div>
|
||||||
{% endfor %}
|
</div>
|
||||||
</tbody>
|
{% for event in events %}
|
||||||
</table>
|
<div class="eventgrid {% if event.cancelled %}
|
||||||
|
table-secondary
|
||||||
|
{% elif not event.is_rig %}
|
||||||
|
table-info
|
||||||
|
{% elif not event.mic %}
|
||||||
|
table-danger
|
||||||
|
{% elif event.confirmed and event.authorised %}
|
||||||
|
{% if event.dry_hire or event.riskassessment %}
|
||||||
|
table-success
|
||||||
|
{% else %}
|
||||||
|
table-warning
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
table-warning
|
||||||
|
{% endif %}" {% if event.cancelled %}style="opacity: 50% !important;"{% endif %} id="event_row">
|
||||||
|
<!---Number-->
|
||||||
|
<div class="font-weight-bold c-none c-lg-block" id="event_number">{{ event.display_id }}</div>
|
||||||
|
<!--Dates & Times-->
|
||||||
|
<div id="event_dates" style="min-width: 180px;">
|
||||||
|
<dl>
|
||||||
|
{% if not event.cancelled %}
|
||||||
|
{% if event.meet_at %}
|
||||||
|
<dt class="font-weight-normal">Meet:</dt>
|
||||||
|
<dd class="text-nowrap font-weight-bold text-lg-right">{{ event.meet_at|date:"D d/m/Y H:i" }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
{% if event.access_at %}
|
||||||
|
<dt class="font-weight-normal">Access:</dt>
|
||||||
|
<dd class="text-nowrap font-weight-bold text-lg-right">{{ event.access_at|date:"D d/m/Y H:i" }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
<dt class="font-weight-normal">Start:</dt>
|
||||||
|
<dd class="text-nowrap font-weight-bold text-lg-right">{{ event.start_date|date:"D d/m/Y" }}
|
||||||
|
{% if event.has_start_time %}
|
||||||
|
{{ event.start_time|date:"H:i" }}
|
||||||
|
{% endif %}
|
||||||
|
</dd>
|
||||||
|
{% if event.end_date %}
|
||||||
|
<dt class="font-weight-normal">End:</dt>
|
||||||
|
<dd class="text-nowrap font-weight-bold text-lg-right">{{ event.end_date|date:"D d/m/Y" }}
|
||||||
|
{% if event.has_end_time %}
|
||||||
|
{{ event.end_time|date:"H:i" }}
|
||||||
|
{% endif %}
|
||||||
|
</dd>
|
||||||
|
{% endif %}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<!---Details-->
|
||||||
|
<div id="event_details" class="w-100">
|
||||||
|
<h4>
|
||||||
|
<a href="{% url 'event_detail' event.pk %}">
|
||||||
|
<span class="c-inline c-lg-none">{{ event }}</span><span class="c-none c-lg-inline">{{ event.name }}</span>
|
||||||
|
</a>
|
||||||
|
{% if event.dry_hire %}
|
||||||
|
<span class="badge badge-secondary">Dry Hire</span>
|
||||||
|
{% endif %}
|
||||||
|
<br class="c-none c-lg-inline">
|
||||||
|
{% if event.venue %}
|
||||||
|
<small>at {{ event.venue|namewithnotes:'venue_detail' }}</small>
|
||||||
|
{% endif %}
|
||||||
|
</h4>
|
||||||
|
{% if event.is_rig and not event.cancelled %}
|
||||||
|
<h5>
|
||||||
|
<a href="{{ event.person.get_absolute_url }}">{{ event.person.name }}</a>
|
||||||
|
{% if event.organisation %}
|
||||||
|
for <a href="{{ event.organisation.get_absolute_url }}">{{ event.organisation.name }}</a>
|
||||||
|
{% endif %}
|
||||||
|
</h5>
|
||||||
|
{% endif %}
|
||||||
|
{% if not event.cancelled and event.description %}
|
||||||
|
<p>{{ event.description|markdown }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% include 'partials/event_status.html' %}
|
||||||
|
<!---MIC-->
|
||||||
|
<div id="event_mic" class="text-nowrap">
|
||||||
|
<span class="c-md-none align-middle">MIC:</span>
|
||||||
|
{% if event.mic %}
|
||||||
|
{% if perms.RIGS.view_profile %}
|
||||||
|
<a href="{% url 'profile_detail' event.mic.pk %}" class="modal-href">
|
||||||
|
{% endif %}
|
||||||
|
<img src="{{ event.mic.profile_picture }}" class="event-mic-photo"/>
|
||||||
|
{{ event.mic }}
|
||||||
|
{% if perms.RIGS.view_profile %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% elif event.is_rig %}
|
||||||
|
<span class="fas fa-exclamation"></span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row align-items-center justify-content-between py-2 align-middle">
|
<div class="row align-items-center justify-content-between py-2 align-middle">
|
||||||
<div class="col-sm-12 col-md align-middle">
|
<div class="col-sm-12 col-md align-middle d-flex flex-wrap">
|
||||||
Key: <span class="table-success mr-1 px-2 rounded">Ready</span><span class="table-warning mr-1 px-2 rounded">Action Required</span><span class="table-danger mr-1 px-2 rounded">Needs MIC</span><span class="table-secondary mr-1 px-2 rounded">Cancelled</span><span class="table-info px-2 rounded">Non-Rig</span>
|
Key: <span class="table-success mr-1 px-2 rounded">Ready</span><span class="table-warning mr-1 px-2 rounded text-nowrap">Action Required</span><span class="table-danger mr-1 px-2 rounded text-nowrap">Needs MIC</span><span class="table-secondary mr-1 px-2 rounded">Cancelled</span><span class="table-info px-2 rounded text-nowrap">Non-Rig</span>
|
||||||
</div>
|
</div>
|
||||||
{% if perms.RIGS.add_event %}
|
{% if perms.RIGS.add_event %}
|
||||||
<div class="col text-right">
|
<div class="col text-right">
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
<div style="container-type: inline-size;">
|
||||||
{% include 'partials/event_table.html' %}
|
{% include 'partials/event_table.html' %}
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
4
app.json
4
app.json
@@ -4,7 +4,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"postdeploy": "python manage.py migrate && python manage.py generateSampleData"
|
"postdeploy": "python manage.py migrate && python manage.py generateSampleData"
|
||||||
},
|
},
|
||||||
"stack": "heroku-20",
|
"stack": "heroku-22",
|
||||||
"env": {
|
"env": {
|
||||||
"DEBUG": {
|
"DEBUG": {
|
||||||
"required": true
|
"required": true
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
"url": "heroku/nodejs"
|
"url": "heroku/nodejs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "https://github.com/nottinghamtec/heroku-buildpack-python"
|
"url": "heroku/python"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,3 +38,17 @@ def test_asset(db, category, status):
|
|||||||
asset, created = models.Asset.objects.get_or_create(asset_id="91991", description="Spaceflower", status=status, category=category, date_acquired=datetime.date(1991, 12, 26), replacement_cost=100)
|
asset, created = models.Asset.objects.get_or_create(asset_id="91991", description="Spaceflower", status=status, category=category, date_acquired=datetime.date(1991, 12, 26), replacement_cost=100)
|
||||||
yield asset
|
yield asset
|
||||||
asset.delete()
|
asset.delete()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def test_status_2(db):
|
||||||
|
status = models.AssetStatus.objects.create(name="Lost", should_show=False)
|
||||||
|
yield status
|
||||||
|
status.delete()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def test_asset_2(db, category, test_status_2):
|
||||||
|
asset, created = models.Asset.objects.get_or_create(asset_id="10", description="Working Mic", status=test_status_2, category=category, date_acquired=datetime.date(2001, 10, 20), replacement_cost=1000)
|
||||||
|
yield asset
|
||||||
|
asset.delete()
|
||||||
|
|||||||
@@ -54,46 +54,45 @@ class TestAssetList(AutoLoginTest):
|
|||||||
self.assertEqual("10", asset_ids[2])
|
self.assertEqual("10", asset_ids[2])
|
||||||
self.assertEqual("C1", asset_ids[3])
|
self.assertEqual("C1", asset_ids[3])
|
||||||
|
|
||||||
@pytest.mark.xfail(reason="Fails on CI for unknown reason", raises=AssertionError)
|
|
||||||
def test_search(self):
|
|
||||||
self.page.set_query("10")
|
|
||||||
self.page.search()
|
|
||||||
self.assertTrue(len(self.page.assets) == 1)
|
|
||||||
self.assertEqual("Working Mic", self.page.assets[0].description)
|
|
||||||
self.assertEqual("10", self.page.assets[0].id)
|
|
||||||
|
|
||||||
self.page.set_query("light")
|
@pytest.mark.xfail(reason="Fails on CI for unknown reason", raises=AssertionError)
|
||||||
self.page.search()
|
def test_search(logged_in_browser, admin_user, live_server, test_asset, test_asset_2, category, status, cable_type):
|
||||||
self.assertTrue(len(self.page.assets) == 1)
|
page = pages.AssetList(logged_in_browser.driver, live_server.url).open()
|
||||||
self.assertEqual("A light", self.page.assets[0].description)
|
page.set_query(test_asset.asset_id)
|
||||||
|
page.search()
|
||||||
|
assert len(page.assets) == 1
|
||||||
|
assert page.assets[0].description == test_asset.description
|
||||||
|
assert page.assets[0].id == test_asset.asset_id
|
||||||
|
|
||||||
self.page.set_query("Random string")
|
page.set_query(test_asset.description)
|
||||||
self.page.search()
|
page.search()
|
||||||
self.assertTrue(len(self.page.assets) == 0)
|
assert len(page.assets) == 1
|
||||||
|
assert page.assets[0].description == test_asset.description
|
||||||
|
|
||||||
self.page.set_query("")
|
page.set_query("Random string")
|
||||||
self.page.search()
|
page.search()
|
||||||
# Only working stuff shown by default
|
assert len(page.assets) == 0
|
||||||
self.assertTrue(len(self.page.assets) == 2)
|
|
||||||
|
|
||||||
self.page.status_selector.toggle()
|
page.set_query("")
|
||||||
self.assertTrue(self.page.status_selector.is_open)
|
page.search()
|
||||||
self.page.status_selector.select_all()
|
# Only working stuff shown by default
|
||||||
self.page.status_selector.toggle()
|
assert len(page.assets) == 1
|
||||||
self.assertFalse(self.page.status_selector.is_open)
|
|
||||||
self.page.filter()
|
|
||||||
self.assertTrue(len(self.page.assets) == 4)
|
|
||||||
|
|
||||||
self.page.category_selector.toggle()
|
page.status_selector.toggle()
|
||||||
self.assertTrue(self.page.category_selector.is_open)
|
assert page.status_selector.is_open
|
||||||
self.page.category_selector.set_option("Sound", True)
|
page.status_selector.select_all()
|
||||||
self.page.category_selector.close()
|
page.status_selector.toggle()
|
||||||
self.assertFalse(self.page.category_selector.is_open)
|
assert not page.status_selector.is_open
|
||||||
self.page.filter()
|
page.filter()
|
||||||
self.assertTrue(len(self.page.assets) == 2)
|
assert len(page.assets) == 2
|
||||||
asset_ids = list(map(lambda x: x.id, self.page.assets))
|
|
||||||
self.assertEqual("1", asset_ids[0])
|
page.category_selector.toggle()
|
||||||
self.assertEqual("10", asset_ids[1])
|
assert page.category_selector.is_open
|
||||||
|
page.category_selector.set_option(category.name, True)
|
||||||
|
page.category_selector.close()
|
||||||
|
assert not page.category_selector.is_open
|
||||||
|
page.filter()
|
||||||
|
assert len(page.assets) == 2
|
||||||
|
|
||||||
|
|
||||||
def test_cable_create(logged_in_browser, admin_user, live_server, test_asset, category, status, cable_type):
|
def test_cable_create(logged_in_browser, admin_user, live_server, test_asset, category, status, cable_type):
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const con = require('gulp-concat');
|
|||||||
const gulpif = require('gulp-if');
|
const gulpif = require('gulp-if');
|
||||||
|
|
||||||
function fonts(done) {
|
function fonts(done) {
|
||||||
return gulp.src('node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.*')
|
return gulp.src('node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.*', { encoding: false })
|
||||||
.pipe(gulp.dest('pipeline/built_assets/fonts'))
|
.pipe(gulp.dest('pipeline/built_assets/fonts'))
|
||||||
.pipe(browsersync.stream());
|
.pipe(browsersync.stream());
|
||||||
}
|
}
|
||||||
@@ -70,16 +70,16 @@ function scripts() {
|
|||||||
.pipe(gulpif(function(file) { return interaction.includes(file.relative);}, con('interaction.js')))
|
.pipe(gulpif(function(file) { return interaction.includes(file.relative);}, con('interaction.js')))
|
||||||
.pipe(gulpif(function(file) { return jpop.includes(file.relative);}, con('jpop.js')))
|
.pipe(gulpif(function(file) { return jpop.includes(file.relative);}, con('jpop.js')))
|
||||||
.pipe(flatten())
|
.pipe(flatten())
|
||||||
.pipe(terser())
|
// Only minify if filename does not already denote it as minified
|
||||||
|
.pipe(gulpif(function(file) { return file.path.indexOf("min") == -1;},terser()))
|
||||||
.pipe(gulp.dest(dest))
|
.pipe(gulp.dest(dest))
|
||||||
.pipe(browsersync.stream());
|
.pipe(browsersync.stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
function browserSync(done) {
|
function browserSync(done) {
|
||||||
spawn('python', ['manage.py', 'runserver'], {stdio: 'inherit'});
|
spawn('python', ['manage.py', 'runserver'], {stdio: 'inherit'});
|
||||||
// TODO Wait for Django server to come up before browsersync, it seems inconsistent
|
|
||||||
browsersync.init({
|
browsersync.init({
|
||||||
notify: false,
|
notify: true,
|
||||||
open: false,
|
open: false,
|
||||||
port: 8001,
|
port: 8001,
|
||||||
proxy: '127.0.0.1:8000'
|
proxy: '127.0.0.1:8000'
|
||||||
|
|||||||
9252
package-lock.json
generated
9252
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,6 @@
|
|||||||
"cssnano": "^5.0.13",
|
"cssnano": "^5.0.13",
|
||||||
"easymde": "^2.16.1",
|
"easymde": "^2.16.1",
|
||||||
"fullcalendar": "^5.10.1",
|
"fullcalendar": "^5.10.1",
|
||||||
"gulp": "^4.0.2",
|
|
||||||
"gulp-concat": "^2.6.1",
|
"gulp-concat": "^2.6.1",
|
||||||
"gulp-flatten": "^0.4.0",
|
"gulp-flatten": "^0.4.0",
|
||||||
"gulp-if": "^3.0.0",
|
"gulp-if": "^3.0.0",
|
||||||
@@ -30,11 +29,12 @@
|
|||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"node-sass": "^9.0.0",
|
"node-sass": "^9.0.0",
|
||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"postcss": "^8.4.5",
|
"postcss": "^8.4.31",
|
||||||
"uglify-js": "^3.14.5"
|
"uglify-js": "^3.14.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"browser-sync": "^2.27.11"
|
"browser-sync": "^3.0.2",
|
||||||
|
"gulp": "^5.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"gulp": "gulp",
|
"gulp": "gulp",
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
function changeSelectedValue(obj,pk,text,update_url) { //Pass in JQuery object and new parameters
|
function changeSelectedValue(obj,pk,text,update_url) { //Pass in JQuery object and new parameters
|
||||||
//console.log('Changing selected value');
|
//console.log('Changing selected value');
|
||||||
obj.find('option').remove(); //Remove all the available options
|
obj.find('option').remove(); //Remove all the available options
|
||||||
obj.append( //Add the new option
|
obj[0].add(new Option(text, pk, true, true)); // Add new option
|
||||||
$("<option></option>")
|
//obj.selectpicker('val', pk); //Set the new value to be selected
|
||||||
.attr("value",pk)
|
obj.selectpicker('refresh');
|
||||||
.text(text)
|
|
||||||
.data('update_url',update_url)
|
|
||||||
);
|
|
||||||
obj.selectpicker('render'); //Re-render the UI
|
|
||||||
obj.selectpicker('refresh'); //Re-render the UI
|
|
||||||
obj.selectpicker('val', pk); //Set the new value to be selected
|
|
||||||
obj.change(); //Trigger the change function manually
|
obj.change(); //Trigger the change function manually
|
||||||
|
//console.log(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshUpdateHref(obj) {
|
function refreshUpdateHref(obj) {
|
||||||
|
|||||||
@@ -17,14 +17,12 @@ jQuery(document).ready(function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var easter_egg = new Konami();
|
var easter_egg = new Konami(function () {
|
||||||
easter_egg.code = function () {
|
|
||||||
var s = document.createElement('script');
|
var s = document.createElement('script');
|
||||||
s.type = 'text/javascript';
|
s.type = 'text/javascript';
|
||||||
document.body.appendChild(s);
|
document.body.appendChild(s);
|
||||||
s.src = '{% static "js/asteroids.min.js"%}';
|
s.src = '/static/js/asteroids.min.js';
|
||||||
ga('send', 'event', 'easter_egg', 'activated');
|
});
|
||||||
}
|
|
||||||
easter_egg.load();
|
easter_egg.load();
|
||||||
});
|
});
|
||||||
//CTRL-Enter form submission
|
//CTRL-Enter form submission
|
||||||
|
|||||||
@@ -77,17 +77,8 @@
|
|||||||
border-collapse: separate !important;
|
border-collapse: separate !important;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
}
|
}
|
||||||
#event_table tr th {
|
|
||||||
border-right: 0 !important;
|
|
||||||
}
|
|
||||||
#event_table tr td {
|
|
||||||
border-left: 0 !important;
|
|
||||||
}
|
|
||||||
#event_table tr td:not(:last-child) {
|
|
||||||
border-right: 0 !important;
|
|
||||||
}
|
|
||||||
@each $color, $value in $theme-colors {
|
@each $color, $value in $theme-colors {
|
||||||
.table-#{$color} {
|
table.table-#{$color} {
|
||||||
> td,th {
|
> td,th {
|
||||||
border: 0.3em solid theme-color-level($color, -6) !important;
|
border: 0.3em solid theme-color-level($color, -6) !important;
|
||||||
}
|
}
|
||||||
@@ -96,6 +87,11 @@
|
|||||||
background-color: #222 !important;
|
background-color: #222 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#event_row.table-#{$color} {
|
||||||
|
border: 0.3em solid theme-color-level($color, -6) !important;
|
||||||
|
background-color: #222 !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
del {
|
del {
|
||||||
color: black;
|
color: black;
|
||||||
@@ -156,4 +152,7 @@
|
|||||||
.modal {
|
.modal {
|
||||||
overflow-y: auto !important; //Bootstrap Dark Theme overrides this to none for some insane reason so we need to change it back
|
overflow-y: auto !important; //Bootstrap Dark Theme overrides this to none for some insane reason so we need to change it back
|
||||||
}
|
}
|
||||||
|
.text-muted {
|
||||||
|
color: #c9c9c9 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,3 +281,12 @@ html.embedded {
|
|||||||
.bootstrap-select, button.btn.dropdown-toggle.bs-placeholder.btn-light {
|
.bootstrap-select, button.btn.dropdown-toggle.bs-placeholder.btn-light {
|
||||||
padding-right: 1rem !important;
|
padding-right: 1rem !important;
|
||||||
}
|
}
|
||||||
|
// New implementation of class dropped in Bootstrap 3
|
||||||
|
.dl-horizontal {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
gap: 0.7rem 0;
|
||||||
|
}
|
||||||
|
.dl-horizontal > dd, .dl-horizontal .markdown > p {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -78,6 +78,11 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr><th colspan="3" class="text-center">{{object}}</th></tr>
|
<tr><th colspan="3" class="text-center">{{object}}</th></tr>
|
||||||
|
<tr>
|
||||||
|
<td><ul class="list-unstyled">{% for req in object.started_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 0 %}</li>{% endfor %}</ul></td>
|
||||||
|
<td><ul class="list-unstyled">{% for req in object.complete_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 1 %}</li>{% endfor %}</ul></td>
|
||||||
|
<td><ul class="list-unstyled">{% for req in object.passed_out_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 2 %}</li>{% endfor %}</ul></td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
for person in Profile.objects.all():
|
for person in Profile.objects.all():
|
||||||
# Inactivate users that have not logged in for a year (or have never logged in)
|
# Inactivate users that have not logged in for a year
|
||||||
if person.last_login is None or (timezone.now() - person.last_login).days > 365:
|
if person.last_login is not None and (timezone.now() - person.last_login).days > 365:
|
||||||
person.is_active = False
|
person.is_active = False
|
||||||
person.is_approved = False
|
person.is_approved = False
|
||||||
person.save()
|
person.save()
|
||||||
|
|||||||
@@ -167,9 +167,11 @@
|
|||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">Events</div>
|
<div class="card-header">Events</div>
|
||||||
|
<div style="container-type: size; height: 30vh; overflow-y: scroll;">
|
||||||
{% with object.latest_events as events %}
|
{% with object.latest_events as events %}
|
||||||
{% include 'partials/event_table.html' %}
|
{% include 'partials/event_table.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user