mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-02-22 22:38:24 +00:00
Compare commits
2 Commits
676cee164b
...
combine-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b16cf6333 | ||
|
|
7798f5c368 |
151
.github/workflows/combine-prs.yml
vendored
151
.github/workflows/combine-prs.yml
vendored
@@ -1,151 +0,0 @@
|
|||||||
name: 'Combine PRs'
|
|
||||||
|
|
||||||
# Controls when the action will run - in this case triggered manually
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
branchPrefix:
|
|
||||||
description: 'Branch prefix to find combinable PRs based on'
|
|
||||||
required: true
|
|
||||||
default: 'dependabot'
|
|
||||||
mustBeGreen:
|
|
||||||
description: 'Only combine PRs that are green (status is success)'
|
|
||||||
required: true
|
|
||||||
default: true
|
|
||||||
combineBranchName:
|
|
||||||
description: 'Name of the branch to combine PRs into'
|
|
||||||
required: true
|
|
||||||
default: 'combine-prs-branch'
|
|
||||||
ignoreLabel:
|
|
||||||
description: 'Exclude PRs with this label'
|
|
||||||
required: true
|
|
||||||
default: 'nocombine'
|
|
||||||
|
|
||||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
|
||||||
jobs:
|
|
||||||
# This workflow contains a single job called "combine-prs"
|
|
||||||
combine-prs:
|
|
||||||
# The type of runner that the job will run on
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
||||||
steps:
|
|
||||||
- uses: actions/github-script@v6
|
|
||||||
id: create-combined-pr
|
|
||||||
name: Create Combined PR
|
|
||||||
with:
|
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
|
||||||
script: |
|
|
||||||
const pulls = await github.paginate('GET /repos/:owner/:repo/pulls', {
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo
|
|
||||||
});
|
|
||||||
let branchesAndPRStrings = [];
|
|
||||||
let baseBranch = null;
|
|
||||||
let baseBranchSHA = null;
|
|
||||||
for (const pull of pulls) {
|
|
||||||
const branch = pull['head']['ref'];
|
|
||||||
console.log('Pull for branch: ' + branch);
|
|
||||||
if (branch.startsWith('${{ github.event.inputs.branchPrefix }}')) {
|
|
||||||
console.log('Branch matched prefix: ' + branch);
|
|
||||||
let statusOK = true;
|
|
||||||
if(${{ github.event.inputs.mustBeGreen }}) {
|
|
||||||
console.log('Checking green status: ' + branch);
|
|
||||||
const stateQuery = `query($owner: String!, $repo: String!, $pull_number: Int!) {
|
|
||||||
repository(owner: $owner, name: $repo) {
|
|
||||||
pullRequest(number:$pull_number) {
|
|
||||||
commits(last: 1) {
|
|
||||||
nodes {
|
|
||||||
commit {
|
|
||||||
statusCheckRollup {
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
const vars = {
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
pull_number: pull['number']
|
|
||||||
};
|
|
||||||
const result = await github.graphql(stateQuery, vars);
|
|
||||||
const [{ commit }] = result.repository.pullRequest.commits.nodes;
|
|
||||||
const state = commit.statusCheckRollup.state
|
|
||||||
console.log('Validating status: ' + state);
|
|
||||||
if(state != 'SUCCESS') {
|
|
||||||
console.log('Discarding ' + branch + ' with status ' + state);
|
|
||||||
statusOK = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log('Checking labels: ' + branch);
|
|
||||||
const labels = pull['labels'];
|
|
||||||
for(const label of labels) {
|
|
||||||
const labelName = label['name'];
|
|
||||||
console.log('Checking label: ' + labelName);
|
|
||||||
if(labelName == '${{ github.event.inputs.ignoreLabel }}') {
|
|
||||||
console.log('Discarding ' + branch + ' with label ' + labelName);
|
|
||||||
statusOK = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (statusOK) {
|
|
||||||
console.log('Adding branch to array: ' + branch);
|
|
||||||
const prString = '#' + pull['number'] + ' ' + pull['title'];
|
|
||||||
branchesAndPRStrings.push({ branch, prString });
|
|
||||||
baseBranch = pull['base']['ref'];
|
|
||||||
baseBranchSHA = pull['base']['sha'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (branchesAndPRStrings.length == 0) {
|
|
||||||
core.setFailed('No PRs/branches matched criteria');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await github.rest.git.createRef({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
ref: 'refs/heads/' + '${{ github.event.inputs.combineBranchName }}',
|
|
||||||
sha: baseBranchSHA
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
core.setFailed('Failed to create combined branch - maybe a branch by that name already exists?');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let combinedPRs = [];
|
|
||||||
let mergeFailedPRs = [];
|
|
||||||
for(const { branch, prString } of branchesAndPRStrings) {
|
|
||||||
try {
|
|
||||||
await github.rest.repos.merge({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
base: '${{ github.event.inputs.combineBranchName }}',
|
|
||||||
head: branch,
|
|
||||||
});
|
|
||||||
console.log('Merged branch ' + branch);
|
|
||||||
combinedPRs.push(prString);
|
|
||||||
} catch (error) {
|
|
||||||
console.log('Failed to merge branch ' + branch);
|
|
||||||
mergeFailedPRs.push(prString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Creating combined PR');
|
|
||||||
const combinedPRsString = combinedPRs.join('\n');
|
|
||||||
let body = '✅ This PR was created by the Combine PRs action by combining the following PRs:\n' + combinedPRsString;
|
|
||||||
if(mergeFailedPRs.length > 0) {
|
|
||||||
const mergeFailedPRsString = mergeFailedPRs.join('\n');
|
|
||||||
body += '\n\n⚠️ The following PRs were left out due to merge conflicts:\n' + mergeFailedPRsString
|
|
||||||
}
|
|
||||||
await github.rest.pulls.create({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
title: 'Combined PR',
|
|
||||||
head: '${{ github.event.inputs.combineBranchName }}',
|
|
||||||
base: baseBranch,
|
|
||||||
body: body
|
|
||||||
});
|
|
||||||
24
.github/workflows/django.yml
vendored
24
.github/workflows/django.yml
vendored
@@ -13,20 +13,26 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.9
|
python-version: 3.9.1
|
||||||
cache: 'pipenv'
|
- uses: actions/cache@v2
|
||||||
|
id: pcache
|
||||||
|
with:
|
||||||
|
path: ~/.local/share/virtualenvs
|
||||||
|
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pipenv-
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install --upgrade pip pipenv
|
python -m pip install --upgrade pip 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
|
||||||
id: static-cache
|
id: static-cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v2
|
||||||
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') }}
|
||||||
@@ -37,9 +43,9 @@ jobs:
|
|||||||
- name: Basic Checks
|
- name: Basic Checks
|
||||||
run: |
|
run: |
|
||||||
pipenv run pycodestyle . --exclude=migrations,node_modules
|
pipenv run pycodestyle . --exclude=migrations,node_modules
|
||||||
pipenv run python3 manage.py check
|
pipenv run python manage.py check
|
||||||
pipenv run python3 manage.py makemigrations --check --dry-run
|
pipenv run python manage.py makemigrations --check --dry-run
|
||||||
pipenv run python3 manage.py collectstatic --noinput
|
pipenv run python manage.py collectstatic --noinput
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: pipenv run pytest -n auto -vv --cov
|
run: pipenv run pytest -n auto -vv --cov
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
|
|||||||
13
Pipfile
13
Pipfile
@@ -11,6 +11,7 @@ asgiref = "~=3.3.1"
|
|||||||
beautifulsoup4 = "~=4.9.3"
|
beautifulsoup4 = "~=4.9.3"
|
||||||
Brotli = "~=1.0.9"
|
Brotli = "~=1.0.9"
|
||||||
cachetools = "~=4.2.1"
|
cachetools = "~=4.2.1"
|
||||||
|
certifi = "~=2020.12.5"
|
||||||
chardet = "~=4.0.0"
|
chardet = "~=4.0.0"
|
||||||
configparser = "~=5.0.1"
|
configparser = "~=5.0.1"
|
||||||
contextlib2 = "~=0.6.0.post1"
|
contextlib2 = "~=0.6.0.post1"
|
||||||
@@ -25,16 +26,18 @@ django-ical = "~=1.7.1"
|
|||||||
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"
|
||||||
|
django-toolbelt = "~=0.0.1"
|
||||||
django-widget-tweaks = "~=1.4.8"
|
django-widget-tweaks = "~=1.4.8"
|
||||||
django-htmlmin = "~=0.11.0"
|
django-htmlmin = "~=0.11.0"
|
||||||
envparse = "*"
|
envparse = "~=0.2.0"
|
||||||
gunicorn = "~=20.0.4"
|
gunicorn = "~=20.0.4"
|
||||||
icalendar = "~=4.0.7"
|
icalendar = "~=4.0.7"
|
||||||
idna = "~=2.10"
|
idna = "~=2.10"
|
||||||
|
lxml = "~=4.7.1"
|
||||||
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 = "~=9.0.0"
|
||||||
premailer = "~=3.7.0"
|
premailer = "~=3.7.0"
|
||||||
progress = "~=1.5"
|
progress = "~=1.5"
|
||||||
psutil = "~=5.8.0"
|
psutil = "~=5.8.0"
|
||||||
@@ -42,7 +45,7 @@ psycopg2 = "~=2.8.6"
|
|||||||
Pygments = "~=2.7.4"
|
Pygments = "~=2.7.4"
|
||||||
pyparsing = "~=2.4.7"
|
pyparsing = "~=2.4.7"
|
||||||
PyPDF2 = "~=1.27.5"
|
PyPDF2 = "~=1.27.5"
|
||||||
PyPOM = "~=2.2.4"
|
PyPOM = "~=2.2.0"
|
||||||
python-dateutil = "~=2.8.1"
|
python-dateutil = "~=2.8.1"
|
||||||
pytoml = "~=0.1.21"
|
pytoml = "~=0.1.21"
|
||||||
pytz = "~=2020.5"
|
pytz = "~=2020.5"
|
||||||
@@ -79,10 +82,10 @@ django-hcaptcha = "*"
|
|||||||
pikepdf = "*"
|
pikepdf = "*"
|
||||||
django-queryable-properties = "*"
|
django-queryable-properties = "*"
|
||||||
django-mass-edit = "*"
|
django-mass-edit = "*"
|
||||||
selenium = "~=3.141.0"
|
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
pycodestyle = "~=2.9.1"
|
selenium = "~=3.141.0"
|
||||||
|
pycodestyle = "*"
|
||||||
coveralls = "*"
|
coveralls = "*"
|
||||||
django-coverage-plugin = "*"
|
django-coverage-plugin = "*"
|
||||||
pytest-cov = "*"
|
pytest-cov = "*"
|
||||||
|
|||||||
1324
Pipfile.lock
generated
1324
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -42,9 +42,8 @@ if not DEBUG:
|
|||||||
|
|
||||||
INTERNAL_IPS = ['127.0.0.1']
|
INTERNAL_IPS = ['127.0.0.1']
|
||||||
|
|
||||||
DOMAIN = env('DOMAIN', default='example.com')
|
ADMINS = [('Tom Price', 'tomtom5152@gmail.com'), ('IT Manager', 'it@nottinghamtec.co.uk'),
|
||||||
|
('Arona Jones', 'arona.jones@nottinghamtec.co.uk')]
|
||||||
ADMINS = [('IT Manager', f'it@{DOMAIN}'), ('Arona Jones', f'arona.jones@{DOMAIN}')]
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
ADMINS.append(('Testing Superuser', 'superuser@example.com'))
|
ADMINS.append(('Testing Superuser', 'superuser@example.com'))
|
||||||
|
|
||||||
|
|||||||
@@ -321,60 +321,45 @@ class OEmbedView(generic.View):
|
|||||||
return JsonResponse(data)
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
||||||
def get_info_string(user):
|
|
||||||
user_str = f"by {user.name} " if user else ""
|
|
||||||
time = timezone.now().strftime('%d/%m/%Y %H:%I')
|
|
||||||
return f"[Paperwork generated {user_str}on {time}"
|
|
||||||
|
|
||||||
|
|
||||||
def render_pdf_response(template, context, append_terms):
|
|
||||||
merger = PdfFileMerger()
|
|
||||||
rml = template.render(context)
|
|
||||||
buffer = rml2pdf.parseString(rml)
|
|
||||||
merger.append(PdfFileReader(buffer))
|
|
||||||
buffer.close()
|
|
||||||
|
|
||||||
if append_terms:
|
|
||||||
terms = urllib.request.urlopen(settings.TERMS_OF_HIRE_URL)
|
|
||||||
merger.append(BytesIO(terms.read()))
|
|
||||||
|
|
||||||
merged = BytesIO()
|
|
||||||
merger.write(merged)
|
|
||||||
|
|
||||||
response = HttpResponse(content_type='application/pdf')
|
|
||||||
f = context['filename']
|
|
||||||
response['Content-Disposition'] = f'filename="{f}"'
|
|
||||||
response.write(merged.getvalue())
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
class PrintView(generic.View):
|
class PrintView(generic.View):
|
||||||
append_terms = False
|
append_terms = False
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
obj = get_object_or_404(self.model, pk=self.kwargs['pk'])
|
obj = get_object_or_404(self.model, pk=self.kwargs['pk'])
|
||||||
|
user_str = f"by {self.request.user.name} " if self.request.user is not None else ""
|
||||||
|
time = timezone.now().strftime('%d/%m/%Y %H:%I')
|
||||||
object_name = re.sub(r'[^a-zA-Z0-9 \n\.]', '', obj.name)
|
object_name = re.sub(r'[^a-zA-Z0-9 \n\.]', '', obj.name)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'object': obj,
|
'object': obj,
|
||||||
'current_user': self.request.user,
|
'current_user': self.request.user,
|
||||||
'object_name': object_name,
|
'object_name': object_name,
|
||||||
'info_string': get_info_string(self.request.user) + f"- {obj.current_version_id}]",
|
'info_string': f"[Paperwork generated {user_str}on {time} - {obj.current_version_id}]",
|
||||||
}
|
}
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get(self, request, pk):
|
def get(self, request, pk):
|
||||||
return render_pdf_response(get_template(self.template_name), self.get_context_data(), self.append_terms)
|
template = get_template(self.template_name)
|
||||||
|
|
||||||
|
merger = PdfFileMerger()
|
||||||
|
|
||||||
class PrintListView(generic.ListView):
|
context = self.get_context_data()
|
||||||
def get_context_data(self, *args, **kwargs):
|
|
||||||
context = super().get_context_data(*args, **kwargs)
|
|
||||||
context['current_user'] = self.request.user
|
|
||||||
context['info_string'] = get_info_string(self.request.user) + "]"
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get(self, request):
|
rml = template.render(context)
|
||||||
self.object_list = self.get_queryset()
|
buffer = rml2pdf.parseString(rml)
|
||||||
return render_pdf_response(get_template(self.template_name), self.get_context_data(), False)
|
merger.append(PdfFileReader(buffer))
|
||||||
|
buffer.close()
|
||||||
|
|
||||||
|
if self.append_terms:
|
||||||
|
terms = urllib.request.urlopen(settings.TERMS_OF_HIRE_URL)
|
||||||
|
merger.append(BytesIO(terms.read()))
|
||||||
|
|
||||||
|
merged = BytesIO()
|
||||||
|
merger.write(merged)
|
||||||
|
|
||||||
|
response = HttpResponse(content_type='application/pdf')
|
||||||
|
f = context['filename']
|
||||||
|
response['Content-Disposition'] = f'filename="{f}"'
|
||||||
|
response.write(merged.getvalue())
|
||||||
|
return response
|
||||||
|
|||||||
@@ -206,8 +206,3 @@ class RiskAssessmentAdmin(VersionAdmin):
|
|||||||
@admin.register(models.EventChecklist)
|
@admin.register(models.EventChecklist)
|
||||||
class EventChecklistAdmin(VersionAdmin):
|
class EventChecklistAdmin(VersionAdmin):
|
||||||
list_display = ('id', 'event', 'reviewed_at', 'reviewed_by')
|
list_display = ('id', 'event', 'reviewed_at', 'reviewed_by')
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.PowerTestRecord)
|
|
||||||
class EventChecklistAdmin(VersionAdmin):
|
|
||||||
list_display = ('id', 'event', 'reviewed_at', 'reviewed_by')
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class EventForm(forms.ModelForm):
|
|||||||
return simplejson.dumps(items)
|
return simplejson.dumps(items)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super(EventForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.fields['items_json'].initial = self._get_items_json
|
self.fields['items_json'].initial = self._get_items_json
|
||||||
self.fields['start_date'].widget.format = '%Y-%m-%d'
|
self.fields['start_date'].widget.format = '%Y-%m-%d'
|
||||||
@@ -217,7 +217,7 @@ class EventChecklistForm(forms.ModelForm):
|
|||||||
for key in vehicles:
|
for key in vehicles:
|
||||||
pk = int(key.split('_')[1])
|
pk = int(key.split('_')[1])
|
||||||
driver_key = 'driver_' + str(pk)
|
driver_key = 'driver_' + str(pk)
|
||||||
if (self.data[driver_key] == ''):
|
if(self.data[driver_key] == ''):
|
||||||
raise forms.ValidationError('Add a driver to vehicle ' + str(pk), code='vehicle_mismatch')
|
raise forms.ValidationError('Add a driver to vehicle ' + str(pk), code='vehicle_mismatch')
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@@ -283,27 +283,3 @@ class EventChecklistForm(forms.ModelForm):
|
|||||||
model = models.EventChecklist
|
model = models.EventChecklist
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
exclude = ['reviewed_at', 'reviewed_by']
|
exclude = ['reviewed_at', 'reviewed_by']
|
||||||
|
|
||||||
|
|
||||||
class PowerTestRecordForm(forms.ModelForm):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
for name, field in self.fields.items():
|
|
||||||
if field.__class__ == forms.NullBooleanField:
|
|
||||||
# Only display yes/no to user, the 'none' is only ever set in the background
|
|
||||||
field.widget = forms.CheckboxInput()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = models.PowerTestRecord
|
|
||||||
fields = '__all__'
|
|
||||||
exclude = ['reviewed_at', 'reviewed_by']
|
|
||||||
|
|
||||||
|
|
||||||
class EventCheckInForm(forms.ModelForm):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.fields['time'].initial = timezone.now()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = models.EventCheckIn
|
|
||||||
fields = '__all__'
|
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ class Command(BaseCommand):
|
|||||||
suspended_structures=bool(random.getrandbits(1)),
|
suspended_structures=bool(random.getrandbits(1)),
|
||||||
outside=bool(random.getrandbits(1)))
|
outside=bool(random.getrandbits(1)))
|
||||||
if i == 0 or random.randint(0, 1) > 0: # Event 1 and 1 in 10 have a Checklist
|
if i == 0 or random.randint(0, 1) > 0: # Event 1 and 1 in 10 have a Checklist
|
||||||
models.EventChecklist.objects.create(event=new_event,
|
models.EventChecklist.objects.create(event=new_event, power_mic=random.choice(self.profiles),
|
||||||
safe_parking=bool(random.getrandbits(1)),
|
safe_parking=bool(random.getrandbits(1)),
|
||||||
safe_packing=bool(random.getrandbits(1)),
|
safe_packing=bool(random.getrandbits(1)),
|
||||||
exits=bool(random.getrandbits(1)),
|
exits=bool(random.getrandbits(1)),
|
||||||
@@ -287,4 +287,6 @@ class Command(BaseCommand):
|
|||||||
ear_plugs=bool(random.getrandbits(1)),
|
ear_plugs=bool(random.getrandbits(1)),
|
||||||
hs_location="Locked away safely",
|
hs_location="Locked away safely",
|
||||||
extinguishers_location="Somewhere, I forgot",
|
extinguishers_location="Somewhere, I forgot",
|
||||||
|
earthing=bool(random.getrandbits(1)),
|
||||||
|
pat=bool(random.getrandbits(1)),
|
||||||
date=timezone.now(), venue=random.choice(self.venues))
|
date=timezone.now(), venue=random.choice(self.venues))
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
import premailer
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from django.template.loader import get_template
|
|
||||||
from django.contrib.staticfiles import finders
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
|
||||||
from django.core.mail import EmailMultiAlternatives
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.urls import reverse
|
|
||||||
|
|
||||||
from RIGS import models
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = 'Sends email reminders as required. Triggered daily through heroku-scheduler in production.'
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
events = models.Event.objects.current_events().select_related('riskassessment')
|
|
||||||
for event in events:
|
|
||||||
earliest_time = event.earliest_time if isinstance(event.earliest_time, datetime.datetime) else timezone.make_aware(datetime.datetime.combine(event.earliest_time, datetime.time(00, 00)))
|
|
||||||
# 48 hours = 172800 seconds
|
|
||||||
if event.is_rig and not event.cancelled and not event.dry_hire and (earliest_time - timezone.now()).total_seconds() <= 172800 and not hasattr(event, 'riskassessment'):
|
|
||||||
context = {
|
|
||||||
"event": event,
|
|
||||||
"url": "https://" + settings.DOMAIN + reverse('event_ra', kwargs={'pk': event.pk})
|
|
||||||
}
|
|
||||||
target = event.mic.email if event.mic else f"productions@{settings.DOMAIN}"
|
|
||||||
msg = EmailMultiAlternatives(
|
|
||||||
f"{event} - Risk Assessment Incomplete",
|
|
||||||
get_template("email/ra_reminder.txt").render(context),
|
|
||||||
to=[target],
|
|
||||||
reply_to=[f"h.s.manager@{settings.DOMAIN}"],
|
|
||||||
)
|
|
||||||
css = finders.find('css/email.css')
|
|
||||||
html = premailer.Premailer(get_template("email/ra_reminder.html").render(context), external_styles=css).transform()
|
|
||||||
msg.attach_alternative(html, 'text/html')
|
|
||||||
msg.send()
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
# Generated by Django 3.2.16 on 2023-05-08 15:58
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import versioning.versioning
|
|
||||||
|
|
||||||
def migrate_old_data(apps, schema_editor):
|
|
||||||
EventChecklist = apps.get_model('RIGS', 'EventChecklist')
|
|
||||||
PowerTestRecord = apps.get_model('RIGS', 'PowerTestRecord')
|
|
||||||
for ec in EventChecklist.objects.all():
|
|
||||||
# New highscore for the most pythonic BS I've ever written.
|
|
||||||
PowerTestRecord.objects.create(event=ec.event, **{i.name:getattr(ec, i.attname) for i in PowerTestRecord._meta.get_fields() if not (i.is_relation or i.auto_created)})
|
|
||||||
|
|
||||||
|
|
||||||
def revert(apps, schema_editor):
|
|
||||||
apps.get_model('RIGS', 'PowerTestRecord').objects.all().delete()
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('RIGS', '0045_alter_profile_is_approved'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='PowerTestRecord',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='power_tests', to='RIGS.event')),
|
|
||||||
('notes', models.TextField(blank=True, default='')),
|
|
||||||
('venue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='RIGS.venue')),
|
|
||||||
('reviewed_at', models.DateTimeField(null=True)),
|
|
||||||
('rcds', models.BooleanField(blank=True, help_text='RCDs installed where needed and tested?', null=True)),
|
|
||||||
('supply_test', models.BooleanField(blank=True, help_text='Electrical supplies tested?<br><small>(using socket tester)</small>', null=True)),
|
|
||||||
('earthing', models.BooleanField(blank=True, help_text='Equipment appropriately earthed?<br><small>(truss, stage, generators etc)</small>', null=True)),
|
|
||||||
('pat', models.BooleanField(blank=True, help_text='All equipment in PAT period?', null=True)),
|
|
||||||
('source_rcd', models.BooleanField(blank=True, help_text='Source RCD protected?<br><small>(if cable is more than 3m long) </small>', null=True)),
|
|
||||||
('labelling', models.BooleanField(blank=True, help_text='Appropriate and clear labelling on distribution and cabling?', null=True)),
|
|
||||||
('fd_voltage_l1', models.IntegerField(blank=True, help_text='L1 - N', null=True, verbose_name='First Distro Voltage L1-N')),
|
|
||||||
('fd_voltage_l2', models.IntegerField(blank=True, help_text='L2 - N', null=True, verbose_name='First Distro Voltage L2-N')),
|
|
||||||
('fd_voltage_l3', models.IntegerField(blank=True, help_text='L3 - N', null=True, verbose_name='First Distro Voltage L3-N')),
|
|
||||||
('fd_phase_rotation', models.BooleanField(blank=True, help_text='Phase Rotation<br><small>(if required)</small>', null=True, verbose_name='Phase Rotation')),
|
|
||||||
('fd_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
|
|
||||||
('fd_pssc', models.IntegerField(blank=True, help_text='Prospective Short Circuit Current', null=True, verbose_name='PSCC')),
|
|
||||||
('w1_description', models.CharField(blank=True, default='', help_text='Description', max_length=255)),
|
|
||||||
('w1_polarity', models.BooleanField(blank=True, help_text='Polarity Checked?', null=True)),
|
|
||||||
('w1_voltage', models.IntegerField(blank=True, help_text='Voltage', null=True)),
|
|
||||||
('w1_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
|
|
||||||
('w2_description', models.CharField(blank=True, default='', help_text='Description', max_length=255)),
|
|
||||||
('w2_polarity', models.BooleanField(blank=True, help_text='Polarity Checked?', null=True)),
|
|
||||||
('w2_voltage', models.IntegerField(blank=True, help_text='Voltage', null=True)),
|
|
||||||
('w2_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
|
|
||||||
('w3_description', models.CharField(blank=True, default='', help_text='Description', max_length=255)),
|
|
||||||
('w3_polarity', models.BooleanField(blank=True, help_text='Polarity Checked?', null=True)),
|
|
||||||
('w3_voltage', models.IntegerField(blank=True, help_text='Voltage', null=True)),
|
|
||||||
('w3_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
|
|
||||||
('all_rcds_tested', models.BooleanField(blank=True, help_text='All circuit RCDs tested?<br><small>(using test button)</small>', null=True)),
|
|
||||||
('public_sockets_tested', models.BooleanField(blank=True, help_text='Public/Performer accessible circuits tested?<br><small>(using socket tester)</small>', null=True)),
|
|
||||||
('reviewed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Reviewer')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
'ordering': ['event'],
|
|
||||||
'permissions': [('review_power', 'Can review Power Test Records')],
|
|
||||||
},
|
|
||||||
bases=(models.Model, versioning.versioning.RevisionMixin),
|
|
||||||
),
|
|
||||||
migrations.RunPython(migrate_old_data, reverse_code=revert),
|
|
||||||
]
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
# Generated by Django 3.2.16 on 2023-05-08 18:46
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('RIGS', '0046_create_powertests'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='all_rcds_tested',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='earthing',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='fd_earth_fault',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='fd_phase_rotation',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='fd_pssc',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='fd_voltage_l1',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='fd_voltage_l2',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='fd_voltage_l3',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='labelling',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='pat',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='public_sockets_tested',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='rcds',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='source_rcd',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='supply_test',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w1_description',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w1_earth_fault',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w1_polarity',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w1_voltage',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w2_description',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w2_earth_fault',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w2_polarity',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w2_voltage',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w3_description',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w3_earth_fault',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w3_polarity',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w3_voltage',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# Generated by Django 3.2.18 on 2023-05-09 19:43
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('RIGS', '0047_auto_20230508_1946'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklistvehicle',
|
|
||||||
name='checklist',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklistvehicle',
|
|
||||||
name='driver',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='power_mic',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='powertestrecord',
|
|
||||||
name='power_mic',
|
|
||||||
field=models.ForeignKey(blank=True, help_text='Who is the Power MIC?', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='checklists', to=settings.AUTH_USER_MODEL, verbose_name='Power MIC'),
|
|
||||||
),
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='EventChecklistCrew',
|
|
||||||
),
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='EventChecklistVehicle',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# Generated by Django 3.2.18 on 2023-05-09 20:12
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('RIGS', '0048_auto_20230509_2043'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='EventCheckOut',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('time', models.DateTimeField()),
|
|
||||||
('vehicle', models.CharField(max_length=100)),
|
|
||||||
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='RIGS.event')),
|
|
||||||
('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='checkouts', to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='EventCheckIn',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('time', models.DateTimeField()),
|
|
||||||
('vehicle', models.CharField(max_length=100)),
|
|
||||||
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='crew', to='RIGS.event')),
|
|
||||||
('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='checkins', to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
112
RIGS/models.py
112
RIGS/models.py
@@ -689,21 +689,8 @@ def validate_url(value):
|
|||||||
raise ValidationError('URL must point to a location on the TEC Sharepoint')
|
raise ValidationError('URL must point to a location on the TEC Sharepoint')
|
||||||
|
|
||||||
|
|
||||||
class ReviewableModel(models.Model):
|
|
||||||
reviewed_at = models.DateTimeField(null=True)
|
|
||||||
reviewed_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
|
|
||||||
verbose_name="Reviewer", on_delete=models.CASCADE)
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def fieldz(self):
|
|
||||||
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
@reversion.register
|
@reversion.register
|
||||||
class RiskAssessment(ReviewableModel, RevisionMixin):
|
class RiskAssessment(models.Model, RevisionMixin):
|
||||||
SMALL = (0, 'Small')
|
SMALL = (0, 'Small')
|
||||||
MEDIUM = (1, 'Medium')
|
MEDIUM = (1, 'Medium')
|
||||||
LARGE = (2, 'Large')
|
LARGE = (2, 'Large')
|
||||||
@@ -751,6 +738,10 @@ class RiskAssessment(ReviewableModel, RevisionMixin):
|
|||||||
|
|
||||||
# Blimey that was a lot of options
|
# Blimey that was a lot of options
|
||||||
|
|
||||||
|
reviewed_at = models.DateTimeField(null=True)
|
||||||
|
reviewed_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
|
||||||
|
verbose_name="Reviewer", on_delete=models.CASCADE)
|
||||||
|
|
||||||
supervisor_consulted = models.BooleanField(null=True)
|
supervisor_consulted = models.BooleanField(null=True)
|
||||||
|
|
||||||
expected_values = {
|
expected_values = {
|
||||||
@@ -787,6 +778,10 @@ class RiskAssessment(ReviewableModel, RevisionMixin):
|
|||||||
('review_riskassessment', 'Can review Risk Assessments')
|
('review_riskassessment', 'Can review Risk Assessments')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def fieldz(self):
|
||||||
|
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def event_size(self):
|
def event_size(self):
|
||||||
# Confirm event size. Check all except generators, since generators entails outside
|
# Confirm event size. Check all except generators, since generators entails outside
|
||||||
@@ -816,10 +811,12 @@ class RiskAssessment(ReviewableModel, RevisionMixin):
|
|||||||
|
|
||||||
|
|
||||||
@reversion.register(follow=['vehicles', 'crew'])
|
@reversion.register(follow=['vehicles', 'crew'])
|
||||||
class EventChecklist(ReviewableModel, RevisionMixin):
|
class EventChecklist(models.Model, RevisionMixin):
|
||||||
event = models.ForeignKey('Event', related_name='checklists', on_delete=models.CASCADE)
|
event = models.ForeignKey('Event', related_name='checklists', on_delete=models.CASCADE)
|
||||||
|
|
||||||
# General
|
# General
|
||||||
|
power_mic = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='checklists',
|
||||||
|
verbose_name="Power MIC", on_delete=models.CASCADE, help_text="Who is the Power MIC?")
|
||||||
venue = models.ForeignKey('Venue', on_delete=models.CASCADE)
|
venue = models.ForeignKey('Venue', on_delete=models.CASCADE)
|
||||||
date = models.DateField()
|
date = models.DateField()
|
||||||
|
|
||||||
@@ -833,32 +830,6 @@ class EventChecklist(ReviewableModel, RevisionMixin):
|
|||||||
hs_location = models.CharField(blank=True, default='', max_length=255, help_text="Location of Safety Bag/Box")
|
hs_location = models.CharField(blank=True, default='', max_length=255, help_text="Location of Safety Bag/Box")
|
||||||
extinguishers_location = models.CharField(blank=True, default='', max_length=255, help_text="Location of fire extinguishers")
|
extinguishers_location = models.CharField(blank=True, default='', max_length=255, help_text="Location of fire extinguishers")
|
||||||
|
|
||||||
inverted_fields = []
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ['event']
|
|
||||||
permissions = [
|
|
||||||
('review_eventchecklist', 'Can review Event Checklists')
|
|
||||||
]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def activity_feed_string(self):
|
|
||||||
return str(self.event)
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('ec_detail', kwargs={'pk': self.pk})
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.pk} - {self.event}"
|
|
||||||
|
|
||||||
|
|
||||||
@reversion.register
|
|
||||||
class PowerTestRecord(ReviewableModel, RevisionMixin):
|
|
||||||
event = models.ForeignKey('Event', related_name='power_tests', on_delete=models.CASCADE)
|
|
||||||
power_mic = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='checklists',
|
|
||||||
verbose_name="Power MIC", on_delete=models.CASCADE, help_text="Who is the Power MIC?")
|
|
||||||
venue = models.ForeignKey('Venue', on_delete=models.CASCADE)
|
|
||||||
notes = models.TextField(blank=True, default='')
|
|
||||||
# Small Electrical Checks
|
# Small Electrical Checks
|
||||||
rcds = models.BooleanField(blank=True, null=True, help_text="RCDs installed where needed and tested?")
|
rcds = models.BooleanField(blank=True, null=True, help_text="RCDs installed where needed and tested?")
|
||||||
supply_test = models.BooleanField(blank=True, null=True, help_text="Electrical supplies tested?<br><small>(using socket tester)</small>")
|
supply_test = models.BooleanField(blank=True, null=True, help_text="Electrical supplies tested?<br><small>(using socket tester)</small>")
|
||||||
@@ -894,25 +865,58 @@ class PowerTestRecord(ReviewableModel, RevisionMixin):
|
|||||||
all_rcds_tested = models.BooleanField(blank=True, null=True, help_text="All circuit RCDs tested?<br><small>(using test button)</small>")
|
all_rcds_tested = models.BooleanField(blank=True, null=True, help_text="All circuit RCDs tested?<br><small>(using test button)</small>")
|
||||||
public_sockets_tested = models.BooleanField(blank=True, null=True, help_text="Public/Performer accessible circuits tested?<br><small>(using socket tester)</small>")
|
public_sockets_tested = models.BooleanField(blank=True, null=True, help_text="Public/Performer accessible circuits tested?<br><small>(using socket tester)</small>")
|
||||||
|
|
||||||
def __str__(self):
|
reviewed_at = models.DateTimeField(null=True)
|
||||||
return f"{self.pk} - {self.event}"
|
reviewed_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
|
||||||
|
verbose_name="Reviewer", on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
inverted_fields = []
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['event']
|
ordering = ['event']
|
||||||
permissions = [
|
permissions = [
|
||||||
('review_power', 'Can review Power Test Records')
|
('review_eventchecklist', 'Can review Event Checklists')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def fieldz(self):
|
||||||
|
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
|
||||||
|
|
||||||
class EventCheckIn(models.Model):
|
@property
|
||||||
event = models.ForeignKey('Event', related_name='crew', on_delete=models.CASCADE)
|
def activity_feed_string(self):
|
||||||
person = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='checkins', on_delete=models.CASCADE)
|
return str(self.event)
|
||||||
time = models.DateTimeField()
|
|
||||||
vehicle = models.CharField(max_length=100)
|
def get_absolute_url(self):
|
||||||
|
return reverse('ec_detail', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.pk} - {self.event}"
|
||||||
|
|
||||||
|
|
||||||
class EventCheckOut(models.Model):
|
@reversion.register
|
||||||
event = models.ForeignKey('Event', on_delete=models.CASCADE)
|
class EventChecklistVehicle(models.Model, RevisionMixin):
|
||||||
person = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='checkouts', on_delete=models.CASCADE)
|
checklist = models.ForeignKey('EventChecklist', related_name='vehicles', blank=True, on_delete=models.CASCADE)
|
||||||
time = models.DateTimeField() # TODO Validate may not check in in future
|
vehicle = models.CharField(max_length=255)
|
||||||
vehicle = models.CharField(max_length=100)
|
driver = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='vehicles', on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
reversion_hide = True
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.vehicle} driven by {self.driver}"
|
||||||
|
|
||||||
|
|
||||||
|
@reversion.register
|
||||||
|
class EventChecklistCrew(models.Model, RevisionMixin):
|
||||||
|
checklist = models.ForeignKey('EventChecklist', related_name='crew', blank=True, on_delete=models.CASCADE)
|
||||||
|
crewmember = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='crewed', on_delete=models.CASCADE)
|
||||||
|
role = models.CharField(max_length=255)
|
||||||
|
start = models.DateTimeField()
|
||||||
|
end = models.DateTimeField()
|
||||||
|
|
||||||
|
reversion_hide = True
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
if self.start > self.end:
|
||||||
|
raise ValidationError('Unless you\'ve invented time travel, crew can\'t finish before they have started.')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.crewmember} ({self.role})"
|
||||||
|
|||||||
@@ -58,13 +58,13 @@ def send_eventauthorisation_success_email(instance):
|
|||||||
|
|
||||||
client_email = EmailMultiAlternatives(
|
client_email = EmailMultiAlternatives(
|
||||||
subject,
|
subject,
|
||||||
get_template("email/eventauthorisation_client_success.txt").render(context),
|
get_template("eventauthorisation_client_success.txt").render(context),
|
||||||
to=[instance.email],
|
to=[instance.email],
|
||||||
reply_to=[settings.AUTHORISATION_NOTIFICATION_ADDRESS],
|
reply_to=[settings.AUTHORISATION_NOTIFICATION_ADDRESS],
|
||||||
)
|
)
|
||||||
|
|
||||||
css = finders.find('css/email.css')
|
css = finders.find('css/email.css')
|
||||||
html = Premailer(get_template("email/eventauthorisation_client_success.html").render(context),
|
html = Premailer(get_template("eventauthorisation_client_success.html").render(context),
|
||||||
external_styles=css).transform()
|
external_styles=css).transform()
|
||||||
client_email.attach_alternative(html, 'text/html')
|
client_email.attach_alternative(html, 'text/html')
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ def send_eventauthorisation_success_email(instance):
|
|||||||
|
|
||||||
mic_email = EmailMessage(
|
mic_email = EmailMessage(
|
||||||
subject,
|
subject,
|
||||||
get_template("email/eventauthorisation_mic_success.txt").render(context),
|
get_template("eventauthorisation_mic_success.txt").render(context),
|
||||||
to=[mic_email_address]
|
to=[mic_email_address]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -117,12 +117,12 @@ def send_admin_awaiting_approval_email(user, request, **kwargs):
|
|||||||
|
|
||||||
email = EmailMultiAlternatives(
|
email = EmailMultiAlternatives(
|
||||||
f"{context['number_of_users']} new users awaiting approval on RIGS",
|
f"{context['number_of_users']} new users awaiting approval on RIGS",
|
||||||
get_template("email/admin_awaiting_approval.txt").render(context),
|
get_template("admin_awaiting_approval.txt").render(context),
|
||||||
to=[admin.email],
|
to=[admin.email],
|
||||||
reply_to=[user.email],
|
reply_to=[user.email],
|
||||||
)
|
)
|
||||||
css = finders.find('css/email.css')
|
css = finders.find('css/email.css')
|
||||||
html = Premailer(get_template("email/admin_awaiting_approval.html").render(context),
|
html = Premailer(get_template("admin_awaiting_approval.html").render(context),
|
||||||
external_styles=css).transform()
|
external_styles=css).transform()
|
||||||
email.attach_alternative(html, 'text/html')
|
email.attach_alternative(html, 'text/html')
|
||||||
email.send()
|
email.send()
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 66 KiB |
@@ -11,7 +11,6 @@
|
|||||||
<initialize>
|
<initialize>
|
||||||
<color id="LightGray" RGB="#D3D3D3"/>
|
<color id="LightGray" RGB="#D3D3D3"/>
|
||||||
<color id="DarkGray" RGB="#707070"/>
|
<color id="DarkGray" RGB="#707070"/>
|
||||||
<color id="Brand" RGB="#3853a4"/>
|
|
||||||
</initialize>
|
</initialize>
|
||||||
|
|
||||||
<paraStyle name="style.para" fontName="OpenSans" />
|
<paraStyle name="style.para" fontName="OpenSans" />
|
||||||
@@ -28,8 +27,6 @@
|
|||||||
<paraStyle name="style.times" fontName="OpenSans" fontSize="10" />
|
<paraStyle name="style.times" fontName="OpenSans" fontSize="10" />
|
||||||
<paraStyle name="style.head_titles" fontName="OpenSans-Bold" fontSize="10" />
|
<paraStyle name="style.head_titles" fontName="OpenSans-Bold" fontSize="10" />
|
||||||
<paraStyle name="style.head_numbers" fontName="OpenSans" fontSize="10" />
|
<paraStyle name="style.head_numbers" fontName="OpenSans" fontSize="10" />
|
||||||
<paraStyle name="style.emheader" fontName="OpenSans" textColor="White" fontSize="12" backColor="Brand" leading="20" borderPadding="4"/>
|
|
||||||
<paraStyle name="style.breakbefore" parent="emheader" pageBreakBefore="1"/>
|
|
||||||
|
|
||||||
<blockTableStyle id="eventSpecifics">
|
<blockTableStyle id="eventSpecifics">
|
||||||
<blockValign value="top"/>
|
<blockValign value="top"/>
|
||||||
|
|||||||
@@ -34,7 +34,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{% if perms.RIGS.view_riskassessment %}
|
{% if perms.RIGS.view_riskassessment %}
|
||||||
<li class="nav-item"><a class="nav-link" href="{% url 'hs_list' %}">H&S</a></li>
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownHS" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
H&S
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-menu" aria-labelledby="navbarDropdownHS">
|
||||||
|
<a class="dropdown-item" href="{% url 'hs_list' %}"><span class="fas fa-eye"></span> Overview</a>
|
||||||
|
<a class="dropdown-item" href="{% url 'ra_list' %}"><span class="fas fa-file-medical"></span> Risk Assessments</a>
|
||||||
|
<a class="dropdown-item" href="{% url 'ec_list' %}"><span class="fas fa-tasks"></span> Event Checklists</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.RIGS.view_invoice %}
|
{% if perms.RIGS.view_invoice %}
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
Hi {{object.event.mic.get_full_name|default_if_none:"somebody"}},
|
|
||||||
|
|
||||||
Just to let you know your event N{{object.eventdisplay_id}} has been successfully authorised for £{{object.amount}} by {{object.name}} as of {{object.event.last_edited_at}}.
|
|
||||||
|
|
||||||
The TEC Rig Information Gathering System
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{% extends 'base_client_email.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<p>Hi {{event.mic.get_full_name|default_if_none:"Productions Manager"}},</p>
|
|
||||||
|
|
||||||
{% if event.mic %}
|
|
||||||
<p>Just to let you know your event {{event.display_id}} <em>requires<em> a pre-event risk assessment completing prior to the event. Please do so as soon as possible.</p>
|
|
||||||
{% else %}
|
|
||||||
<p>This is a reminder that event {{event.display_id}} requires a MIC assigning and a risk assessment completing.</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<p>Fill it out here:</p>
|
|
||||||
<a href="{{url}}" class="btn btn-info"><span class="fas fa-paperclip"></span> Create Risk Assessment</a>
|
|
||||||
|
|
||||||
<p>TEC PA & Lighting</p>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
Hi {{event.mic.get_full_name|default_if_none:"Productions Manager"}},
|
|
||||||
|
|
||||||
{% if event.mic %}
|
|
||||||
Just to let you know your event {{event.display_id}} requires a risk assessment completing prior to the event. Please do so as soon as possible.
|
|
||||||
{% else %}
|
|
||||||
This is a reminder that event {{event.display_id}} requires a MIC assigning and a risk assessment completing.
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
The TEC Rig Information Gathering System
|
|
||||||
5
RIGS/templates/eventauthorisation_mic_success.txt
Normal file
5
RIGS/templates/eventauthorisation_mic_success.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Hi {{object.event.mic.get_full_name|default_if_none:"somebody"}},
|
||||||
|
|
||||||
|
Just to let you know your event N{{object.event.pk|stringformat:"05d"}} has been successfully authorised for £{{object.amount}} by {{object.name}} as of {{object.event.last_edited_at}}.
|
||||||
|
|
||||||
|
The TEC Rig Information Gathering System
|
||||||
@@ -30,6 +30,14 @@
|
|||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt class="col-6">{{ object|help_text:'power_mic' }}</dt>
|
||||||
|
<dd class="col-6">
|
||||||
|
{% if object.power_mic %}
|
||||||
|
<a href="{% url 'profile_detail' object.power_mic.pk %}">{{ object.power_mic.name }}</a>
|
||||||
|
{% else %}
|
||||||
|
None
|
||||||
|
{% endif %}
|
||||||
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<p>List vehicles and their drivers</p>
|
<p>List vehicles and their drivers</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@@ -19,6 +19,41 @@
|
|||||||
{{ 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 src="{% static 'js/tooltip.js' %}"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('button[data-action=add]').on('click', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
let target = $($(this).attr('data-target'));
|
||||||
|
let newID = Number(target.attr('data-pk'));
|
||||||
|
let newRow = $($(this).attr('data-clone'))
|
||||||
|
.clone().attr('style', "")
|
||||||
|
.attr('id', function(i, val){
|
||||||
|
return val.split("_")[0] + '_' + newID;
|
||||||
|
})
|
||||||
|
.appendTo(target);
|
||||||
|
newRow.find('select,input').attr('name', function(i, val){
|
||||||
|
return val.split("_")[0] + '_' + newID;
|
||||||
|
})//Disabled is to prevent the hidden row being sent to the form
|
||||||
|
.removeAttr('disabled');
|
||||||
|
newRow.find('button[data-action=delete]').attr('data-id', newID);
|
||||||
|
newRow.find('select').addClass('selectpicker');
|
||||||
|
newRow.find('.selectpicker').selectpicker('refresh');
|
||||||
|
$(".selectpicker").each(function(){initPicker($(this))});
|
||||||
|
initDatetime();
|
||||||
|
$(target).attr('data-pk', newID - 1);
|
||||||
|
});
|
||||||
|
$(document).on('click', 'button[data-action=delete]', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
$(this).closest('tr').remove();
|
||||||
|
});
|
||||||
|
//Somewhat rudimentary way of ensuring people fill in completely (if it hits the database validation the whole table row disappears when the page reloads...)
|
||||||
|
//the not is to avoid adding it to some of bootstrap-selects extra crap
|
||||||
|
$('#vehiclest,#crewmemberst').on('change', 'select,input', function () {
|
||||||
|
$(this).closest('tr').find("select,input").not(':input[type=search]').attr('required', 'true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -67,6 +102,52 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group form-row" id="{{ form.power_mic.id_for_label }}-group">
|
||||||
|
<label for="{{ form.power_mic.id_for_label }}"
|
||||||
|
class="col-4 col-form-label">{{ form.power_mic.help_text }}</label>
|
||||||
|
<select id="{{ form.power_mic.id_for_label }}" name="{{ form.power_mic.name }}" class="form-control selectpicker col-8" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" required="true">
|
||||||
|
{% if power_mic %}
|
||||||
|
<option value="{{power_mic.pk}}" selected="selected">{{ power_mic.name }}</option>
|
||||||
|
{% elif event.riskassessment.power_mic %}
|
||||||
|
<option value="{{event.riskassessment.power_mic.pk}}" selected="selected">{{ event.riskassessment.power_mic.name }}</option>
|
||||||
|
{% endif %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<p class="pt-3 font-weight-bold">List vehicles and their drivers</p>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Vehicle</th>
|
||||||
|
<th scope="col">Driver</th>
|
||||||
|
<th scope="col"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="vehiclest" data-pk="-1">
|
||||||
|
<tr id="vehicles_new" style="display: none;">
|
||||||
|
<td><input type="text" class="form-control" name="vehicle_new" disabled="true"/></td>
|
||||||
|
<td><select data-container="body" class="form-control" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" name="driver_new" disabled="true"></select></td>
|
||||||
|
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-action='delete' data-target='#vehicle'><span class="fas fa-times"></span></button></td>
|
||||||
|
</tr>
|
||||||
|
{% for i in object.vehicles.all %}
|
||||||
|
<tr id="vehicles_{{i.pk}}">
|
||||||
|
<td><input name="vehicle_{{i.pk}}" type="text" class="form-control" value="{{ i.vehicle }}"/></td>
|
||||||
|
<td>
|
||||||
|
<select data-container="body" name="driver_{{i.pk}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
|
||||||
|
{% if i.driver != '' %}
|
||||||
|
<option value="{{i.driver.pk}}" selected="selected">{{ i.driver.name }}</option>
|
||||||
|
{% endif %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-id='{{i.pk}}' data-action='delete' data-target='#vehicle'><span class="fas fa-times"></span></button></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<button type="button" class="btn btn-secondary" id="vehicle-add" data-action='add' data-target='#vehiclest' data-clone='#vehicles_new'><span class="fas fa-plus"></span> Add Vehicle</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,6 +175,176 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row my-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">Crew Record</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Person</th>
|
||||||
|
<th scope="col">Start Time</th>
|
||||||
|
<th scope="col">Role</th>
|
||||||
|
<th scope="col">End Time</th>
|
||||||
|
<th scope="col"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="crewmemberst" data-pk="-1">
|
||||||
|
<tr id="crew_new" style="display: none;">
|
||||||
|
<td>
|
||||||
|
<select name="crewmember_new" class="form-control" data-container="body" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" disabled="true"></select>
|
||||||
|
</td>
|
||||||
|
<td style="min-width: 15ch"><input name="start_new" type="datetime-local" class="form-control" value="{{ i.start }}" disabled=""/></td>
|
||||||
|
<td style="min-width: 15ch"><input name="role_new" type="text" class="form-control" value="{{ i.role }}" disabled="true"/></td>
|
||||||
|
<td style="min-width: 15ch"><input name="end_new" type="datetime-local" class="form-control" value="{{ i.end }}" disabled="true" /></td>
|
||||||
|
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-id='{{crew.pk}}' data-action='delete' data-target='#crewmember'><span class="fas fa-times"></span></button></td>
|
||||||
|
</tr>
|
||||||
|
{% for crew in object.crew.all %}
|
||||||
|
<tr id="crew_{{crew.pk}}">
|
||||||
|
<td>
|
||||||
|
<select data-container="body" name="crewmember_{{crew.pk}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
|
||||||
|
{% if crew.crewmember != '' %}
|
||||||
|
<option value="{{crew.crewmember.pk}}" selected="selected">{{ crew.crewmember.name }}</option>
|
||||||
|
{% endif %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td><input name="start_{{crew.pk}}" type="datetime-local" class="form-control" value="{{ crew.start|date:'Y-m-d' }}T{{ crew.start|date:'H:i:s' }}"/></td>
|
||||||
|
<td><input name="role_{{crew.pk}}" type="text" class="form-control" value="{{ crew.role }}"/></td>
|
||||||
|
<td><input name="end_{{crew.pk}}" type="datetime-local" class="form-control" value="{{ crew.end|date:'Y-m-d' }}T{{ crew.end|date:'H:i:s' }}"/></td>
|
||||||
|
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-id='{{crew.pk}}' data-action='delete' data-target='#crewmember'><span class="fas fa-times"></span></button></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<div class="text-right">
|
||||||
|
<button type="button" class="btn btn-secondary" data-action='add' data-target='#crewmemberst' data-clone='#crew_new'><span class="fas fa-plus"></span> Add Crewmember</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if event.riskassessment.event_size == 0 %}
|
||||||
|
<div class="row my-3" id="size-0">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card border-success">
|
||||||
|
<div class="card-header">Electrical Checks <small>for ‘Small’ TEC Events <6kVA (approx. 26A)</small></div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% include 'partials/checklist_checkbox.html' with formitem=form.rcds %}
|
||||||
|
{% include 'partials/checklist_checkbox.html' with formitem=form.supply_test %}
|
||||||
|
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
|
||||||
|
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="row my-3" id="size-1">
|
||||||
|
<div class="col-12">
|
||||||
|
{% if event.riskassessment.event_size == 1 %}
|
||||||
|
<div class="card border-warning">
|
||||||
|
<div class="card-header">Electrical Checks <small>for ‘Medium’ TEC Events </small></div>
|
||||||
|
<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.labelling %}
|
||||||
|
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
|
||||||
|
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
|
||||||
|
<hr>
|
||||||
|
<p>Tests at first distro</p>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col" class="text-center">Test</th>
|
||||||
|
<th scope="col" colspan="3" class="text-center">Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th scope="row" rowspan="2">Voltage<br><small>(cube meter)</small></th>
|
||||||
|
<th class="text-center">{{ form.fd_voltage_l1.help_text }}</th>
|
||||||
|
<th class="text-center">{{ form.fd_voltage_l2.help_text }}</th>
|
||||||
|
<th class="text-center">{{ form.fd_voltage_l3.help_text }}</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% render_field form.fd_voltage_l1 class+="form-control" style="min-width: 5rem;" %}</td>
|
||||||
|
<td>{% render_field form.fd_voltage_l2 class+="form-control" style="min-width: 5rem;" %}</td>
|
||||||
|
<td>{% render_field form.fd_voltage_l3 class+="form-control" style="min-width: 5rem;" %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{{form.fd_phase_rotation.help_text|safe}}</th>
|
||||||
|
<td colspan="3">{% include 'partials/checklist_checkbox.html' with formitem=form.fd_phase_rotation %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{{form.fd_earth_fault.help_text|safe}}</th>
|
||||||
|
<td colspan="3">{% render_field form.fd_earth_fault class+="form-control" %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{{form.fd_pssc.help_text|safe}}</th>
|
||||||
|
<td colspan="3">{% render_field form.fd_pssc class+="form-control" %}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<p>Tests at 'Worst Case' points (at least 1 point required)</p>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col" class="text-center">Test</th>
|
||||||
|
<th scope="col" class="text-center">Point 1</th>
|
||||||
|
<th scope="col" class="text-center">Point 2</th>
|
||||||
|
<th scope="col" class="text-center">Point 3</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{{form.w1_description.help_text|safe}}</th>
|
||||||
|
<td>{% render_field form.w1_description class+="form-control" style="min-width: 5rem;" %}</td>
|
||||||
|
<td>{% render_field form.w2_description class+="form-control" style="min-width: 5rem;" %}</td>
|
||||||
|
<td>{% render_field form.w3_description class+="form-control" style="min-width: 5rem;" %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{{form.w1_polarity.help_text|safe}}</th>
|
||||||
|
<td>{% render_field form.w1_polarity %}</td>
|
||||||
|
<td>{% render_field form.w2_polarity %}</td>
|
||||||
|
<td>{% render_field form.w3_polarity %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{{form.w1_voltage.help_text|safe}}</th>
|
||||||
|
<td>{% render_field form.w1_voltage class+="form-control" %}</td>
|
||||||
|
<td>{% render_field form.w2_voltage class+="form-control" %}</td>
|
||||||
|
<td>{% render_field form.w3_voltage class+="form-control" %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{{form.w1_earth_fault.help_text|safe}}</th>
|
||||||
|
<td>{% render_field form.w1_earth_fault class+="form-control" %}</td>
|
||||||
|
<td>{% render_field form.w2_earth_fault class+="form-control" %}</td>
|
||||||
|
<td>{% render_field form.w3_earth_fault class+="form-control" %}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
{% include 'partials/checklist_checkbox.html' with formitem=form.all_rcds_tested %}
|
||||||
|
{% include 'partials/checklist_checkbox.html' with formitem=form.public_sockets_tested %}
|
||||||
|
{% include 'partials/ec_power_info.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% 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">
|
||||||
{% button 'submit' %}
|
{% button 'submit' %}
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %}
|
|
||||||
{% load widget_tweaks %}
|
|
||||||
{% load static %}
|
|
||||||
{% load help_text from filters %}
|
|
||||||
{% load profile_by_index from filters %}
|
|
||||||
{% load button from filters %}
|
|
||||||
|
|
||||||
{% block css %}
|
|
||||||
{{ block.super }}
|
|
||||||
<link rel="stylesheet" href="{% static 'css/selects.css' %}"/>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block preload_js %}
|
|
||||||
{{ block.super }}
|
|
||||||
<script src="{% static 'js/selects.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block js %}
|
|
||||||
{{ block.super }}
|
|
||||||
<script src="{% static 'js/autocompleter.js' %}"></script>
|
|
||||||
<script src="{% static 'js/tooltip.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="col-12">
|
|
||||||
{% include 'form_errors.html' %}
|
|
||||||
<form role="form" method="POST" action="{% url 'event_checkin' pk=event.pk %}">
|
|
||||||
<input type="hidden" name="{{ form.event.name }}" id="{{ form.event.id_for_label }}"
|
|
||||||
value="{{event.pk}}"/>
|
|
||||||
<input type="hidden" name="{{ form.person.name }}" id="{{ form.person.id_for_label }}"
|
|
||||||
value="{{request.user.pk}}"/>
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.time.id_for_label }}"
|
|
||||||
class="col-sm-4 col-form-label">{{ form.time.label }}</label>
|
|
||||||
<div class="col-sm-8">
|
|
||||||
{% render_field form.time class+="form-control" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.vehicle.id_for_label }}" class="col-sm-4 col-form-label">Did you drive? If so, what did you drive?</label>
|
|
||||||
<br><button type="button" class="btn btn-primary" onclick="document.getElementById('id_vehicle').value='Virgil'"><span class="fas fa-truck-moving"></span> Virgil</button>
|
|
||||||
<button type="button" class="btn btn-secondary" onclick="document.getElementById('id_vehicle').value='Virgil + Erms'"><span class="fas fa-trailer"></span><span class="fas fa-truck-moving"></span> Virgil + Erms</button>
|
|
||||||
<br>Other (enter text)
|
|
||||||
<div class="col-sm-8">
|
|
||||||
{% render_field form.vehicle class+="form-control" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mt-3">
|
|
||||||
<div class="col-sm-12 text-right">
|
|
||||||
{% button 'submit' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -12,7 +12,6 @@
|
|||||||
<th scope="col">Dates</th>
|
<th scope="col">Dates</th>
|
||||||
<th scope="col">RA</th>
|
<th scope="col">RA</th>
|
||||||
<th scope="col">Checklists</th>
|
<th scope="col">Checklists</th>
|
||||||
<th scope="col">Power Records</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -36,14 +35,6 @@
|
|||||||
<a href="{% url 'event_ec' event.pk %}" class="btn btn-info"><span class="fas fa-paperclip"></span> <span
|
<a href="{% url 'event_ec' event.pk %}" class="btn btn-info"><span class="fas fa-paperclip"></span> <span
|
||||||
class="d-none d-sm-inline">Create</span></a>
|
class="d-none d-sm-inline">Create</span></a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
{% for record in event.power_tests.all %}
|
|
||||||
{% include 'partials/hs_status.html' with event=event object=record view='pt_detail' edit='pt_edit' create='event_pt' review='pt_review' perm=perms.RIGS.review_power %}
|
|
||||||
<br/>
|
|
||||||
{% endfor %}
|
|
||||||
<a href="{% url 'event_pt' event.pk %}" class="btn btn-info"><span class="fas fa-paperclip"></span> <span
|
|
||||||
class="hidden-xs">Create</span></a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr class="bg-warning text-dark">
|
<tr class="bg-warning text-dark">
|
||||||
|
|||||||
59
RIGS/templates/hs/hs_object_list.html
Normal file
59
RIGS/templates/hs/hs_object_list.html
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
{% extends 'base_rigs.html' %}
|
||||||
|
{% load paginator from filters %}
|
||||||
|
{% load help_text from filters %}
|
||||||
|
{% load verbose_name from filters %}
|
||||||
|
{% load get_field from filters %}
|
||||||
|
|
||||||
|
{% block title %}{{ title }} List{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<h2>{{title}} List</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table mb-0 table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Event</th>
|
||||||
|
{# mmm hax #}
|
||||||
|
{% if object_list.0 != None %}
|
||||||
|
{% for field in object_list.0.fieldz %}
|
||||||
|
<th scope="col">{{ object_list.0|verbose_name:field|title }}</th>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
<th scope="col"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for object in object_list %}
|
||||||
|
<tr class="{% if object.reviewed_by %}table-success{%endif%}">
|
||||||
|
{# General #}
|
||||||
|
<th scope="row"><a href="{% url 'event_detail' object.event.pk %}">{{ object.event }}</a><br><small>{{ object.event.get_status_display }}</small></th>
|
||||||
|
{% for field in object_list.0.fieldz %}
|
||||||
|
<td>{{ object|get_field:field }}</td>
|
||||||
|
{% endfor %}
|
||||||
|
{# Buttons #}
|
||||||
|
<td>
|
||||||
|
{% include 'partials/hs_status.html' %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr class="bg-warning">
|
||||||
|
<td colspan="6">Nothing found</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if is_paginated %}
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
{% paginator %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
|
|
||||||
{% load help_text from filters %}
|
|
||||||
{% load profile_by_index from filters %}
|
|
||||||
{% load yesnoi from filters %}
|
|
||||||
{% load button from filters %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 text-right my-3">
|
|
||||||
{% button 'edit' url='pt_edit' pk=object.pk %}
|
|
||||||
{% 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' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card mb-3">
|
|
||||||
<div class="card-header">{% include 'partials/event_size.html' with object=object.event.riskassessment %}</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<dl class="row">
|
|
||||||
<dt class="col-6">{{ object|help_text:'power_mic' }}</dt>
|
|
||||||
<dd class="col-6">
|
|
||||||
{% if object.power_mic %}
|
|
||||||
<a href="{% url 'profile_detail' object.power_mic.pk %}">{{ object.power_mic.name }}</a>
|
|
||||||
{% else %}
|
|
||||||
None
|
|
||||||
{% endif %}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-6">Venue</dt>
|
|
||||||
<dd class="col-6">
|
|
||||||
{% if object.venue %}
|
|
||||||
<a href="{% url 'venue_detail' object.venue.pk %}" class="modal-href">
|
|
||||||
{{ object.venue }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-6">Notes</dt>
|
|
||||||
<dd class="col-6">
|
|
||||||
{{ object.notes }}
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
{% if object.event.riskassessment.event_size == 0 %}
|
|
||||||
<dl class="row">
|
|
||||||
<dt class="col-10">{{ object|help_text:'rcds'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.rcds|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-10">{{ object|help_text:'supply_test'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.supply_test|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.earthing|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.pat|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
{% else %}
|
|
||||||
<dl class="row">
|
|
||||||
<dt class="col-10">{{ object|help_text:'source_rcd'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.source_rcd|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-10">{{ object|help_text:'labelling'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.labelling|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.earthing|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.pat|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<hr>
|
|
||||||
<p>Tests at first distro</p>
|
|
||||||
<table class="table table-bordered">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col" class="text-center">Test</th>
|
|
||||||
<th scope="col" colspan="3" class="text-center">Value</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th scope="row" rowspan="2">Voltage<br><small>(cube meter)</small></th>
|
|
||||||
<th>{{ object|help_text:'fd_voltage_l1' }}</th>
|
|
||||||
<th>{{ object|help_text:'fd_voltage_l2' }}</th>
|
|
||||||
<th>{{ object|help_text:'fd_voltage_l3' }}</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ object.fd_voltage_l1 }}</td>
|
|
||||||
<td>{{ object.fd_voltage_l2 }}</td>
|
|
||||||
<td>{{ object.fd_voltage_l3 }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{ object|help_text:'fd_phase_rotation'|safe }}</th>
|
|
||||||
<td colspan="3">{{ object.fd_phase_rotation|yesnoi }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{ object|help_text:'fd_earth_fault'|safe}}</th>
|
|
||||||
<td colspan="3">{{ object.fd_earth_fault }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{ object|help_text:'fd_pssc'}}</th>
|
|
||||||
<td colspan="3">{{ object.fd_pssc }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<hr>
|
|
||||||
<p>Tests at 'Worst Case' points (at least 1 point required)</p>
|
|
||||||
<table class="table table-bordered">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col" class="text-center">Test</th>
|
|
||||||
<th scope="col" class="text-center">Point 1</th>
|
|
||||||
<th scope="col" class="text-center">Point 2</th>
|
|
||||||
<th scope="col" class="text-center">Point 3</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{ object|help_text:'w1_description'|safe}}</th>
|
|
||||||
<td>{{ object.w1_description }}</td>
|
|
||||||
<td>{{ object.w2_description|default:'' }}</td>
|
|
||||||
<td>{{ object.w3_description|default:'' }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{ object|help_text:'w1_polarity'|safe}}</th>
|
|
||||||
<td>{{ object.w1_polarity|yesnoi }}</td>
|
|
||||||
<td>{{ object.w2_polarity|default:''|yesnoi }}</td>
|
|
||||||
<td>{{ object.w3_polarity|default:''|yesnoi }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{ object|help_text:'w1_voltage'|safe}}</th>
|
|
||||||
<td>{{ object.w1_voltage }}</td>
|
|
||||||
<td>{{ object.w2_voltage|default:'' }}</td>
|
|
||||||
<td>{{ object.w3_voltage|default:'' }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{ object|help_text:'w1_earth_fault'|safe}}</th>
|
|
||||||
<td>{{ object.w1_earth_fault }}</td>
|
|
||||||
<td>{{ object.w2_earth_fault|default:'' }}</td>
|
|
||||||
<td>{{ object.w3_earth_fault|default:'' }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<hr>
|
|
||||||
<dl class="row">
|
|
||||||
<dt class="col-10">{{ object|help_text:'all_rcds_tested'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.all_rcds_tested|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-10">{{ object|help_text:'public_sockets_tested'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.public_sockets_tested|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<hr>
|
|
||||||
{% include 'partials/ec_power_info.html' %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 text-right">
|
|
||||||
{% button 'edit' url='ec_edit' pk=object.pk %}
|
|
||||||
{% button 'view' url='event_detail' pk=object.pk text="Event" %}
|
|
||||||
{% include 'partials/review_status.html' with perm=perms.RIGS.review_eventchecklist review='ec_review' %}
|
|
||||||
</div>
|
|
||||||
<div class="col-12 text-right">
|
|
||||||
{% include 'partials/last_edited.html' with target="eventchecklist_history" %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %}
|
|
||||||
{% load widget_tweaks %}
|
|
||||||
{% load static %}
|
|
||||||
{% load help_text from filters %}
|
|
||||||
{% load profile_by_index from filters %}
|
|
||||||
{% load button from filters %}
|
|
||||||
|
|
||||||
{% block css %}
|
|
||||||
{{ block.super }}
|
|
||||||
<link rel="stylesheet" href="{% static 'css/selects.css' %}"/>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block preload_js %}
|
|
||||||
{{ block.super }}
|
|
||||||
<script src="{% static 'js/selects.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block js %}
|
|
||||||
{{ block.super }}
|
|
||||||
<script src="{% static 'js/autocompleter.js' %}"></script>
|
|
||||||
<script src="{% static 'js/tooltip.js' %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="col-12">
|
|
||||||
{% include 'form_errors.html' %}
|
|
||||||
{% if edit %}
|
|
||||||
<form role="form" method="POST" action="{% url 'pt_edit' pk=object.pk %}">
|
|
||||||
{% else %}
|
|
||||||
<form role="form" method="POST" action="{% url 'event_pt' pk=event.pk %}">
|
|
||||||
{% endif %}
|
|
||||||
<input type="hidden" name="{{ form.event.name }}" id="{{ form.event.id_for_label }}"
|
|
||||||
value="{{event.pk}}"/>
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Event Information</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<dl class="row">
|
|
||||||
<dt class="col-4">Event Date</dt>
|
|
||||||
<dd class="col-8">{{ event.start_date}}{%if event.end_date %}-{{ event.end_date}}{%endif%}</dd>
|
|
||||||
<dt class="col-4">Event Name</dt>
|
|
||||||
<dd class="col-8">{{ event.name }}</dd>
|
|
||||||
<dt class="col-4">Client</dt>
|
|
||||||
<dd class="col-8">{{ event.person }}</dd>
|
|
||||||
<dt class="col-4">Event Size</dt>
|
|
||||||
<dd class="col-8">{% include 'partials/event_size.html' with object=event.riskassessment %}</dd>
|
|
||||||
</dl>
|
|
||||||
<hr>
|
|
||||||
<div class="form-group form-row" id="{{ form.power_mic.id_for_label }}-group">
|
|
||||||
<label for="{{ form.power_mic.id_for_label }}"
|
|
||||||
class="col-4 col-form-label">{{ form.power_mic.help_text }}</label>
|
|
||||||
<select id="{{ form.power_mic.id_for_label }}" name="{{ form.power_mic.name }}" class="form-control selectpicker col-8" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" required="true">
|
|
||||||
{% if power_mic %}
|
|
||||||
<option value="{{power_mic.pk}}" selected="selected">{{ power_mic.name }}</option>
|
|
||||||
{% elif event.riskassessment.power_mic %}
|
|
||||||
<option value="{{event.riskassessment.power_mic.pk}}" selected="selected">{{ event.riskassessment.power_mic.name }}</option>
|
|
||||||
{% endif %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group form-row" id="{{ form.venue.id_for_label }}-group">
|
|
||||||
<label for="{{ form.venue.id_for_label }}"
|
|
||||||
class="col-4 col-form-label">{{ form.venue.label }}</label>
|
|
||||||
<select id="{{ form.venue.id_for_label }}" name="{{ form.venue.name }}" class="form-control selectpicker col-8" data-live-search="true" data-sourceurl="{% url 'api_secure' model='venue' %}">
|
|
||||||
{% if venue %}
|
|
||||||
<option value="{{venue.pk}}" selected="selected">{{ venue.name }}</option>
|
|
||||||
{% elif event.venue %}
|
|
||||||
<option value="{{event.venue.pk}}" selected="selected">{{ event.venue.name }}</option>
|
|
||||||
{% endif %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<label for="{{ form.notes.id_for_label }}">Notes</label>
|
|
||||||
{% render_field form.notes class+="form-control" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% if event.riskassessment.event_size == 0 %}
|
|
||||||
<div class="row my-3" id="size-0">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="card border-success">
|
|
||||||
<div class="card-header">Electrical Checks <small>for ‘Small’ TEC Events <6kVA (approx. 26A)</small></div>
|
|
||||||
<div class="card-body">
|
|
||||||
{% include 'partials/checklist_checkbox.html' with formitem=form.rcds %}
|
|
||||||
{% include 'partials/checklist_checkbox.html' with formitem=form.supply_test %}
|
|
||||||
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
|
|
||||||
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="row my-3" id="size-1">
|
|
||||||
<div class="col-12">
|
|
||||||
{% if event.riskassessment.event_size == 1 %}
|
|
||||||
<div class="card border-warning">
|
|
||||||
<div class="card-header">Electrical Checks <small>for ‘Medium’ TEC Events </small></div>
|
|
||||||
<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.labelling %}
|
|
||||||
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
|
|
||||||
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
|
|
||||||
<hr>
|
|
||||||
<p>Tests at first distro</p>
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-bordered table-sm">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col" class="text-center">Test</th>
|
|
||||||
<th scope="col" colspan="3" class="text-center">Value</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th scope="row" rowspan="2">Voltage<br><small>(cube meter)</small></th>
|
|
||||||
<th class="text-center">{{ form.fd_voltage_l1.help_text }}</th>
|
|
||||||
<th class="text-center">{{ form.fd_voltage_l2.help_text }}</th>
|
|
||||||
<th class="text-center">{{ form.fd_voltage_l3.help_text }}</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{% render_field form.fd_voltage_l1 class+="form-control" style="min-width: 5rem;" %}</td>
|
|
||||||
<td>{% render_field form.fd_voltage_l2 class+="form-control" style="min-width: 5rem;" %}</td>
|
|
||||||
<td>{% render_field form.fd_voltage_l3 class+="form-control" style="min-width: 5rem;" %}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{form.fd_phase_rotation.help_text|safe}}</th>
|
|
||||||
<td colspan="3">{% include 'partials/checklist_checkbox.html' with formitem=form.fd_phase_rotation %}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{form.fd_earth_fault.help_text|safe}}</th>
|
|
||||||
<td colspan="3">{% render_field form.fd_earth_fault class+="form-control" %}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{form.fd_pssc.help_text|safe}}</th>
|
|
||||||
<td colspan="3">{% render_field form.fd_pssc class+="form-control" %}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<p>Tests at 'Worst Case' points (at least 1 point required)</p>
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-bordered">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col" class="text-center">Test</th>
|
|
||||||
<th scope="col" class="text-center">Point 1</th>
|
|
||||||
<th scope="col" class="text-center">Point 2</th>
|
|
||||||
<th scope="col" class="text-center">Point 3</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{form.w1_description.help_text|safe}}</th>
|
|
||||||
<td>{% render_field form.w1_description class+="form-control" style="min-width: 5rem;" %}</td>
|
|
||||||
<td>{% render_field form.w2_description class+="form-control" style="min-width: 5rem;" %}</td>
|
|
||||||
<td>{% render_field form.w3_description class+="form-control" style="min-width: 5rem;" %}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{form.w1_polarity.help_text|safe}}</th>
|
|
||||||
<td>{% render_field form.w1_polarity %}</td>
|
|
||||||
<td>{% render_field form.w2_polarity %}</td>
|
|
||||||
<td>{% render_field form.w3_polarity %}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{form.w1_voltage.help_text|safe}}</th>
|
|
||||||
<td>{% render_field form.w1_voltage class+="form-control" %}</td>
|
|
||||||
<td>{% render_field form.w2_voltage class+="form-control" %}</td>
|
|
||||||
<td>{% render_field form.w3_voltage class+="form-control" %}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{{form.w1_earth_fault.help_text|safe}}</th>
|
|
||||||
<td>{% render_field form.w1_earth_fault class+="form-control" %}</td>
|
|
||||||
<td>{% render_field form.w2_earth_fault class+="form-control" %}</td>
|
|
||||||
<td>{% render_field form.w3_earth_fault class+="form-control" %}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<hr/>
|
|
||||||
{% include 'partials/checklist_checkbox.html' with formitem=form.all_rcds_tested %}
|
|
||||||
{% include 'partials/checklist_checkbox.html' with formitem=form.public_sockets_tested %}
|
|
||||||
{% include 'partials/ec_power_info.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="row mt-3">
|
|
||||||
<div class="col-sm-12 text-right">
|
|
||||||
{% button 'submit' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -47,6 +47,6 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p><strong>Voltage Drop on Circuit:</strong> ≤5% (approx. 12v)</p>
|
<p><strong>Voltage Drop on Circuit:</strong> 5% (approx. 12v)</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -49,6 +49,5 @@
|
|||||||
{% 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>
|
<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>
|
||||||
<a href="{% url 'event_checkin' event.pk %}" class="btn btn-success"><span class="fas fa-user-clock"></span> <span class="d-none d-sm-inline">Check In</span></a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,18 +10,10 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<h5>Event Checklists:</h5>
|
<h5>Event Checklists:</h5>
|
||||||
{% for checklist in event.checklists.all %}
|
{% for checklist in event.checklists.all %}
|
||||||
{% include 'partials/hs_status.html' with event=event object=checklist view='ec_detail' edit='ec_edit' create='event_ec' review='ec_review' perm=perms.RIGS.review_eventchecklist %}
|
{% include 'partials/hs_status.html' with event=event object=checklist view='ec_detail' edit='ec_edit' create='event_ec' review='ec_review' perm=perms.RIGS.review_eventchecklist %}
|
||||||
<br/>
|
<br/>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<a href="{% url 'event_ec' event.pk %}" class="btn btn-info mt-2"><span class="fas fa-paperclip"></span> <span
|
<a href="{% url 'event_ec' event.pk %}" class="btn btn-info mt-2"><span class="fas fa-paperclip"></span> <span
|
||||||
class="hidden-xs">Create</span></a>
|
class="hidden-xs">Create</span></a>
|
||||||
<hr>
|
|
||||||
<h5>Power Test Records:</h5>
|
|
||||||
{% for record in event.power_tests.all %}
|
|
||||||
{% include 'partials/hs_status.html' with event=event object=record view='pt_detail' edit='pt_edit' create='event_pt' review='pt_review' perm=perms.RIGS.review_power %}
|
|
||||||
<br/>
|
|
||||||
{% endfor %}
|
|
||||||
<a href="{% url 'event_pt' event.pk %}" class="btn btn-info mt-2"><span class="fas fa-paperclip"></span> <span
|
|
||||||
class="hidden-xs">Create</span></a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -118,9 +118,9 @@ def orderby(request, field, attr):
|
|||||||
@register.filter(needs_autoescape=True) # Used for accessing outside of a form, i.e. in detail views of RiskAssessment and EventChecklist
|
@register.filter(needs_autoescape=True) # Used for accessing outside of a form, i.e. in detail views of RiskAssessment and EventChecklist
|
||||||
def get_field(obj, field, autoescape=True):
|
def get_field(obj, field, autoescape=True):
|
||||||
value = getattr(obj, field)
|
value = getattr(obj, field)
|
||||||
if (isinstance(value, bool)):
|
if(isinstance(value, bool)):
|
||||||
value = yesnoi(value, field in obj.inverted_fields)
|
value = yesnoi(value, field in obj.inverted_fields)
|
||||||
elif (isinstance(value, str)):
|
elif(isinstance(value, str)):
|
||||||
value = truncatewords(value, 20)
|
value = truncatewords(value, 20)
|
||||||
return mark_safe(value)
|
return mark_safe(value)
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ def get_list(dictionary, key):
|
|||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def profile_by_index(value):
|
def profile_by_index(value):
|
||||||
if (value):
|
if(value):
|
||||||
return models.Profile.objects.get(pk=int(value))
|
return models.Profile.objects.get(pk=int(value))
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
@@ -196,8 +196,8 @@ def button(type, url=None, pk=None, clazz="", icon=None, text="", id=None, style
|
|||||||
text = "Edit"
|
text = "Edit"
|
||||||
elif type == 'print':
|
elif type == 'print':
|
||||||
clazz += " btn-primary "
|
clazz += " btn-primary "
|
||||||
icon = "fa-download"
|
icon = "fa-print"
|
||||||
text = "Export"
|
text = "Print"
|
||||||
elif type == 'duplicate':
|
elif type == 'duplicate':
|
||||||
clazz += " btn-info "
|
clazz += " btn-info "
|
||||||
icon = "fa-copy"
|
icon = "fa-copy"
|
||||||
|
|||||||
28
RIGS/urls.py
28
RIGS/urls.py
@@ -75,34 +75,26 @@ urlpatterns = [
|
|||||||
|
|
||||||
path('event/<int:pk>/ra/', permission_required_with_403('RIGS.add_riskassessment')(views.EventRiskAssessmentCreate.as_view()),
|
path('event/<int:pk>/ra/', permission_required_with_403('RIGS.add_riskassessment')(views.EventRiskAssessmentCreate.as_view()),
|
||||||
name='event_ra'),
|
name='event_ra'),
|
||||||
path('event/ra/<int:pk>/', login_required(views.EventRiskAssessmentDetail.as_view()),
|
path('event/ra/<int:pk>/', permission_required_with_403('RIGS.view_riskassessment')(views.EventRiskAssessmentDetail.as_view()),
|
||||||
name='ra_detail'),
|
name='ra_detail'),
|
||||||
path('event/ra/<int:pk>/edit/', permission_required_with_403('RIGS.change_riskassessment')(views.EventRiskAssessmentEdit.as_view()),
|
path('event/ra/<int:pk>/edit/', permission_required_with_403('RIGS.change_riskassessment')(views.EventRiskAssessmentEdit.as_view()),
|
||||||
name='ra_edit'),
|
name='ra_edit'),
|
||||||
path('event/ra/<int:pk>/review/', permission_required_with_403('RIGS.review_riskassessment')(views.MarkReviewed.as_view()),
|
path('event/ra/list', permission_required_with_403('RIGS.view_riskassessment')(views.EventRiskAssessmentList.as_view()),
|
||||||
name='ra_review', kwargs={'model': 'RiskAssessment'}),
|
name='ra_list'),
|
||||||
|
path('event/ra/<int:pk>/review/', permission_required_with_403('RIGS.review_riskassessment')(views.EventRiskAssessmentReview.as_view()),
|
||||||
|
name='ra_review'),
|
||||||
path('event/ra/<int:pk>/print/', permission_required_with_403('RIGS.view_riskassessment')(views.RAPrint.as_view()), name='ra_print'),
|
path('event/ra/<int:pk>/print/', permission_required_with_403('RIGS.view_riskassessment')(views.RAPrint.as_view()), name='ra_print'),
|
||||||
|
|
||||||
path('event/<int:pk>/checklist/', permission_required_with_403('RIGS.add_eventchecklist')(views.EventChecklistCreate.as_view()),
|
path('event/<int:pk>/checklist/', permission_required_with_403('RIGS.add_eventchecklist')(views.EventChecklistCreate.as_view()),
|
||||||
name='event_ec'),
|
name='event_ec'),
|
||||||
path('event/checklist/<int:pk>/', login_required(views.EventChecklistDetail.as_view()),
|
path('event/checklist/<int:pk>/', permission_required_with_403('RIGS.view_eventchecklist')(views.EventChecklistDetail.as_view()),
|
||||||
name='ec_detail'),
|
name='ec_detail'),
|
||||||
path('event/checklist/<int:pk>/edit/', permission_required_with_403('RIGS.change_eventchecklist')(views.EventChecklistEdit.as_view()),
|
path('event/checklist/<int:pk>/edit/', permission_required_with_403('RIGS.change_eventchecklist')(views.EventChecklistEdit.as_view()),
|
||||||
name='ec_edit'),
|
name='ec_edit'),
|
||||||
path('event/checklist/<int:pk>/review/', permission_required_with_403('RIGS.review_eventchecklist')(views.MarkReviewed.as_view()),
|
path('event/checklist/list', permission_required_with_403('RIGS.view_eventchecklist')(views.EventChecklistList.as_view()),
|
||||||
name='ec_review', kwargs={'model': 'EventChecklist'}),
|
name='ec_list'),
|
||||||
|
path('event/checklist/<int:pk>/review/', permission_required_with_403('RIGS.review_eventchecklist')(views.EventChecklistReview.as_view()),
|
||||||
path('event/<int:pk>/power/', permission_required_with_403('RIGS.add_powertestrecord')(views.PowerTestCreate.as_view()),
|
name='ec_review'),
|
||||||
name='event_pt'),
|
|
||||||
path('event/power/<int:pk>/', login_required(views.PowerTestDetail.as_view()),
|
|
||||||
name='pt_detail'),
|
|
||||||
path('event/power/<int:pk>/edit/', permission_required_with_403('RIGS.change_powertestrecord')(views.PowerTestEdit.as_view()),
|
|
||||||
name='pt_edit'),
|
|
||||||
path('event/power/<int:pk>/review/', permission_required_with_403('RIGS.review_power')(views.MarkReviewed.as_view()),
|
|
||||||
name='pt_review', kwargs={'model': 'PowerTestRecord'}),
|
|
||||||
|
|
||||||
path('event/<int:pk>/checkin/', permission_required_with_403('RIGS.add_eventcheckin')(views.EventCheckIn.as_view()),
|
|
||||||
name='event_checkin'),
|
|
||||||
|
|
||||||
# Finance
|
# Finance
|
||||||
path('invoice/', permission_required_with_403('RIGS.view_invoice')(views.InvoiceIndex.as_view()),
|
path('invoice/', permission_required_with_403('RIGS.view_invoice')(views.InvoiceIndex.as_view()),
|
||||||
|
|||||||
188
RIGS/views/hs.py
188
RIGS/views/hs.py
@@ -1,4 +1,3 @@
|
|||||||
from django.apps import apps
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
@@ -11,35 +10,7 @@ from RIGS.views.rigboard import get_related
|
|||||||
from PyRIGS.views import PrintView
|
from PyRIGS.views import PrintView
|
||||||
|
|
||||||
|
|
||||||
class HSCreateView(generic.CreateView):
|
class EventRiskAssessmentCreate(generic.CreateView):
|
||||||
def get_form(self, **kwargs):
|
|
||||||
form = super().get_form(**kwargs)
|
|
||||||
epk = self.kwargs.get('pk')
|
|
||||||
event = models.Event.objects.get(pk=epk)
|
|
||||||
form.instance.event = event
|
|
||||||
return form
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
epk = self.kwargs.get('pk')
|
|
||||||
event = models.Event.objects.get(pk=epk)
|
|
||||||
context['event'] = event
|
|
||||||
context['page_title'] = f'Create {self} for Event {event.display_id}'
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class MarkReviewed(generic.View):
|
|
||||||
def get(self, *args, **kwargs):
|
|
||||||
obj = apps.get_model('RIGS', kwargs.get('model')).objects.get(pk=kwargs.get('pk'))
|
|
||||||
with reversion.create_revision():
|
|
||||||
reversion.set_user(self.request.user)
|
|
||||||
obj.reviewed_by = self.request.user
|
|
||||||
obj.reviewed_at = timezone.now()
|
|
||||||
obj.save()
|
|
||||||
return HttpResponseRedirect(reverse_lazy('hs_list'))
|
|
||||||
|
|
||||||
|
|
||||||
class EventRiskAssessmentCreate(HSCreateView):
|
|
||||||
model = models.RiskAssessment
|
model = models.RiskAssessment
|
||||||
template_name = 'hs/risk_assessment_form.html'
|
template_name = 'hs/risk_assessment_form.html'
|
||||||
form_class = forms.EventRiskAssessmentForm
|
form_class = forms.EventRiskAssessmentForm
|
||||||
@@ -56,6 +27,22 @@ class EventRiskAssessmentCreate(HSCreateView):
|
|||||||
|
|
||||||
return super(EventRiskAssessmentCreate, self).get(self)
|
return super(EventRiskAssessmentCreate, self).get(self)
|
||||||
|
|
||||||
|
def get_form(self, **kwargs):
|
||||||
|
form = super(EventRiskAssessmentCreate, self).get_form(**kwargs)
|
||||||
|
epk = self.kwargs.get('pk')
|
||||||
|
event = models.Event.objects.get(pk=epk)
|
||||||
|
form.instance.event = event
|
||||||
|
return form
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(EventRiskAssessmentCreate, self).get_context_data(**kwargs)
|
||||||
|
epk = self.kwargs.get('pk')
|
||||||
|
event = models.Event.objects.get(pk=epk)
|
||||||
|
context['event'] = event
|
||||||
|
context['page_title'] = f'Create Risk Assessment for Event {event.display_id}'
|
||||||
|
get_related(context['form'], context)
|
||||||
|
return context
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy('ra_detail', kwargs={'pk': self.object.pk})
|
return reverse_lazy('ra_detail', kwargs={'pk': self.object.pk})
|
||||||
|
|
||||||
@@ -93,6 +80,36 @@ class EventRiskAssessmentDetail(generic.DetailView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class EventRiskAssessmentList(generic.ListView):
|
||||||
|
paginate_by = 20
|
||||||
|
model = models.RiskAssessment
|
||||||
|
template_name = 'hs/hs_object_list.html'
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return self.model.objects.exclude(event__status=models.Event.CANCELLED).order_by('reviewed_at').select_related('event')
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(EventRiskAssessmentList, self).get_context_data(**kwargs)
|
||||||
|
context['title'] = 'Risk Assessment'
|
||||||
|
context['view'] = 'ra_detail'
|
||||||
|
context['edit'] = 'ra_edit'
|
||||||
|
context['review'] = 'ra_review'
|
||||||
|
context['perm'] = 'perms.RIGS.review_riskassessment'
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class EventRiskAssessmentReview(generic.View):
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
rpk = kwargs.get('pk')
|
||||||
|
ra = models.RiskAssessment.objects.get(pk=rpk)
|
||||||
|
with reversion.create_revision():
|
||||||
|
reversion.set_user(self.request.user)
|
||||||
|
ra.reviewed_by = self.request.user
|
||||||
|
ra.reviewed_at = timezone.now()
|
||||||
|
ra.save()
|
||||||
|
return HttpResponseRedirect(reverse_lazy('ra_list'))
|
||||||
|
|
||||||
|
|
||||||
class EventChecklistDetail(generic.DetailView):
|
class EventChecklistDetail(generic.DetailView):
|
||||||
model = models.EventChecklist
|
model = models.EventChecklist
|
||||||
template_name = 'hs/event_checklist_detail.html'
|
template_name = 'hs/event_checklist_detail.html'
|
||||||
@@ -126,7 +143,7 @@ class EventChecklistEdit(generic.UpdateView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class EventChecklistCreate(HSCreateView):
|
class EventChecklistCreate(generic.CreateView):
|
||||||
model = models.EventChecklist
|
model = models.EventChecklist
|
||||||
template_name = 'hs/event_checklist_form.html'
|
template_name = 'hs/event_checklist_form.html'
|
||||||
form_class = forms.EventChecklistForm
|
form_class = forms.EventChecklistForm
|
||||||
@@ -145,63 +162,53 @@ class EventChecklistCreate(HSCreateView):
|
|||||||
|
|
||||||
return super(EventChecklistCreate, self).get(self)
|
return super(EventChecklistCreate, self).get(self)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_form(self, **kwargs):
|
||||||
return reverse_lazy('ec_detail', kwargs={'pk': self.object.pk})
|
form = super(EventChecklistCreate, self).get_form(**kwargs)
|
||||||
|
epk = self.kwargs.get('pk')
|
||||||
|
|
||||||
class PowerTestDetail(generic.DetailView):
|
|
||||||
model = models.PowerTestRecord
|
|
||||||
template_name = 'hs/power_detail.html'
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['page_title'] = f"Power Test Record for Event <a href='{self.object.event.get_absolute_url()}'>{self.object.event.display_id} {self.object.event.name}</a>"
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class PowerTestEdit(generic.UpdateView):
|
|
||||||
model = models.PowerTestRecord
|
|
||||||
template_name = 'hs/power_form.html'
|
|
||||||
form_class = forms.PowerTestRecordForm
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
ec = self.get_object()
|
|
||||||
ec.reviewed_by = None
|
|
||||||
ec.reviewed_at = None
|
|
||||||
ec.save()
|
|
||||||
return reverse_lazy('ec_detail', kwargs={'pk': self.object.pk})
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
pk = self.kwargs.get('pk')
|
|
||||||
ec = models.PowerTestRecord.objects.get(pk=pk)
|
|
||||||
context['event'] = ec.event
|
|
||||||
context['edit'] = True
|
|
||||||
context['page_title'] = f'Edit Power Test Record for Event {ec.event.display_id}'
|
|
||||||
# get_related(context['form'], context)
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class PowerTestCreate(HSCreateView):
|
|
||||||
model = models.PowerTestRecord
|
|
||||||
template_name = 'hs/power_form.html'
|
|
||||||
form_class = forms.PowerTestRecordForm
|
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
|
||||||
epk = kwargs.get('pk')
|
|
||||||
event = models.Event.objects.get(pk=epk)
|
event = models.Event.objects.get(pk=epk)
|
||||||
|
form.instance.event = event
|
||||||
|
return form
|
||||||
|
|
||||||
# Check if RA exists
|
def get_context_data(self, **kwargs):
|
||||||
ra = models.RiskAssessment.objects.filter(event=event).first()
|
context = super(EventChecklistCreate, self).get_context_data(**kwargs)
|
||||||
|
epk = self.kwargs.get('pk')
|
||||||
if ra is None:
|
event = models.Event.objects.get(pk=epk)
|
||||||
messages.error(self.request, f'A Risk Assessment must exist prior to creating any Power Test Records for {event}! Please create one now.')
|
context['event'] = event
|
||||||
return HttpResponseRedirect(reverse_lazy('event_ra', kwargs={'pk': epk}))
|
context['page_title'] = f'Create Event Checklist for Event {event.display_id}'
|
||||||
|
return context
|
||||||
return super().get(self)
|
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy('pt_detail', kwargs={'pk': self.object.pk})
|
return reverse_lazy('ec_detail', kwargs={'pk': self.object.pk})
|
||||||
|
|
||||||
|
|
||||||
|
class EventChecklistList(generic.ListView):
|
||||||
|
paginate_by = 20
|
||||||
|
model = models.EventChecklist
|
||||||
|
template_name = 'hs/hs_object_list.html'
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return self.model.objects.exclude(event__status=models.Event.CANCELLED).order_by('reviewed_at').select_related('event')
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(EventChecklistList, self).get_context_data(**kwargs)
|
||||||
|
context['title'] = 'Event Checklist'
|
||||||
|
context['view'] = 'ec_detail'
|
||||||
|
context['edit'] = 'ec_edit'
|
||||||
|
context['review'] = 'ec_review'
|
||||||
|
context['perm'] = 'perms.RIGS.review_eventchecklist'
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class EventChecklistReview(generic.View):
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
rpk = kwargs.get('pk')
|
||||||
|
ec = models.EventChecklist.objects.get(pk=rpk)
|
||||||
|
with reversion.create_revision():
|
||||||
|
reversion.set_user(self.request.user)
|
||||||
|
ec.reviewed_by = self.request.user
|
||||||
|
ec.reviewed_at = timezone.now()
|
||||||
|
ec.save()
|
||||||
|
return HttpResponseRedirect(reverse_lazy('ec_list'))
|
||||||
|
|
||||||
|
|
||||||
class HSList(generic.ListView):
|
class HSList(generic.ListView):
|
||||||
@@ -226,16 +233,3 @@ class RAPrint(PrintView):
|
|||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['filename'] = f"EventSpecificRiskAssessment_for_{context['object'].event.display_id}.pdf"
|
context['filename'] = f"EventSpecificRiskAssessment_for_{context['object'].event.display_id}.pdf"
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class EventCheckIn(generic.CreateView):
|
|
||||||
model = models.EventCheckIn
|
|
||||||
template_name = 'hs/eventcheckin_form.html'
|
|
||||||
form_class = forms.EventCheckInForm
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['event'] = models.Event.objects.get(pk=self.kwargs.get('pk'))
|
|
||||||
context['page_title'] = f'Check In to Event {context["event"].display_id}'
|
|
||||||
# get_related(context['form'], context)
|
|
||||||
return context
|
|
||||||
|
|||||||
@@ -342,12 +342,12 @@ class EventAuthorisationRequest(generic.FormView, generic.detail.SingleObjectMix
|
|||||||
|
|
||||||
msg = EmailMultiAlternatives(
|
msg = EmailMultiAlternatives(
|
||||||
f"{self.object.display_id} | {self.object.name} - Event Authorisation Request",
|
f"{self.object.display_id} | {self.object.name} - Event Authorisation Request",
|
||||||
get_template("email/eventauthorisation_client_request.txt").render(context),
|
get_template("eventauthorisation_client_request.txt").render(context),
|
||||||
to=[email],
|
to=[email],
|
||||||
reply_to=[self.request.user.email],
|
reply_to=[self.request.user.email],
|
||||||
)
|
)
|
||||||
css = finders.find('css/email.css')
|
css = finders.find('css/email.css')
|
||||||
html = premailer.Premailer(get_template("email/eventauthorisation_client_request.html").render(context),
|
html = premailer.Premailer(get_template("eventauthorisation_client_request.html").render(context),
|
||||||
external_styles=css).transform()
|
external_styles=css).transform()
|
||||||
msg.attach_alternative(html, 'text/html')
|
msg.attach_alternative(html, 'text/html')
|
||||||
|
|
||||||
@@ -357,7 +357,7 @@ class EventAuthorisationRequest(generic.FormView, generic.detail.SingleObjectMix
|
|||||||
|
|
||||||
|
|
||||||
class EventAuthoriseRequestEmailPreview(generic.DetailView):
|
class EventAuthoriseRequestEmailPreview(generic.DetailView):
|
||||||
template_name = "email/eventauthorisation_client_request.html"
|
template_name = "eventauthorisation_client_request.html"
|
||||||
model = models.Event
|
model = models.Event
|
||||||
|
|
||||||
def render_to_response(self, context, **response_kwargs):
|
def render_to_response(self, context, **response_kwargs):
|
||||||
|
|||||||
@@ -105,7 +105,8 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
prefix = random.choice(asset_prefixes)
|
prefix = random.choice(asset_prefixes)
|
||||||
asset_id = get_available_asset_id(wanted_prefix=prefix)
|
asset_id = str(get_available_asset_id(wanted_prefix=prefix))
|
||||||
|
asset_id = prefix + asset_id
|
||||||
asset = models.Asset(
|
asset = models.Asset(
|
||||||
asset_id=asset_id,
|
asset_id=asset_id,
|
||||||
description=random.choice(asset_description),
|
description=random.choice(asset_description),
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 3.2.16 on 2022-12-11 00:26
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0026_auto_20220526_1623'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='asset',
|
|
||||||
name='nickname',
|
|
||||||
field=models.CharField(blank=True, max_length=120),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -95,15 +95,14 @@ class AssetManager(models.Manager):
|
|||||||
def search(self, query=None):
|
def search(self, query=None):
|
||||||
qs = self.get_queryset()
|
qs = self.get_queryset()
|
||||||
if query is not None:
|
if query is not None:
|
||||||
or_lookup = (Q(asset_id__exact=query.upper()) | Q(description__icontains=query) | Q(serial_number__exact=query) | Q(nickname__icontains=query))
|
or_lookup = (Q(asset_id__exact=query.upper()) | Q(description__icontains=query) | Q(serial_number__exact=query))
|
||||||
qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups
|
qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
def get_available_asset_id(wanted_prefix=""):
|
def get_available_asset_id(wanted_prefix=""):
|
||||||
last_asset = Asset.objects.filter(asset_id_prefix=wanted_prefix).last()
|
last_asset = Asset.objects.filter(asset_id_prefix=wanted_prefix).last()
|
||||||
last_asset_id = last_asset.asset_id_number if last_asset else 0
|
return 9000 if last_asset is None else wanted_prefix + str(last_asset.asset_id_number + 1)
|
||||||
return wanted_prefix + str(last_asset_id + 1)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_positive(value):
|
def validate_positive(value):
|
||||||
@@ -126,7 +125,6 @@ class Asset(models.Model, RevisionMixin):
|
|||||||
purchase_price = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=10, validators=[validate_positive])
|
purchase_price = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=10, validators=[validate_positive])
|
||||||
replacement_cost = models.DecimalField(null=True, decimal_places=2, max_digits=10, validators=[validate_positive])
|
replacement_cost = models.DecimalField(null=True, decimal_places=2, max_digits=10, validators=[validate_positive])
|
||||||
comments = models.TextField(blank=True)
|
comments = models.TextField(blank=True)
|
||||||
nickname = models.CharField(max_length=120, blank=True)
|
|
||||||
|
|
||||||
# Audit
|
# Audit
|
||||||
last_audited_at = models.DateTimeField(blank=True, null=True)
|
last_audited_at = models.DateTimeField(blank=True, null=True)
|
||||||
|
|||||||
@@ -21,10 +21,6 @@
|
|||||||
<label for="{{ form.description.id_for_label }}">Description</label>
|
<label for="{{ form.description.id_for_label }}">Description</label>
|
||||||
{% render_field form.description|add_class:'form-control' value=object.description %}
|
{% render_field form.description|add_class:'form-control' value=object.description %}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label for="{{ form.nickname.id_for_label }}">Nickname</label>
|
|
||||||
{% render_field form.nickname|add_class:'form-control' value=object.nickname %}
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="{{ form.category.id_for_label }}" >Category</label>
|
<label for="{{ form.category.id_for_label }}" >Category</label>
|
||||||
{% render_field form.category|add_class:'form-control'%}
|
{% render_field form.category|add_class:'form-control'%}
|
||||||
@@ -49,10 +45,7 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<dt>Asset ID</dt>
|
<dt>Asset ID</dt>
|
||||||
<dd>{{ object.asset_id }}</dd>
|
<dd>{{ object.asset_id }}</dd>
|
||||||
{% if object.nickname %}
|
|
||||||
<dt>Nickname</dt>
|
|
||||||
<dd>"{{ object.nickname }}"</dd>
|
|
||||||
{% endif %}
|
|
||||||
<dt>Description</dt>
|
<dt>Description</dt>
|
||||||
<dd>{{ object.description }}</dd>
|
<dd>{{ object.description }}</dd>
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ class DuplicateMixin:
|
|||||||
class AssetDuplicate(DuplicateMixin, AssetIDUrlMixin, AssetCreate):
|
class AssetDuplicate(DuplicateMixin, AssetIDUrlMixin, AssetCreate):
|
||||||
def get_initial(self, *args, **kwargs):
|
def get_initial(self, *args, **kwargs):
|
||||||
initial = super().get_initial(*args, **kwargs)
|
initial = super().get_initial(*args, **kwargs)
|
||||||
initial["asset_id"] = models.get_available_asset_id()
|
initial["asset_id"] = models.get_available_asset_id(wanted_prefix=self.get_object().asset_id_prefix)
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
|||||||
5957
package-lock.json
generated
5957
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -28,13 +28,13 @@
|
|||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
"konami": "^1.6.3",
|
"konami": "^1.6.3",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"node-sass": "^7.0.3",
|
"node-sass": "^7.0.0",
|
||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"postcss": "^8.4.5",
|
"postcss": "^8.4.5",
|
||||||
"uglify-js": "^3.14.5"
|
"uglify-js": "^3.14.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"browser-sync": "^2.27.11"
|
"browser-sync": "^2.27.10"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"gulp": "gulp",
|
"gulp": "gulp",
|
||||||
|
|||||||
@@ -143,15 +143,6 @@ class Command(BaseCommand):
|
|||||||
"Bin Diving",
|
"Bin Diving",
|
||||||
"Wiki Editing"]
|
"Wiki Editing"]
|
||||||
|
|
||||||
descriptions = [
|
|
||||||
"Physical training concentrates on mechanistic goals: training programs in this area develop specific motor skills, agility, strength or physical fitness, often with an intention of peaking at a particular time.",
|
|
||||||
"In military use, training means gaining the physical ability to perform and survive in combat, and learn the many skills needed in a time of war.",
|
|
||||||
"These include how to use a variety of weapons, outdoor survival skills, and how to survive being captured by the enemy, among many others. See military education and training.",
|
|
||||||
"While some studies have indicated relaxation training is useful for some medical conditions, autogenic training has limited results or has been the result of few studies.",
|
|
||||||
"Some occupations are inherently hazardous, and require a minimum level of competence before the practitioners can perform the work at an acceptable level of safety to themselves or others in the vicinity.",
|
|
||||||
"Occupational diving, rescue, firefighting and operation of certain types of machinery and vehicles may require assessment and certification of a minimum acceptable competence before the person is allowed to practice as a licensed instructor."
|
|
||||||
]
|
|
||||||
|
|
||||||
for i, name in enumerate(names):
|
for i, name in enumerate(names):
|
||||||
category = random.choice(self.categories)
|
category = random.choice(self.categories)
|
||||||
previous_item = models.TrainingItem.objects.filter(category=category).last()
|
previous_item = models.TrainingItem.objects.filter(category=category).last()
|
||||||
@@ -159,7 +150,7 @@ class Command(BaseCommand):
|
|||||||
number = previous_item.reference_number + 1
|
number = previous_item.reference_number + 1
|
||||||
else:
|
else:
|
||||||
number = 0
|
number = 0
|
||||||
item = models.TrainingItem.objects.create(category=category, reference_number=number, name=name, description=random.choice(descriptions) + random.choice(descriptions) + random.choice(descriptions))
|
item = models.TrainingItem.objects.create(category=category, reference_number=number, description=name)
|
||||||
self.items.append(item)
|
self.items.append(item)
|
||||||
|
|
||||||
def setup_levels(self):
|
def setup_levels(self):
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 3.2.18 on 2023-02-19 14:02
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('training', '0005_auto_20220223_1535'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='trainingitem',
|
|
||||||
old_name='description',
|
|
||||||
new_name='name',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 3.2.18 on 2023-02-19 14:02
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('training', '0006_rename_description_trainingitem_name'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='trainingitem',
|
|
||||||
name='description',
|
|
||||||
field=models.TextField(blank=True),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -85,7 +85,7 @@ class TrainingItemManager(QueryablePropertiesManager):
|
|||||||
def search(self, query=None):
|
def search(self, query=None):
|
||||||
qs = self.get_queryset()
|
qs = self.get_queryset()
|
||||||
if query is not None:
|
if query is not None:
|
||||||
or_lookup = (Q(name__icontains=query) | Q(description__icontains=query) | Q(display_id=query))
|
or_lookup = (Q(description__icontains=query) | Q(display_id=query))
|
||||||
qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups
|
qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
@@ -94,13 +94,16 @@ class TrainingItemManager(QueryablePropertiesManager):
|
|||||||
class TrainingItem(models.Model):
|
class TrainingItem(models.Model):
|
||||||
reference_number = models.IntegerField()
|
reference_number = models.IntegerField()
|
||||||
category = models.ForeignKey('TrainingCategory', related_name='items', on_delete=models.CASCADE)
|
category = models.ForeignKey('TrainingCategory', related_name='items', on_delete=models.CASCADE)
|
||||||
name = models.CharField(max_length=50)
|
description = models.CharField(max_length=50)
|
||||||
description = models.TextField(blank=True)
|
|
||||||
active = models.BooleanField(default=True)
|
active = models.BooleanField(default=True)
|
||||||
prerequisites = models.ManyToManyField('self', symmetrical=False, blank=True)
|
prerequisites = models.ManyToManyField('self', symmetrical=False, blank=True)
|
||||||
|
|
||||||
objects = TrainingItemManager()
|
objects = TrainingItemManager()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return str(self)
|
||||||
|
|
||||||
@queryable_property
|
@queryable_property
|
||||||
def display_id(self):
|
def display_id(self):
|
||||||
return f"{self.category.reference_number}.{self.reference_number}"
|
return f"{self.category.reference_number}.{self.reference_number}"
|
||||||
@@ -118,7 +121,7 @@ class TrainingItem(models.Model):
|
|||||||
return models.Q()
|
return models.Q()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
name = f"{self.display_id} {self.name}"
|
name = f"{self.display_id} {self.description}"
|
||||||
if not self.active:
|
if not self.active:
|
||||||
name += " (inactive)"
|
name += " (inactive)"
|
||||||
return name
|
return name
|
||||||
@@ -146,7 +149,7 @@ class TrainingItemQualificationManager(QueryablePropertiesManager):
|
|||||||
def search(self, query=None):
|
def search(self, query=None):
|
||||||
qs = self.get_queryset().select_related('item', 'supervisor', 'item__category')
|
qs = self.get_queryset().select_related('item', 'supervisor', 'item__category')
|
||||||
if query is not None:
|
if query is not None:
|
||||||
or_lookup = (Q(item__name__icontains=query) | Q(supervisor__first_name__icontains=query) | Q(supervisor__last_name__icontains=query) | Q(item__category__name__icontains=query) | Q(item__display_id=query))
|
or_lookup = (Q(item__description__icontains=query) | Q(supervisor__first_name__icontains=query) | Q(supervisor__last_name__icontains=query) | Q(item__category__name__icontains=query) | Q(item__display_id=query))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
or_lookup = Q(item__category__reference_number=int(query)) | or_lookup
|
or_lookup = Q(item__category__reference_number=int(query)) | or_lookup
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
{% extends 'base_training.html' %}
|
{% extends 'base_training.html' %}
|
||||||
|
|
||||||
{% load button from filters %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-12 text-right py-2 pr-0">
|
|
||||||
{% button 'print' 'item_list_export' %}
|
|
||||||
</div>
|
|
||||||
<div id="accordion">
|
<div id="accordion">
|
||||||
{% for category in categories %}
|
{% for category in categories %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -18,11 +13,10 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="list-group list-group-flush">
|
<div class="list-group list-group-flush">
|
||||||
{% for item in category.items.all %}
|
{% for item in category.items.all %}
|
||||||
<li class="list-group-item {% if not item.active%}text-warning{%endif%}">{{ item }} <a href="{% url 'item_qualification' item.pk %}" class="btn btn-info float-right"><span class="fas fa-user"></span> Qualified Users</a>
|
<li class="list-group-item {% if not item.active%}text-warning{%endif%}">{{ item }}
|
||||||
<br><small>{{ item.description }}</small>
|
|
||||||
{% if item.prerequisites.exists %}
|
{% if item.prerequisites.exists %}
|
||||||
<div class="ml-3 font-italic">
|
<div class="ml-3 font-italic">
|
||||||
<p class="text-info mb-0">Competency Assessment Prerequisites:</p>
|
<p class="text-info mb-0">Passed Out Prerequisites:</p>
|
||||||
<ul>
|
<ul>
|
||||||
{% for p in item.prerequisites.all %}
|
{% for p in item.prerequisites.all %}
|
||||||
<li>{{p}}</li>
|
<li>{{p}}</li>
|
||||||
@@ -30,6 +24,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<a href="{% url 'item_qualification' item.pk %}" class="btn btn-info"><span class="fas fa-user"></span> Qualified Users</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
{% extends 'base_print.xml' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1 style="page-head">TEC Training Item List</h1>
|
|
||||||
<spacer length="15" />
|
|
||||||
{% for category in categories %}
|
|
||||||
<h2 {% if not forloop.first %}style="breakbefore"{%else%}style="emheader"{%endif%}>{{category}}</h2>
|
|
||||||
<spacer length="10" />
|
|
||||||
{% for item in category.items.all %}
|
|
||||||
<h3>{{ item }}</h3>
|
|
||||||
<spacer length="4" />
|
|
||||||
<para>{{ item.description }}</para>
|
|
||||||
{% if item.prerequisites.exists %}
|
|
||||||
<h4>Competency Assessment Prerequisites:</h4>
|
|
||||||
<ul bulletFontSize="5">
|
|
||||||
{% for p in item.prerequisites.all %}
|
|
||||||
<li><para>{{p}}</para></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
<spacer length="8" />
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
<namedString id="lastPage"><pageNumber/></namedString>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -2,19 +2,20 @@ from django.urls import path
|
|||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from training.decorators import is_supervisor
|
from training.decorators import is_supervisor
|
||||||
|
from PyRIGS.decorators import permission_required_with_403
|
||||||
|
|
||||||
from training import views, models
|
from training import views, models
|
||||||
from versioning.views import VersionHistory
|
from versioning.views import VersionHistory
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('items/', login_required(views.ItemList.as_view()), name='item_list'),
|
path('items/', login_required(views.ItemList.as_view()), name='item_list'),
|
||||||
path('items/export/', login_required(views.ItemListExport.as_view()), name='item_list_export'),
|
|
||||||
path('item/<int:pk>/qualified_users/', login_required(views.ItemQualifications.as_view()), name='item_qualification'),
|
path('item/<int:pk>/qualified_users/', login_required(views.ItemQualifications.as_view()), name='item_qualification'),
|
||||||
|
|
||||||
path('trainee/list/', login_required(views.TraineeList.as_view()), name='trainee_list'),
|
path('trainee/list/', login_required(views.TraineeList.as_view()), name='trainee_list'),
|
||||||
path('trainee/<int:pk>/', login_required(views.TraineeDetail.as_view()),
|
path('trainee/<int:pk>/',
|
||||||
|
permission_required_with_403('RIGS.view_profile')(views.TraineeDetail.as_view()),
|
||||||
name='trainee_detail'),
|
name='trainee_detail'),
|
||||||
path('trainee/<int:pk>/history', login_required(VersionHistory.as_view()), name='trainee_history', kwargs={'model': models.Trainee, 'app': 'training'}), # Not picked up automatically because proxy model (I think)
|
path('trainee/<int:pk>/history', permission_required_with_403('RIGS.view_profile')(VersionHistory.as_view()), name='trainee_history', kwargs={'model': models.Trainee, 'app': 'training'}), # Not picked up automatically because proxy model (I think)
|
||||||
path('trainee/<int:pk>/add_qualification/', is_supervisor()(views.AddQualification.as_view()),
|
path('trainee/<int:pk>/add_qualification/', is_supervisor()(views.AddQualification.as_view()),
|
||||||
name='add_qualification'),
|
name='add_qualification'),
|
||||||
path('trainee/edit_qualification/<int:pk>/', is_supervisor()(views.EditQualification.as_view()),
|
path('trainee/edit_qualification/<int:pk>/', is_supervisor()(views.EditQualification.as_view()),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from django.db import transaction
|
|||||||
from django.db.models import Q, Count
|
from django.db.models import Q, Count
|
||||||
from django.db.utils import IntegrityError
|
from django.db.utils import IntegrityError
|
||||||
|
|
||||||
from PyRIGS.views import is_ajax, ModalURLMixin, get_related, PrintListView
|
from PyRIGS.views import is_ajax, ModalURLMixin, get_related
|
||||||
from training import models, forms
|
from training import models, forms
|
||||||
from users import views
|
from users import views
|
||||||
from reversion.views import RevisionMixin
|
from reversion.views import RevisionMixin
|
||||||
@@ -24,20 +24,6 @@ class ItemList(generic.ListView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class ItemListExport(PrintListView):
|
|
||||||
model = models.TrainingItem
|
|
||||||
template_name = 'item_list.xml'
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return self.model.objects.filter(active=True)
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['filename'] = "TrainingItemList.pdf"
|
|
||||||
context["categories"] = models.TrainingCategory.objects.all()
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class TraineeDetail(views.ProfileDetail):
|
class TraineeDetail(views.ProfileDetail):
|
||||||
template_name = "trainee_detail.html"
|
template_name = "trainee_detail.html"
|
||||||
model = models.Trainee
|
model = models.Trainee
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ class ModelComparison:
|
|||||||
def name(self):
|
def name(self):
|
||||||
obj = self.new if self.new else self.old
|
obj = self.new if self.new else self.old
|
||||||
|
|
||||||
if (hasattr(obj, 'activity_feed_string')):
|
if(hasattr(obj, 'activity_feed_string')):
|
||||||
return obj.activity_feed_string
|
return obj.activity_feed_string
|
||||||
else:
|
else:
|
||||||
return str(obj)
|
return str(obj)
|
||||||
|
|||||||
Reference in New Issue
Block a user