mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-10-05 12:05:38 +00:00
Port to Django 5.2 (#635)
* Port to Django 5.2 * Pin pluggy to 1.2.0 Any newer and the mystery importlib metadata error appears. Weird! >_> * Update for premailer changed default * Update view logic for is_ajax being changed to a template context processor * Port a few more tests to pytest proper Having two distinct test flavours is giving me a headache * Version 1 dockerfile Makes a VERY big image, I suspect we can optimise this a lot... * Optimise dockerfile a little lot a bit * fix(users): change logout link to POST request * fix(tests): fix some syntax errors in test code still got lots of failed tests :( * fix: replace deprecated Django APIs * ci: update Dockerfile * ci: update Dockerfile * fix(dependencies+tests): update EoL/vulnerable dependencies and improve test stability * Upgrade Python from 3.10 to 3.12 * Update frontend dependencies, replace node-sass with sass * chore(logging): ignore dangling obj reference warning from pypdf * ci: update compose.yml for prod deployment - Add Nginx as a reverse proxy - Add cert-selfsign.sh for generating self-signed certificates - Add certbot-issue.sh for Let's Encrypt certificate issuance and renewal - Add cron-install.sh and cron-uninstall.sh for system cron management - Add .env.example as an environment variable template * style: reformat code * ci: add more sleep trying to pass tests * ci: ignore browser-based tests during ci testing * fix: create home for the new user during Dockerfile building * chore: remove heroku conf file * fix: RIGS not franken anymore * fix: restore is_ajax as a boolean and split out the context processor * test: remove Selenium interaction tests and their CI workarounds * fix: harden production settings (CSRF origins, env casts, ADMINS) and drop dead debug toolbar code * deploy: serve static files from nginx, pin PGDATA, and make cert scripts loud on failure * build: switch .dockerignore to an allowlist * build: drop unused dependencies, soft-pin the rest and target Python 3.14 * build: move image to Python 3.14 / Node 24 and force DEBUG off * ci: add dependabot config for uv, npm, docker and actions * build: narrow Sass deprecation silencing to @import and require Node 24 * build: provide placeholder env for collectstatic now that DEBUG is off in the image * deploy: add plain-HTTP nginx config for local development and use it in compose * deploy: remove self-signed cert script * build: make the image multi-arch with official node and python base images * ci: lint and test on PRs, build the image on PRs and push it to GHCR on master * docs: add local running and sample data instructions * fix: report a form error instead of crashing when big power has no Power MIC * fix: upgrade pypdf and urllib3 to patched releases * fix: apply non-breaking npm audit fixes * fix: strip whitespace in ALLOWED_HOSTS and drop misleading HSTS preload * deploy: add a pyrigs healthcheck and make nginx wait for it * build: pin the uv image version * fix: correct the check-in person picker condition and use BeautifulSoup's string argument * refactor: replace unique_together with UniqueConstraint * build: replace pycodestyle with ruff and fix what it found Removes unused imports and variables, and fixes a few real problems it surfaced: - EventCheckIn.active() referenced an undefined name and raised NameError; it now returns whether the check-in has no end time - RIGS.admin defined EventChecklistAdmin twice; the second is now PowerTestRecordAdmin - RIGS/tests/conftest.py used date/timedelta without importing them - the signal-registering imports in apps.py are kept with noqa pycodestyle config in setup.cfg is dropped. * style: format the codebase with ruff * style: normalise line endings, trailing whitespace and end-of-file newlines * ci: run ruff and file hygiene through prek, and document it --------- Co-authored-by: Hang <me@hangxu.me> Co-authored-by: Joe Banks <joe@jb3.dev>
This commit is contained in:
@@ -7,13 +7,13 @@ from training import models as tmodels
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Deletes testing sample data'
|
||||
help = "Deletes testing sample data"
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
from django.conf import settings
|
||||
|
||||
if not settings.DEBUG:
|
||||
raise CommandError('You cannot run this command in production')
|
||||
raise CommandError("You cannot run this command in production")
|
||||
|
||||
self.delete_objects(models.AssetCategory)
|
||||
self.delete_objects(models.AssetStatus)
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
from django.core.management import call_command
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from RIGS import models
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Adds sample data to use for testing'
|
||||
help = "Adds sample data to use for testing"
|
||||
can_import_settings = True
|
||||
|
||||
def handle(self, *args, **options):
|
||||
call_command('generateSampleUserData')
|
||||
call_command('generateSampleRIGSData')
|
||||
call_command('generateSampleAssetsData')
|
||||
call_command('generateSampleTrainingData')
|
||||
call_command("generateSampleUserData")
|
||||
call_command("generateSampleRIGSData")
|
||||
call_command("generateSampleAssetsData")
|
||||
call_command("generateSampleTrainingData")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import datetime
|
||||
import random
|
||||
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import transaction
|
||||
from django.utils import timezone
|
||||
@@ -11,7 +10,7 @@ from RIGS import models
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Adds sample data to use for testing'
|
||||
help = "Adds sample data to use for testing"
|
||||
can_import_settings = True
|
||||
|
||||
people = []
|
||||
@@ -25,13 +24,14 @@ class Command(BaseCommand):
|
||||
from django.conf import settings
|
||||
|
||||
if not (settings.DEBUG or settings.STAGING):
|
||||
raise CommandError('You cannot run this command in production')
|
||||
raise CommandError("You cannot run this command in production")
|
||||
|
||||
random.seed(
|
||||
'Some object to seed the random number generator') # otherwise it is done by time, which could lead to inconsistant tests
|
||||
"Some object to seed the random number generator"
|
||||
) # otherwise it is done by time, which could lead to inconsistant tests
|
||||
|
||||
with transaction.atomic():
|
||||
models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
|
||||
models.VatRate.objects.create(start_at="2014-03-05", rate=0.20, comment="test1")
|
||||
self.setup_people()
|
||||
self.setup_organisations()
|
||||
self.setup_venues()
|
||||
@@ -39,18 +39,72 @@ class Command(BaseCommand):
|
||||
print("Done generating rigboard data")
|
||||
|
||||
def setup_people(self):
|
||||
names = ["Regulus Black", "Sirius Black", "Lavender Brown", "Cho Chang", "Vincent Crabbe", "Vincent Crabbe",
|
||||
"Bartemius Crouch", "Fleur Delacour", "Cedric Diggory", "Alberforth Dumbledore", "Albus Dumbledore",
|
||||
"Dudley Dursley", "Petunia Dursley", "Vernon Dursley", "Argus Filch", "Seamus Finnigan",
|
||||
"Nicolas Flamel", "Cornelius Fudge", "Goyle", "Gregory Goyle", "Hermione Granger", "Rubeus Hagrid",
|
||||
"Igor Karkaroff", "Viktor Krum", "Bellatrix Lestrange", "Alice Longbottom", "Frank Longbottom",
|
||||
"Neville Longbottom", "Luna Lovegood", "Xenophilius Lovegood", # noqa
|
||||
"Remus Lupin", "Draco Malfoy", "Lucius Malfoy", "Narcissa Malfoy", "Olympe Maxime",
|
||||
"Minerva McGonagall", "Mad-Eye Moody", "Peter Pettigrew", "Harry Potter", "James Potter",
|
||||
"Lily Potter", "Quirinus Quirrell", "Tom Riddle", "Mary Riddle", "Lord Voldemort", "Rita Skeeter",
|
||||
"Severus Snape", "Nymphadora Tonks", "Dolores Janes Umbridge", "Arthur Weasley", "Bill Weasley",
|
||||
"Charlie Weasley", "Fred Weasley", "George Weasley", "Ginny Weasley", "Molly Weasley", "Percy Weasley",
|
||||
"Ron Weasley", "Dobby", "Fluffy", "Hedwig", "Moaning Myrtle", "Aragog", "Grawp"] # noqa
|
||||
names = [
|
||||
"Regulus Black",
|
||||
"Sirius Black",
|
||||
"Lavender Brown",
|
||||
"Cho Chang",
|
||||
"Vincent Crabbe",
|
||||
"Vincent Crabbe",
|
||||
"Bartemius Crouch",
|
||||
"Fleur Delacour",
|
||||
"Cedric Diggory",
|
||||
"Alberforth Dumbledore",
|
||||
"Albus Dumbledore",
|
||||
"Dudley Dursley",
|
||||
"Petunia Dursley",
|
||||
"Vernon Dursley",
|
||||
"Argus Filch",
|
||||
"Seamus Finnigan",
|
||||
"Nicolas Flamel",
|
||||
"Cornelius Fudge",
|
||||
"Goyle",
|
||||
"Gregory Goyle",
|
||||
"Hermione Granger",
|
||||
"Rubeus Hagrid",
|
||||
"Igor Karkaroff",
|
||||
"Viktor Krum",
|
||||
"Bellatrix Lestrange",
|
||||
"Alice Longbottom",
|
||||
"Frank Longbottom",
|
||||
"Neville Longbottom",
|
||||
"Luna Lovegood",
|
||||
"Xenophilius Lovegood", # noqa
|
||||
"Remus Lupin",
|
||||
"Draco Malfoy",
|
||||
"Lucius Malfoy",
|
||||
"Narcissa Malfoy",
|
||||
"Olympe Maxime",
|
||||
"Minerva McGonagall",
|
||||
"Mad-Eye Moody",
|
||||
"Peter Pettigrew",
|
||||
"Harry Potter",
|
||||
"James Potter",
|
||||
"Lily Potter",
|
||||
"Quirinus Quirrell",
|
||||
"Tom Riddle",
|
||||
"Mary Riddle",
|
||||
"Lord Voldemort",
|
||||
"Rita Skeeter",
|
||||
"Severus Snape",
|
||||
"Nymphadora Tonks",
|
||||
"Dolores Janes Umbridge",
|
||||
"Arthur Weasley",
|
||||
"Bill Weasley",
|
||||
"Charlie Weasley",
|
||||
"Fred Weasley",
|
||||
"George Weasley",
|
||||
"Ginny Weasley",
|
||||
"Molly Weasley",
|
||||
"Percy Weasley",
|
||||
"Ron Weasley",
|
||||
"Dobby",
|
||||
"Fluffy",
|
||||
"Hedwig",
|
||||
"Moaning Myrtle",
|
||||
"Aragog",
|
||||
"Grawp",
|
||||
] # noqa
|
||||
for i, name in enumerate(names):
|
||||
with reversion.create_revision():
|
||||
reversion.set_user(random.choice(models.Profile.objects.all()))
|
||||
@@ -72,32 +126,131 @@ class Command(BaseCommand):
|
||||
self.people.append(person)
|
||||
|
||||
def setup_organisations(self):
|
||||
names = ["Acme, inc.", "Widget Corp", "123 Warehousing", "Demo Company", "Smith and Co.", "Foo Bars",
|
||||
"ABC Telecom", "Fake Brothers", "QWERTY Logistics", "Demo, inc.", "Sample Company", "Sample, inc",
|
||||
"Acme Corp", "Allied Biscuit", "Ankh-Sto Associates", "Extensive Enterprise", "Galaxy Corp",
|
||||
"Globo-Chem", "Mr. Sparkle", "Globex Corporation", "LexCorp", "LuthorCorp",
|
||||
"North Central Positronics", "Omni Consimer Products", "Praxis Corporation", "Sombra Corporation",
|
||||
"Sto Plains Holdings", "Tessier-Ashpool", "Wayne Enterprises", "Wentworth Industries", "ZiffCorp",
|
||||
"Bluth Company", "Strickland Propane", "Thatherton Fuels", "Three Waters", "Water and Power",
|
||||
"Western Gas & Electric", "Mammoth Pictures", "Mooby Corp", "Gringotts", "Thrift Bank",
|
||||
"Flowers By Irene", "The Legitimate Businessmens Club", "Osato Chemicals", "Transworld Consortium",
|
||||
"Universal Export", "United Fried Chicken", "Virtucon", "Kumatsu Motors", "Keedsler Motors",
|
||||
"Powell Motors", "Industrial Automation", "Sirius Cybernetics Corporation",
|
||||
"U.S. Robotics and Mechanical Men", "Colonial Movers", "Corellian Engineering Corporation",
|
||||
"Incom Corporation", "General Products", "Leeding Engines Ltd.", "Blammo", # noqa
|
||||
"Input, Inc.", "Mainway Toys", "Videlectrix", "Zevo Toys", "Ajax", "Axis Chemical Co.", "Barrytron",
|
||||
"Carrys Candles", "Cogswell Cogs", "Spacely Sprockets", "General Forge and Foundry",
|
||||
"Duff Brewing Company", "Dunder Mifflin", "General Services Corporation", "Monarch Playing Card Co.",
|
||||
"Krustyco", "Initech", "Roboto Industries", "Primatech", "Sonky Rubber Goods", "St. Anky Beer",
|
||||
"Stay Puft Corporation", "Vandelay Industries", "Wernham Hogg", "Gadgetron",
|
||||
"Burleigh and Stronginthearm", "BLAND Corporation", "Nordyne Defense Dynamics", "Petrox Oil Company",
|
||||
"Roxxon", "McMahon and Tate", "Sixty Second Avenue", "Charles Townsend Agency", "Spade and Archer",
|
||||
"Megadodo Publications", "Rouster and Sideways", "C.H. Lavatory and Sons", "Globo Gym American Corp",
|
||||
"The New Firm", "SpringShield", "Compuglobalhypermeganet", "Data Systems", "Gizmonic Institute",
|
||||
"Initrode", "Taggart Transcontinental", "Atlantic Northern", "Niagular", "Plow King",
|
||||
"Big Kahuna Burger", "Big T Burgers and Fries", "Chez Quis", "Chotchkies", "The Frying Dutchman",
|
||||
"Klimpys", "The Krusty Krab", "Monks Diner", "Milliways", "Minuteman Cafe", "Taco Grande",
|
||||
"Tip Top Cafe", "Moes Tavern", "Central Perk", "Chasers"] # noqa
|
||||
names = [
|
||||
"Acme, inc.",
|
||||
"Widget Corp",
|
||||
"123 Warehousing",
|
||||
"Demo Company",
|
||||
"Smith and Co.",
|
||||
"Foo Bars",
|
||||
"ABC Telecom",
|
||||
"Fake Brothers",
|
||||
"QWERTY Logistics",
|
||||
"Demo, inc.",
|
||||
"Sample Company",
|
||||
"Sample, inc",
|
||||
"Acme Corp",
|
||||
"Allied Biscuit",
|
||||
"Ankh-Sto Associates",
|
||||
"Extensive Enterprise",
|
||||
"Galaxy Corp",
|
||||
"Globo-Chem",
|
||||
"Mr. Sparkle",
|
||||
"Globex Corporation",
|
||||
"LexCorp",
|
||||
"LuthorCorp",
|
||||
"North Central Positronics",
|
||||
"Omni Consimer Products",
|
||||
"Praxis Corporation",
|
||||
"Sombra Corporation",
|
||||
"Sto Plains Holdings",
|
||||
"Tessier-Ashpool",
|
||||
"Wayne Enterprises",
|
||||
"Wentworth Industries",
|
||||
"ZiffCorp",
|
||||
"Bluth Company",
|
||||
"Strickland Propane",
|
||||
"Thatherton Fuels",
|
||||
"Three Waters",
|
||||
"Water and Power",
|
||||
"Western Gas & Electric",
|
||||
"Mammoth Pictures",
|
||||
"Mooby Corp",
|
||||
"Gringotts",
|
||||
"Thrift Bank",
|
||||
"Flowers By Irene",
|
||||
"The Legitimate Businessmens Club",
|
||||
"Osato Chemicals",
|
||||
"Transworld Consortium",
|
||||
"Universal Export",
|
||||
"United Fried Chicken",
|
||||
"Virtucon",
|
||||
"Kumatsu Motors",
|
||||
"Keedsler Motors",
|
||||
"Powell Motors",
|
||||
"Industrial Automation",
|
||||
"Sirius Cybernetics Corporation",
|
||||
"U.S. Robotics and Mechanical Men",
|
||||
"Colonial Movers",
|
||||
"Corellian Engineering Corporation",
|
||||
"Incom Corporation",
|
||||
"General Products",
|
||||
"Leeding Engines Ltd.",
|
||||
"Blammo", # noqa
|
||||
"Input, Inc.",
|
||||
"Mainway Toys",
|
||||
"Videlectrix",
|
||||
"Zevo Toys",
|
||||
"Ajax",
|
||||
"Axis Chemical Co.",
|
||||
"Barrytron",
|
||||
"Carrys Candles",
|
||||
"Cogswell Cogs",
|
||||
"Spacely Sprockets",
|
||||
"General Forge and Foundry",
|
||||
"Duff Brewing Company",
|
||||
"Dunder Mifflin",
|
||||
"General Services Corporation",
|
||||
"Monarch Playing Card Co.",
|
||||
"Krustyco",
|
||||
"Initech",
|
||||
"Roboto Industries",
|
||||
"Primatech",
|
||||
"Sonky Rubber Goods",
|
||||
"St. Anky Beer",
|
||||
"Stay Puft Corporation",
|
||||
"Vandelay Industries",
|
||||
"Wernham Hogg",
|
||||
"Gadgetron",
|
||||
"Burleigh and Stronginthearm",
|
||||
"BLAND Corporation",
|
||||
"Nordyne Defense Dynamics",
|
||||
"Petrox Oil Company",
|
||||
"Roxxon",
|
||||
"McMahon and Tate",
|
||||
"Sixty Second Avenue",
|
||||
"Charles Townsend Agency",
|
||||
"Spade and Archer",
|
||||
"Megadodo Publications",
|
||||
"Rouster and Sideways",
|
||||
"C.H. Lavatory and Sons",
|
||||
"Globo Gym American Corp",
|
||||
"The New Firm",
|
||||
"SpringShield",
|
||||
"Compuglobalhypermeganet",
|
||||
"Data Systems",
|
||||
"Gizmonic Institute",
|
||||
"Initrode",
|
||||
"Taggart Transcontinental",
|
||||
"Atlantic Northern",
|
||||
"Niagular",
|
||||
"Plow King",
|
||||
"Big Kahuna Burger",
|
||||
"Big T Burgers and Fries",
|
||||
"Chez Quis",
|
||||
"Chotchkies",
|
||||
"The Frying Dutchman",
|
||||
"Klimpys",
|
||||
"The Krusty Krab",
|
||||
"Monks Diner",
|
||||
"Milliways",
|
||||
"Minuteman Cafe",
|
||||
"Taco Grande",
|
||||
"Tip Top Cafe",
|
||||
"Moes Tavern",
|
||||
"Central Perk",
|
||||
"Chasers",
|
||||
] # noqa
|
||||
for i, name in enumerate(names):
|
||||
with reversion.create_revision():
|
||||
reversion.set_user(random.choice(models.Profile.objects.all()))
|
||||
@@ -113,7 +266,9 @@ class Command(BaseCommand):
|
||||
new_organisation.notes = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua"
|
||||
|
||||
if i % 7 == 0:
|
||||
new_organisation.address = "1 Organisation Test Street \n Demoton \n United States of TEC \n RMRF 567"
|
||||
new_organisation.address = (
|
||||
"1 Organisation Test Street \n Demoton \n United States of TEC \n RMRF 567"
|
||||
)
|
||||
|
||||
if i % 9 == 0:
|
||||
new_organisation.phone = "01234 567894"
|
||||
@@ -122,18 +277,76 @@ class Command(BaseCommand):
|
||||
self.organisations.append(new_organisation)
|
||||
|
||||
def setup_venues(self):
|
||||
names = ["Bear Island", "Crossroads Inn", "Deepwood Motte", "The Dreadfort", "The Eyrie", "Greywater Watch",
|
||||
"The Iron Islands", "Karhold", "Moat Cailin", "Oldstones", "Raventree Hall", "Riverlands",
|
||||
"The Ruby Ford", "Saltpans", "Seagard", "Torrhen's Square", "The Trident", "The Twins",
|
||||
"The Vale of Arryn", "The Whispering Wood", "White Harbor", "Winterfell", "The Arbor", "Ashemark",
|
||||
"Brightwater Keep", "Casterly Rock", "Clegane's Keep", "Dragonstone", "Dorne", "God's Eye",
|
||||
"The Golden Tooth", # noqa
|
||||
"Harrenhal", "Highgarden", "Horn Hill", "Fingers", "King's Landing", "Lannisport", "Oldtown",
|
||||
"Rainswood", "Storm's End", "Summerhall", "Sunspear", "Tarth", "Castle Black", "Craster's Keep",
|
||||
"Fist of the First Men", "The Frostfangs", "The Gift", "The Skirling Pass", "The Wall", "Asshai",
|
||||
"Astapor", "Braavos", "The Dothraki Sea", "Lys", "Meereen", "Myr", "Norvos", "Pentos", "Qarth",
|
||||
"Qohor", "The Red Waste", "Tyrosh", "Vaes Dothrak", "Valyria", "Village of the Lhazareen", "Volantis",
|
||||
"Yunkai"] # noqa
|
||||
names = [
|
||||
"Bear Island",
|
||||
"Crossroads Inn",
|
||||
"Deepwood Motte",
|
||||
"The Dreadfort",
|
||||
"The Eyrie",
|
||||
"Greywater Watch",
|
||||
"The Iron Islands",
|
||||
"Karhold",
|
||||
"Moat Cailin",
|
||||
"Oldstones",
|
||||
"Raventree Hall",
|
||||
"Riverlands",
|
||||
"The Ruby Ford",
|
||||
"Saltpans",
|
||||
"Seagard",
|
||||
"Torrhen's Square",
|
||||
"The Trident",
|
||||
"The Twins",
|
||||
"The Vale of Arryn",
|
||||
"The Whispering Wood",
|
||||
"White Harbor",
|
||||
"Winterfell",
|
||||
"The Arbor",
|
||||
"Ashemark",
|
||||
"Brightwater Keep",
|
||||
"Casterly Rock",
|
||||
"Clegane's Keep",
|
||||
"Dragonstone",
|
||||
"Dorne",
|
||||
"God's Eye",
|
||||
"The Golden Tooth", # noqa
|
||||
"Harrenhal",
|
||||
"Highgarden",
|
||||
"Horn Hill",
|
||||
"Fingers",
|
||||
"King's Landing",
|
||||
"Lannisport",
|
||||
"Oldtown",
|
||||
"Rainswood",
|
||||
"Storm's End",
|
||||
"Summerhall",
|
||||
"Sunspear",
|
||||
"Tarth",
|
||||
"Castle Black",
|
||||
"Craster's Keep",
|
||||
"Fist of the First Men",
|
||||
"The Frostfangs",
|
||||
"The Gift",
|
||||
"The Skirling Pass",
|
||||
"The Wall",
|
||||
"Asshai",
|
||||
"Astapor",
|
||||
"Braavos",
|
||||
"The Dothraki Sea",
|
||||
"Lys",
|
||||
"Meereen",
|
||||
"Myr",
|
||||
"Norvos",
|
||||
"Pentos",
|
||||
"Qarth",
|
||||
"Qohor",
|
||||
"The Red Waste",
|
||||
"Tyrosh",
|
||||
"Vaes Dothrak",
|
||||
"Valyria",
|
||||
"Village of the Lhazareen",
|
||||
"Volantis",
|
||||
"Yunkai",
|
||||
] # noqa
|
||||
for i, name in enumerate(names):
|
||||
with reversion.create_revision():
|
||||
reversion.set_user(random.choice(self.profiles))
|
||||
@@ -158,32 +371,81 @@ class Command(BaseCommand):
|
||||
self.venues.append(new_venue)
|
||||
|
||||
def setup_events(self):
|
||||
names = ["Outdoor Concert", "Hall Open Mic Night", "Festival", "Weekend Event", "Magic Show", "Society Ball",
|
||||
"Evening Show", "Talent Show", "Acoustic Evening", "Hire of Things", "SU Event",
|
||||
"End of Term Show", "Theatre Show", "Outdoor Fun Day", "Summer Carnival", "Open Days", "Magic Show",
|
||||
"Awards Ceremony", "Debating Event", "Club Night", "DJ Evening", "Building Projection",
|
||||
"Choir Concert"]
|
||||
descriptions = ["A brief description of the event", "This event is boring", "Probably wont happen",
|
||||
"Warning: this has lots of kit"]
|
||||
notes = ["The client came into the office at some point", "Who knows if this will happen",
|
||||
"Probably should check this event", "Maybe not happening", "Run away!"]
|
||||
names = [
|
||||
"Outdoor Concert",
|
||||
"Hall Open Mic Night",
|
||||
"Festival",
|
||||
"Weekend Event",
|
||||
"Magic Show",
|
||||
"Society Ball",
|
||||
"Evening Show",
|
||||
"Talent Show",
|
||||
"Acoustic Evening",
|
||||
"Hire of Things",
|
||||
"SU Event",
|
||||
"End of Term Show",
|
||||
"Theatre Show",
|
||||
"Outdoor Fun Day",
|
||||
"Summer Carnival",
|
||||
"Open Days",
|
||||
"Magic Show",
|
||||
"Awards Ceremony",
|
||||
"Debating Event",
|
||||
"Club Night",
|
||||
"DJ Evening",
|
||||
"Building Projection",
|
||||
"Choir Concert",
|
||||
]
|
||||
descriptions = [
|
||||
"A brief description of the event",
|
||||
"This event is boring",
|
||||
"Probably wont happen",
|
||||
"Warning: this has lots of kit",
|
||||
]
|
||||
notes = [
|
||||
"The client came into the office at some point",
|
||||
"Who knows if this will happen",
|
||||
"Probably should check this event",
|
||||
"Maybe not happening",
|
||||
"Run away!",
|
||||
]
|
||||
|
||||
item_options = [
|
||||
{'name': 'Speakers', 'description': 'Some really really big speakers \n these are very loud', 'quantity': 2,
|
||||
'cost': 200.00},
|
||||
{'name': 'Projector',
|
||||
'description': 'Some kind of video thinamejig, probably with unnecessary processing for free',
|
||||
'quantity': 1, 'cost': 500.00},
|
||||
{'name': 'Lighting Desk', 'description': 'Cannot provide guarentee that it will work', 'quantity': 1,
|
||||
'cost': 200.52},
|
||||
{'name': 'Moving lights', 'description': 'Flashy lights, with the copper', 'quantity': 8, 'cost': 50.00},
|
||||
{'name': 'Microphones', 'description': 'Make loud noise \n you will want speakers with this', 'quantity': 5,
|
||||
'cost': 0.50},
|
||||
{'name': 'Sound Mixer Thing', 'description': 'Might be analogue, might be digital', 'quantity': 1,
|
||||
'cost': 100.00},
|
||||
{'name': 'Electricity', 'description': 'You need this', 'quantity': 1, 'cost': 200.00},
|
||||
{'name': 'Crew', 'description': 'Costs nothing, because reasons', 'quantity': 1, 'cost': 0.00},
|
||||
{'name': 'Loyalty Discount', 'description': 'Have some negative moneys', 'quantity': 1, 'cost': -50.00}]
|
||||
{
|
||||
"name": "Speakers",
|
||||
"description": "Some really really big speakers \n these are very loud",
|
||||
"quantity": 2,
|
||||
"cost": 200.00,
|
||||
},
|
||||
{
|
||||
"name": "Projector",
|
||||
"description": "Some kind of video thinamejig, probably with unnecessary processing for free",
|
||||
"quantity": 1,
|
||||
"cost": 500.00,
|
||||
},
|
||||
{
|
||||
"name": "Lighting Desk",
|
||||
"description": "Cannot provide guarentee that it will work",
|
||||
"quantity": 1,
|
||||
"cost": 200.52,
|
||||
},
|
||||
{"name": "Moving lights", "description": "Flashy lights, with the copper", "quantity": 8, "cost": 50.00},
|
||||
{
|
||||
"name": "Microphones",
|
||||
"description": "Make loud noise \n you will want speakers with this",
|
||||
"quantity": 5,
|
||||
"cost": 0.50,
|
||||
},
|
||||
{
|
||||
"name": "Sound Mixer Thing",
|
||||
"description": "Might be analogue, might be digital",
|
||||
"quantity": 1,
|
||||
"cost": 100.00,
|
||||
},
|
||||
{"name": "Electricity", "description": "You need this", "quantity": 1, "cost": 200.00},
|
||||
{"name": "Crew", "description": "Costs nothing, because reasons", "quantity": 1, "cost": 0.00},
|
||||
{"name": "Loyalty Discount", "description": "Have some negative moneys", "quantity": 1, "cost": -50.00},
|
||||
]
|
||||
|
||||
day_delta = -120 # start adding events from 4 months ago
|
||||
|
||||
@@ -222,9 +484,10 @@ class Command(BaseCommand):
|
||||
|
||||
# Could have any status, equally weighted
|
||||
new_event.status = random.choice(
|
||||
[models.Event.BOOKED, models.Event.CONFIRMED, models.Event.PROVISIONAL, models.Event.CANCELLED])
|
||||
[models.Event.BOOKED, models.Event.CONFIRMED, models.Event.PROVISIONAL, models.Event.CANCELLED]
|
||||
)
|
||||
|
||||
new_event.dry_hire = (random.randint(0, 7) == 0) # 1 in 7 are dry hire
|
||||
new_event.dry_hire = random.randint(0, 7) == 0 # 1 in 7 are dry hire
|
||||
|
||||
if random.randint(0, 1) > 0: # 1 in 2 have description
|
||||
new_event.description = random.choice(descriptions)
|
||||
@@ -253,39 +516,49 @@ class Command(BaseCommand):
|
||||
if new_event.status is models.Event.CANCELLED: # void cancelled events
|
||||
new_invoice.void = True
|
||||
elif random.randint(0, 2) > 1: # 1 in 3 have been paid
|
||||
models.Payment.objects.create(invoice=new_invoice, amount=new_invoice.balance,
|
||||
date=datetime.date.today(), method=random.choice(models.Payment.METHODS)[0])
|
||||
models.Payment.objects.create(
|
||||
invoice=new_invoice,
|
||||
amount=new_invoice.balance,
|
||||
date=datetime.date.today(),
|
||||
method=random.choice(models.Payment.METHODS)[0],
|
||||
)
|
||||
if i == 1 or random.randint(0, 5) > 0: # Event 1 and 1 in 5 have a RA
|
||||
models.RiskAssessment.objects.create(event=new_event, supervisor_consulted=bool(random.getrandbits(1)),
|
||||
nonstandard_equipment=bool(random.getrandbits(1)),
|
||||
nonstandard_use=bool(random.getrandbits(1)),
|
||||
contractors=bool(random.getrandbits(1)),
|
||||
other_companies=bool(random.getrandbits(1)),
|
||||
crew_fatigue=bool(random.getrandbits(1)),
|
||||
big_power=bool(random.getrandbits(1)),
|
||||
generators=bool(random.getrandbits(1)),
|
||||
other_companies_power=bool(random.getrandbits(1)),
|
||||
nonstandard_equipment_power=bool(random.getrandbits(1)),
|
||||
multiple_electrical_environments=bool(random.getrandbits(1)),
|
||||
noise_monitoring=bool(random.getrandbits(1)),
|
||||
known_venue=bool(random.getrandbits(1)),
|
||||
safe_loading=bool(random.getrandbits(1)),
|
||||
safe_storage=bool(random.getrandbits(1)),
|
||||
area_outside_of_control=bool(random.getrandbits(1)),
|
||||
barrier_required=bool(random.getrandbits(1)),
|
||||
nonstandard_emergency_procedure=bool(random.getrandbits(1)),
|
||||
special_structures=bool(random.getrandbits(1)),
|
||||
suspended_structures=bool(random.getrandbits(1)),
|
||||
parking_and_access=bool(random.getrandbits(1)),
|
||||
outside=bool(random.getrandbits(1)))
|
||||
models.RiskAssessment.objects.create(
|
||||
event=new_event,
|
||||
supervisor_consulted=bool(random.getrandbits(1)),
|
||||
nonstandard_equipment=bool(random.getrandbits(1)),
|
||||
nonstandard_use=bool(random.getrandbits(1)),
|
||||
contractors=bool(random.getrandbits(1)),
|
||||
other_companies=bool(random.getrandbits(1)),
|
||||
crew_fatigue=bool(random.getrandbits(1)),
|
||||
big_power=bool(random.getrandbits(1)),
|
||||
generators=bool(random.getrandbits(1)),
|
||||
other_companies_power=bool(random.getrandbits(1)),
|
||||
nonstandard_equipment_power=bool(random.getrandbits(1)),
|
||||
multiple_electrical_environments=bool(random.getrandbits(1)),
|
||||
noise_monitoring=bool(random.getrandbits(1)),
|
||||
known_venue=bool(random.getrandbits(1)),
|
||||
safe_loading=bool(random.getrandbits(1)),
|
||||
safe_storage=bool(random.getrandbits(1)),
|
||||
area_outside_of_control=bool(random.getrandbits(1)),
|
||||
barrier_required=bool(random.getrandbits(1)),
|
||||
nonstandard_emergency_procedure=bool(random.getrandbits(1)),
|
||||
special_structures=bool(random.getrandbits(1)),
|
||||
suspended_structures=bool(random.getrandbits(1)),
|
||||
parking_and_access=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
|
||||
models.EventChecklist.objects.create(event=new_event,
|
||||
safe_parking=bool(random.getrandbits(1)),
|
||||
safe_packing=bool(random.getrandbits(1)),
|
||||
exits=bool(random.getrandbits(1)),
|
||||
trip_hazard=bool(random.getrandbits(1)),
|
||||
warning_signs=bool(random.getrandbits(1)),
|
||||
ear_plugs=bool(random.getrandbits(1)),
|
||||
hs_location="Locked away safely",
|
||||
extinguishers_location="Somewhere, I forgot",
|
||||
date=timezone.now(), venue=random.choice(self.venues))
|
||||
models.EventChecklist.objects.create(
|
||||
event=new_event,
|
||||
safe_parking=bool(random.getrandbits(1)),
|
||||
safe_packing=bool(random.getrandbits(1)),
|
||||
exits=bool(random.getrandbits(1)),
|
||||
trip_hazard=bool(random.getrandbits(1)),
|
||||
warning_signs=bool(random.getrandbits(1)),
|
||||
ear_plugs=bool(random.getrandbits(1)),
|
||||
hs_location="Locked away safely",
|
||||
extinguishers_location="Somewhere, I forgot",
|
||||
date=timezone.now(),
|
||||
venue=random.choice(self.venues),
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ 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.management.base import BaseCommand
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.utils import timezone
|
||||
from django.urls import reverse
|
||||
@@ -13,17 +13,27 @@ from RIGS import models
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Sends email reminders as required. Triggered daily through heroku-scheduler in production.'
|
||||
help = "Sends email reminders as required. Triggered daily through sys cron in production."
|
||||
|
||||
def handle(self, *args, **options):
|
||||
events = models.Event.objects.current_events().select_related('riskassessment')
|
||||
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)))
|
||||
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'):
|
||||
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})
|
||||
"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(
|
||||
@@ -32,7 +42,11 @@ class Command(BaseCommand):
|
||||
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')
|
||||
css = finders.find("css/email.css")
|
||||
html = premailer.Premailer(
|
||||
get_template("email/ra_reminder.html").render(context),
|
||||
external_styles=css,
|
||||
allow_loading_external_files=True,
|
||||
).transform()
|
||||
msg.attach_alternative(html, "text/html")
|
||||
msg.send()
|
||||
|
||||
Reference in New Issue
Block a user