Skip to content

Commit

Permalink
Added history tracking to Member and Machine
Browse files Browse the repository at this point in the history
--- DEPLOYMENT NOTES ---
Should run `manage.py populate_history internal.member make_queue.machine`
  • Loading branch information
ddabble committed Mar 4, 2023
1 parent 5f2b7d9 commit 8e85f56
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 2 deletions.
2 changes: 1 addition & 1 deletion internal/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from .models import Member, Quote, Secret, SystemAccess


class MemberAdmin(DefaultAdminWidgetsMixin, admin.ModelAdmin):
class MemberAdmin(DefaultAdminWidgetsMixin, SimpleHistoryAdmin):
list_display = ('get_name', 'last_modified')
list_select_related = ('user',)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
# Generated by Django 4.1.3 on 2022-11-21 04:00

import datetime
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import internal.modelfields
import internal.validators
import phonenumber_field.modelfields
import re
import simple_history.models
import web.modelfields


class Migration(migrations.Migration):

dependencies = [
("groups", "0012_history_date_db_index"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("internal", "0023_rename_gmail_member_google_email"),
]

operations = [
migrations.CreateModel(
name="HistoricalMember",
fields=[
(
"id",
models.BigIntegerField(
auto_created=True, blank=True, db_index=True, verbose_name="ID"
),
),
(
"role",
web.modelfields.UnlimitedCharField(blank=True, verbose_name="role"),
),
(
"contact_email",
models.EmailField(
blank=True, max_length=254, verbose_name="contact email"
),
),
(
"google_email",
models.EmailField(
blank=True, max_length=254, verbose_name="Google email"
),
),
(
"MAKE_email",
models.EmailField(
blank=True,
max_length=254,
validators=[
internal.validators.WhitelistedEmailValidator(
valid_domains=["makentnu.no"]
)
],
verbose_name="MAKE email",
),
),
(
"phone_number",
phonenumber_field.modelfields.PhoneNumberField(
blank=True,
max_length=32,
region=None,
verbose_name="phone number",
),
),
(
"study_program",
web.modelfields.UnlimitedCharField(
blank=True, verbose_name="study program"
),
),
(
"ntnu_starting_semester",
internal.modelfields.SemesterField(
blank=True,
help_text="Must be in the format [V/H][year], e.g. “V17” or “H2017”.",
null=True,
verbose_name="starting semester at NTNU",
),
),
(
"date_joined",
models.DateField(
default=datetime.datetime.now, verbose_name="date joined"
),
),
(
"date_quit_or_retired",
models.DateField(
blank=True, null=True, verbose_name="date quit or retired"
),
),
(
"reason_quit",
models.TextField(blank=True, verbose_name="reason quit"),
),
("comment", models.TextField(blank=True, verbose_name="comment")),
("active", models.BooleanField(default=True, verbose_name="is active")),
(
"guidance_exemption",
models.BooleanField(
default=False, verbose_name="guidance exemption"
),
),
("quit", models.BooleanField(default=False, verbose_name="has quit")),
("retired", models.BooleanField(default=False, verbose_name="retired")),
(
"honorary",
models.BooleanField(default=False, verbose_name="honorary"),
),
(
"github_username",
web.modelfields.UnlimitedCharField(
blank=True, verbose_name="GitHub username"
),
),
(
"discord_username",
web.modelfields.UnlimitedCharField(
blank=True,
help_text="The username must include the hashtag and the four digits at the end.",
validators=[
django.core.validators.RegexValidator(
re.compile("^(.+)#([0-9]{4})$"),
"Enter a valid Discord username - including the hashtag and the four digits at the end.",
)
],
verbose_name="Discord username",
),
),
(
"minecraft_username",
web.modelfields.UnlimitedCharField(
blank=True, verbose_name="Minecraft username"
),
),
("history_id", models.AutoField(primary_key=True, serialize=False)),
("history_date", models.DateTimeField(db_index=True)),
("history_change_reason", models.CharField(max_length=100, null=True)),
(
"history_type",
models.CharField(
choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")],
max_length=1,
),
),
(
"history_user",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="+",
to=settings.AUTH_USER_MODEL,
),
),
(
"user",
models.ForeignKey(
blank=True,
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
to=settings.AUTH_USER_MODEL,
verbose_name="user",
),
),
],
options={
"verbose_name": "historical member",
"verbose_name_plural": "historical members",
"ordering": ("-history_date", "-history_id"),
"get_latest_by": ("history_date", "history_id"),
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.CreateModel(
name="HistoricalMember_committees",
fields=[
(
"id",
models.BigIntegerField(
auto_created=True, blank=True, db_index=True, verbose_name="ID"
),
),
("m2m_history_id", models.AutoField(primary_key=True, serialize=False)),
(
"committee",
models.ForeignKey(
blank=True,
db_constraint=False,
db_tablespace="",
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
to="groups.committee",
),
),
(
"history",
models.ForeignKey(
db_constraint=False,
on_delete=django.db.models.deletion.DO_NOTHING,
to="internal.historicalmember",
),
),
(
"member",
models.ForeignKey(
blank=True,
db_constraint=False,
db_tablespace="",
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
to="internal.member",
),
),
],
options={
"verbose_name": "HistoricalMember_committees",
},
),
]
2 changes: 2 additions & 0 deletions internal/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class Member(models.Model):
minecraft_username = UnlimitedCharField(blank=True, verbose_name=_("Minecraft username"))
last_modified = models.DateTimeField(auto_now=True, verbose_name=_("last modified"))

history = HistoricalRecords(m2m_fields=[committees], excluded_fields=['last_modified'])

class Meta:
permissions = (
('is_internal', "Is a member of MAKE NTNU"),
Expand Down
2 changes: 1 addition & 1 deletion make_queue/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def get_search_results(self, request, queryset, search_term):
return search_escaped_and_unescaped(super(), request, queryset, search_term)


class MachineAdmin(DefaultAdminWidgetsMixin, admin.ModelAdmin):
class MachineAdmin(DefaultAdminWidgetsMixin, SimpleHistoryAdmin):
list_display = ('name', 'machine_model', 'machine_type', 'get_location', 'internal', 'status', 'priority', 'last_modified')
list_filter = ('machine_type', 'machine_model', 'location', 'status')
search_fields = ('name', 'stream_name', 'machine_model', 'machine_type__name', 'location', 'location_url')
Expand Down
47 changes: 47 additions & 0 deletions make_queue/migrations/0033_historicalmachine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Generated by Django 4.0.6 on 2022-09-20 15:28

from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import re
import simple_history.models
import web.modelfields


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('make_queue', '0032_printer3dcourse_sla_course'),
]

operations = [
migrations.CreateModel(
name='HistoricalMachine',
fields=[
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('name', web.modelfields.UnlimitedCharField(db_index=True, verbose_name='name')),
('stream_name', models.CharField(blank=True, default='', help_text="Used for connecting to the machine's stream.", max_length=50, validators=[django.core.validators.RegexValidator(code='invalid_lowercase_slug', message='This can only consist of lowercase English letters, numbers, hyphens or underscores.', regex=re.compile('^[a-z0-9_-]+$'))], verbose_name='stream name')),
('machine_model', web.modelfields.UnlimitedCharField(verbose_name='machine model')),
('location', web.modelfields.UnlimitedCharField(verbose_name='location')),
('location_url', web.modelfields.URLTextField(verbose_name='location URL')),
('internal', models.BooleanField(default=False, help_text='If selected, the machine will only be visible to and reservable by MAKE members.', verbose_name='internal')),
('info_message', models.TextField(blank=True, help_text="Information that's useful to know before using the machine, e.g. the filament that the 3D printer uses, the needle that's currently inserted in the sewing machine, or just the machine's current state/“mood” (emojis are allowed 🤠).", verbose_name='info message')),
('notes', models.TextField(blank=True, help_text='This is only for internal use and is not displayed anywhere.', verbose_name='notes')),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField(db_index=True)),
('history_change_reason', models.CharField(max_length=100, null=True)),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
('machine_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='make_queue.machinetype', verbose_name='machine type')),
],
options={
'verbose_name': 'historical machine',
'verbose_name_plural': 'historical machines',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': ('history_date', 'history_id'),
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
]
1 change: 1 addition & 0 deletions make_queue/models/machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class Status(models.TextChoices):
last_modified = models.DateTimeField(auto_now=True, verbose_name=_("last modified"))

objects = MachineQuerySet.as_manager()
history = HistoricalRecords(excluded_fields=['status', 'info_message_date', 'priority', 'last_modified'])

def __str__(self):
return f"{self.name} - {self.machine_model}"
Expand Down

0 comments on commit 8e85f56

Please sign in to comment.