Compare commits

...

5 Commits

Author SHA1 Message Date
57c730a2cf Update tailwind to v3, various wangling 2023-05-16 17:48:32 +01:00
c55577e04e More templating 2021-02-14 13:18:31 +00:00
56a8b7afaa Init reversion 2021-02-14 12:52:36 +00:00
82ebef8465 Make migrations, template stuff 2021-02-14 12:48:12 +00:00
5874c5299b More initial sketching 2021-02-14 11:49:56 +00:00
33 changed files with 7146 additions and 183621 deletions

View File

@@ -7,8 +7,9 @@ name = "pypi"
Django = "*"
django-registration-redux = "*"
django-tailwind = "*"
django-reversion = "*"
[dev-packages]
[requires]
python_version = "3.8"
python_version = "3.10"

59
Pipfile.lock generated
View File

@@ -1,11 +1,11 @@
{
"_meta": {
"hash": {
"sha256": "7c687a6bc97aa06b7d51dcb4f6b6115d9dec1ca067ebe7189c94a98dfe8c0c7b"
"sha256": "68bdb29cd524e36b810214289b254227c5f1ca9d18c311b7e49f9d4cd1f3e7e3"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.8"
"python_version": "3.10"
},
"sources": [
{
@@ -18,48 +18,59 @@
"default": {
"asgiref": {
"hashes": [
"sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17",
"sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0"
"sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac",
"sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"
],
"version": "==3.3.1"
"markers": "python_version >= '3.7'",
"version": "==3.6.0"
},
"django": {
"hashes": [
"sha256:169e2e7b4839a7910b393eec127fd7cbae62e80fa55f89c6510426abf673fe5f",
"sha256:c6c0462b8b361f8691171af1fb87eceb4442da28477e12200c40420176206ba7"
"sha256:066b6debb5ac335458d2a713ed995570536c8b59a580005acb0732378d5eb1ee",
"sha256:7efa6b1f781a6119a10ac94b4794ded90db8accbe7802281cd26f8664ffed59c"
],
"index": "pypi",
"version": "==3.1.6"
"version": "==4.2.1"
},
"django-browser-reload": {
"hashes": [
"sha256:054ef5ce8b80f46e0beda1a3c847becdff51272cad6f2bda607e201a7afe7425",
"sha256:6db7e6c5807fff87e421b53926c50cfa3de0115e438f3c2de6103d3069557adb"
],
"markers": "python_version >= '3.7'",
"version": "==1.8.0"
},
"django-registration-redux": {
"hashes": [
"sha256:e3d123354a1b8cbfa005d60f1ebb89ae8541f3eaffd6174d9f2aff529b57e430",
"sha256:e94b8a945e1cbfa9ec6c32b549597270405328d4e26651985d287d0211120691"
"sha256:2213bbe8732be72724034f4146f0255a7bd666eb5a5e1b2d8d8aa633fe8af894",
"sha256:56fbc7b01a7f0f48812fe4d4e0729d2dac916e16f8aaed36b3f10129f2df9d0f"
],
"index": "pypi",
"version": "==2.9"
"version": "==2.12"
},
"django-reversion": {
"hashes": [
"sha256:a591cbce8621b5d036a37617554668b5ef2eb9777682e3af20b6401ee87cfbc5",
"sha256:c12bab452d31dd3c244456cf1df383acf14ba147cf99404c5e44412596de42fc"
],
"index": "pypi",
"version": "==5.0.4"
},
"django-tailwind": {
"hashes": [
"sha256:2c1bf6c91fc54c844b85e8b379c211c971a2827c3d5b84f7e0464485ef9b8adf",
"sha256:d3aadba6aa046ead22efc68584b9ddccd0f25ccfbad433bb4727bee684a02177"
"sha256:c089b05b47d0e9d19567a81a74949fde16cbbfd8e4264c1f8eb4216a644065b2",
"sha256:dcc3b29463fff48a0e8ccd15c87fd467f0e746a7987bde0fdde6253dbb7861be"
],
"index": "pypi",
"version": "==1.1.1"
},
"pytz": {
"hashes": [
"sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
"sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
],
"version": "==2021.1"
"version": "==3.5.0"
},
"sqlparse": {
"hashes": [
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
"sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3",
"sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"
],
"version": "==0.4.1"
"markers": "python_version >= '3.5'",
"version": "==0.4.4"
}
},
"develop": {}

View File

@@ -11,6 +11,8 @@ https://docs.djangoproject.com/en/3.1/ref/settings/
"""
from pathlib import Path
from django.forms.renderers import TemplatesSetting
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
@@ -35,6 +37,7 @@ if DEBUG:
INSTALLED_APPS = [
# Django
'django.forms',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
@@ -44,6 +47,7 @@ INSTALLED_APPS = [
# 3rd Party
'registration',
'tailwind',
'reversion',
# 1st Party
'theme',
'users',
@@ -53,6 +57,7 @@ INSTALLED_APPS = [
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'reversion.middleware.RevisionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
@@ -65,7 +70,7 @@ ROOT_URLCONF = 'core.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'DIRS': ['templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
@@ -131,3 +136,8 @@ USE_TZ = True
STATIC_URL = '/static/'
TAILWIND_APP_NAME = 'theme'
class CustomFormRenderer(TemplatesSetting):
form_template_name = "custom_form.html"
FORM_RENDERER = "core.settings.CustomFormRenderer"

View File

@@ -2,7 +2,10 @@ from django.contrib import admin
from django.urls import path
from django.conf.urls import include
from . import views
urlpatterns = [
path('', views.Index.as_view(), name='index'),
path('user/', include('users.urls')),
path('vehicles/', include('vehicles.urls')),
path('admin/', admin.site.urls),

5
core/views.py Normal file
View File

@@ -0,0 +1,5 @@
from django.views import generic
class Index(generic.TemplateView):
template_name = 'index.html'

0
maintenance/__init__.py Normal file
View File

3
maintenance/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
maintenance/apps.py Normal file
View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class MaintenanceConfig(AppConfig):
name = 'maintenance'

View File

3
maintenance/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
maintenance/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
maintenance/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

10
templates/index.html Normal file
View File

@@ -0,0 +1,10 @@
{% extends 'base.html' %}
{% block content %}
<h1 class="text-4xl center py-2 font-serif font-black">SUPERINTENDANT</h1>
<h2 class="text-3xl center py-2">Fleet Manager</h2>
<h3 class="text-2xl center italic py-2 pb-8 text-blue-400">Fljōtr - Swift (Old Norse)</h3>
<h2 class="text-2xl underline text-blue-100 py-2"><a href="{% url 'vehicle_list' %}"><svg style="width: 24px; height: 24px;" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>Vehicle List</a></h2>
{% endblock %}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

6475
theme/static_src/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -16,15 +16,19 @@
"author": "",
"license": "MIT",
"devDependencies": {
"autoprefixer": "^10.0.2",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.4.4",
"@tailwindcss/typography": "^0.5.9",
"autoprefixer": "^10.4.14",
"clean-css-cli": "^4.3.0",
"cross-env": "^7.0.3",
"node-sass": "^5.0.0",
"postcss": "^8.1.9",
"postcss": "^8.4.23",
"postcss-cli": "^8.3.0",
"postcss-scss": "^3.0.4",
"rimraf": "^3.0.2",
"tailwindcss": "^2.0.1",
"tailwindcss": "^3.3.2",
"watch": "^1.0.2"
}
}

View File

@@ -1,3 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
input {
color: black;
}

View File

@@ -2,14 +2,10 @@
// If you need the full config, get it from here:
// https://unpkg.com/browse/tailwindcss@latest/stubs/defaultConfig.stub.js
module.exports = {
purge: [
// Templates within theme app (e.g. base.html)
content: [
'../templates/**/*.html',
// Templates in other apps. Uncomment the following line if it matches
// your project structure or change it to match.
'../../templates/**/*.html',
],
darkMode: 'media',
theme: {
extend: {},
},

View File

@@ -9,13 +9,13 @@
<title>Fleet Management</title>
<meta name="description" content="">
<meta name="keywords" content="">
<meta name="author" content="">
<meta name="author" content="FreneticScribbler">
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
</head>
<body class="bg-grey-lightest font-serif leading-normal tracking-normal">
<div class="container mx-auto">
<body class="bg-gray-900 font-sans leading-normal tracking-normal">
<div class="container mx-auto text-white py-4 text-center">
{% block content %}
{% endblock %}
</div>

View File

@@ -0,0 +1,3 @@
{% for field in form %}
{{ field.label_tag }} {{ field }} <br/>
{% endfor %}

View File

@@ -0,0 +1,39 @@
# Generated by Django 3.1.6 on 2021-02-14 12:44
import django.contrib.auth.models
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Licence',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('licence_number', models.CharField(max_length=100)),
('date_obtained', models.DateField()),
],
),
migrations.CreateModel(
name='Driver',
fields=[
('customuser_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='users.customuser')),
('licence', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.licence')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
bases=('users.customuser',),
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 3.1.6 on 2021-02-14 13:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('vehicles', '0003_auto_20210214_1315'),
('users', '0002_driver_licence'),
]
operations = [
migrations.AddField(
model_name='driver',
name='insurance',
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='insurance', to='vehicles.insurancepolicy'),
preserve_default=False,
),
]

View File

@@ -1,6 +1,18 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
# Create your models here.
class CustomUser(AbstractUser):
pass
class Licence(models.Model):
licence_number = models.CharField(max_length=100)
date_obtained = models.DateField()
# entitlements
class Driver(CustomUser):
licence = models.ForeignKey('Licence', on_delete=models.CASCADE)
insurance = models.ForeignKey('vehicles.InsurancePolicy', related_name='insurance', on_delete=models.CASCADE)

View File

@@ -1,3 +1,8 @@
from django.contrib import admin
# Register your models here.
from vehicles.models import Make, Model, Status, Vehicle
admin.site.register(Make)
admin.site.register(Model)
admin.site.register(Status)
admin.site.register(Vehicle)

View File

@@ -0,0 +1,47 @@
# Generated by Django 3.1.6 on 2021-02-14 12:44
import datetime
from django.db import migrations, models
import django.db.models.deletion
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('vehicles', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='vehicle',
name='engine_number',
),
migrations.AddField(
model_name='vehicle',
name='date_purchased',
field=models.DateField(default=datetime.datetime(2021, 2, 14, 12, 44, 16, 378064, tzinfo=utc)),
preserve_default=False,
),
migrations.AddField(
model_name='vehicle',
name='first_registered',
field=models.DateField(default=datetime.datetime(2021, 2, 14, 12, 44, 22, 562249, tzinfo=utc)),
preserve_default=False,
),
migrations.AddField(
model_name='vehicle',
name='yom',
field=models.DateField(default=datetime.datetime(2021, 2, 14, 12, 44, 32, 354386, tzinfo=utc)),
preserve_default=False,
),
migrations.CreateModel(
name='ICEVehicle',
fields=[
('vehicle_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='vehicles.vehicle')),
('engine_number', models.CharField(max_length=50)),
('capacity', models.IntegerField()),
],
bases=('vehicles.vehicle',),
),
]

View File

@@ -0,0 +1,50 @@
# Generated by Django 3.1.6 on 2021-02-14 13:15
import datetime
from django.db import migrations, models
import django.db.models.deletion
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('vehicles', '0002_auto_20210214_1244'),
]
operations = [
migrations.CreateModel(
name='InsurancePolicy',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('expires', models.DateField()),
('company', models.CharField(max_length=20)),
],
),
migrations.AddField(
model_name='vehicle',
name='mot_expires',
field=models.DateField(default=datetime.datetime(2021, 2, 14, 13, 15, 32, 868514, tzinfo=utc)),
preserve_default=False,
),
migrations.AddField(
model_name='vehicle',
name='tax_expires',
field=models.DateField(default=datetime.datetime(2021, 2, 14, 13, 15, 37, 180671, tzinfo=utc)),
preserve_default=False,
),
migrations.CreateModel(
name='MotorcycleInsurancePolicy',
fields=[
('insurancepolicy_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='vehicles.insurancepolicy')),
('ride_other_bikes', models.BooleanField(default=False)),
('pillion', models.BooleanField(default=False)),
],
bases=('vehicles.insurancepolicy',),
),
migrations.AddField(
model_name='insurancepolicy',
name='vehicle',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='vehicles.vehicle'),
),
]

View File

@@ -1,26 +1,65 @@
from django.db import models
from reversion import revisions as reversion
class Status(models.Model):
name = models.CharField(max_length=80)
def __str__(self):
return self.name
class Make(models.Model):
name = models.CharField(max_length=80)
def __str__(self):
return self.name
class Model(models.Model): # Cause that won't be a confusing name at all
name = models.CharField(max_length=80)
make = models.ForeignKey('Make', on_delete=models.CASCADE)
def __str__(self):
return "{} {}".format(self.make.name, self.name)
@reversion.register
class Vehicle(models.Model):
name = models.CharField(max_length=50)
# color
yom = models.DateField(verbose_name="Year of Manufacture")
first_registered = models.DateField()
date_purchased = models.DateField()
reg_number = models.CharField(max_length=7)
vin_number = models.CharField(max_length=50)
chassis_number = models.CharField(max_length=50)
engine_number = models.CharField(max_length=50)
status = models.ForeignKey('Status', on_delete=models.CASCADE)
owner = models.ForeignKey('users.CustomUser', blank=True, null=True, on_delete=models.CASCADE)
model = models.ForeignKey('Model', on_delete=models.CASCADE)
tax_expires = models.DateField()
mot_expires = models.DateField()
def __str__(self):
return self.name
class ICEVehicle(Vehicle):
engine_number = models.CharField(max_length=50)
capacity = models.IntegerField()
class InsurancePolicy(models.Model):
vehicle = models.OneToOneField('Vehicle', on_delete=models.CASCADE) # Not always one to one in practice
expires = models.DateField()
company = models.CharField(max_length=20)
class MotorcycleInsurancePolicy(InsurancePolicy):
ride_other_bikes = models.BooleanField(default=False)
pillion = models.BooleanField(default=False)

View File

@@ -0,0 +1,20 @@
{% extends 'base.html' %}
{% block content %}
<h1 class="text-2xl">Vehicle: <span class="font-bold">{{ object }}</span></h1>
<div class="bg-yellow-400 text-black font-black rounded text-xl p-2 w-1/12 m-2 center">{{ object.reg_number|slice:"4:" }} {{ object.reg_number|slice:":3" }}</div>
<div class="flex flex-nowrap">
<div class="border w-1/3 bg-green-800">
<h4 class="font-bold">Tax:</h4>
Valid until {{ object.tax_expires }}
</div>
<div class="border w-1/3 bg-green-800">
<h4 class="font-bold">MOT:</h4>
Valid until {{ object.mot_expires }}
</div>
<div class="border w-1/3 bg-green-800">
<h4 class="font-bold">Insurance:</h4>
Valid until {{ object.insurance.expires }}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block content %}
<form action="{% url 'vehicle_create' %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
{% endblock %}

View File

@@ -1,5 +1,37 @@
{% extends 'base.html' %}
{% block content %}
Test!
<h1 class="text-2xl py-3 font-bold">All Vehicles</h1>
<table class="table-auto border w-full">
<thead>
<th class="p-2">Name</th>
<th class="p-2">Make & Model</th>
<th class="p-2">Registration</th>
<th class="p-2">Owner</th>
<th class="p-2">Status</th>
<th class="p-2">VIN Number</th>
<th class="p-2">Engine Number</th>
<th class="p-2">Chassis Number</th>
<th class="p-2">Links</th>
</thead>
<tbody>
{% for object in object_list %}
<tr>
<th scope="row" class="p-2">{{ object.name }}</th>
<td class="p-2">{{ object.model }}</td>
<td class="bg-yellow-400 text-black font-black rounded text-xl px-2">{{ object.reg_number|slice:"4:" }} {{ object.reg_number|slice:":3" }}</td>
<td class="p-2">{{ object.owner|default:"God" }}</td>
<td class="p-2">{{ object.status }}</td>
<td class="p-2">{{ object.vin_number|default:"Unknown" }}</td>
<td class="p-2">{{ object.engine_number|default:"Unknown" }}</td>
<td class="p-2">{{ object.chassis_number|default:"Unknown" }}</td>
<td class="p-2"><a href="{% url 'vehicle_detail' object.pk %}" class="underline text-blue-100">Detail</a></td>
</tr>
{% empty %}
<tr class="bg-yellow-400">
<td span="10">Nothing found!</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@@ -1,7 +1,9 @@
from django.urls import path
from .views import VehicleList
from . import views
urlpatterns = [
path('list/', VehicleList.as_view(), name='vehicle_list')
path('list/', views.VehicleList.as_view(), name='vehicle_list'),
path('<int:pk>/', views.VehicleDetail.as_view(), name='vehicle_detail'),
path('create/', views.VehicleCreate.as_view(), name='vehicle_create'),
]

View File

@@ -1,9 +1,20 @@
from django.shortcuts import render
from django.views.generic.list import ListView
from django.views import generic
from .models import Vehicle
class VehicleList(ListView):
class VehicleList(generic.ListView):
template_name = 'vehicle_list.html'
model = Vehicle
class VehicleDetail(generic.DetailView):
template_name = 'vehicle_detail.html'
model = Vehicle
class VehicleCreate(generic.CreateView):
template_name = 'vehicle_form.html'
model = Vehicle
fields = "__all__"