membershipworks: Use django-simple-history for Member, Flag, and MemberFlag

This commit is contained in:
Adam Goldsmith 2024-08-28 15:10:13 -04:00
parent ee61451759
commit b98804e514
6 changed files with 485 additions and 6 deletions

View File

@ -54,6 +54,7 @@ class Base(Configuration):
"django_db_views", "django_db_views",
"django_sendfile", "django_sendfile",
"django_bootstrap5", "django_bootstrap5",
"simple_history",
# "tasks.apps.TasksConfig", # "tasks.apps.TasksConfig",
"rentals.apps.RentalsConfig", "rentals.apps.RentalsConfig",
"membershipworks.apps.MembershipworksConfig", "membershipworks.apps.MembershipworksConfig",

View File

@ -10,6 +10,7 @@ from django_object_actions import (
) )
from django_q.models import Task from django_q.models import Task
from django_q.tasks import async_task from django_q.tasks import async_task
from simple_history.admin import SimpleHistoryAdmin
from .models import ( from .models import (
Event, Event,
@ -28,7 +29,7 @@ from .tasks.scrape import (
from .tasks.ucsAccounts import sync_accounts from .tasks.ucsAccounts import sync_accounts
class ReadOnlyAdmin(admin.ModelAdmin): class ReadOnlyAdminMixin:
def has_add_permission(self, request, obj=None): def has_add_permission(self, request, obj=None):
return False return False
@ -39,7 +40,9 @@ class ReadOnlyAdmin(admin.ModelAdmin):
return False return False
class BaseMembershipWorksAdmin(DjangoObjectActions, ReadOnlyAdmin): class BaseMembershipWorksAdmin(
DjangoObjectActions, ReadOnlyAdminMixin, SimpleHistoryAdmin
):
changelist_actions = ("refresh_membershipworks_data", "sync_ucs_accounts") changelist_actions = ("refresh_membershipworks_data", "sync_ucs_accounts")
# internal method from DjangoObjectActions # internal method from DjangoObjectActions

View File

@ -0,0 +1,453 @@
# Generated by Django 5.1 on 2024-08-28 19:20
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
import simple_history.models
class Migration(migrations.Migration):
dependencies = [
("membershipworks", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="HistoricalFlag",
fields=[
("id", models.CharField(db_index=True, max_length=24)),
("name", models.TextField(blank=True, null=True)),
("type", models.CharField(max_length=6)),
("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,
),
),
],
options={
"verbose_name": "historical flag",
"verbose_name_plural": "historical flags",
"ordering": ("-history_date", "-history_id"),
"get_latest_by": ("history_date", "history_id"),
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.CreateModel(
name="HistoricalMember",
fields=[
("uid", models.CharField(db_index=True, max_length=24)),
(
"year_of_birth",
models.TextField(blank=True, db_column="Year of Birth", null=True),
),
(
"account_name",
models.TextField(blank=True, db_column="Account Name", null=True),
),
(
"first_name",
models.TextField(blank=True, db_column="First Name", null=True),
),
(
"last_name",
models.TextField(blank=True, db_column="Last Name", null=True),
),
("phone", models.TextField(blank=True, db_column="Phone", null=True)),
("email", models.TextField(blank=True, db_column="Email", null=True)),
(
"volunteer_email",
models.TextField(
blank=True, db_column="Volunteer Email", null=True
),
),
(
"address_street",
models.TextField(
blank=True, db_column="Address (Street)", null=True
),
),
(
"address_city",
models.TextField(blank=True, db_column="Address (City)", null=True),
),
(
"address_state_province",
models.TextField(
blank=True, db_column="Address (State/Province)", null=True
),
),
(
"address_postal_code",
models.TextField(
blank=True, db_column="Address (Postal Code)", null=True
),
),
(
"address_country",
models.TextField(
blank=True, db_column="Address (Country)", null=True
),
),
(
"profile_description",
models.TextField(
blank=True, db_column="Profile description", null=True
),
),
(
"website",
models.TextField(blank=True, db_column="Website", null=True),
),
("fax", models.TextField(blank=True, db_column="Fax", null=True)),
(
"contact_person",
models.TextField(blank=True, db_column="Contact Person", null=True),
),
(
"password",
models.TextField(blank=True, db_column="Password", null=True),
),
(
"position_relation",
models.TextField(
blank=True, db_column="Position/relation", null=True
),
),
(
"parent_account_id",
models.TextField(
blank=True, db_column="Parent Account ID", null=True
),
),
(
"closet_storage",
models.TextField(
blank=True, db_column="Closet Storage #", null=True
),
),
(
"storage_shelf",
models.TextField(
blank=True, db_column="Storage Shelf #", null=True
),
),
(
"personal_studio_space",
models.TextField(
blank=True, db_column="Personal Studio Space #", null=True
),
),
(
"access_permitted_shops_during_extended_hours",
models.BooleanField(
db_column="Access Permitted Shops During Extended Hours?"
),
),
(
"access_front_door_and_studio_space_during_extended_hours",
models.BooleanField(
db_column="Access Front Door and Studio Space During Extended Hours?"
),
),
(
"access_wood_shop",
models.BooleanField(db_column="Access Wood Shop?"),
),
(
"access_metal_shop",
models.BooleanField(db_column="Access Metal Shop?"),
),
(
"access_storage_closet",
models.BooleanField(db_column="Access Storage Closet?"),
),
(
"access_studio_space",
models.BooleanField(db_column="Access Studio Space?"),
),
(
"access_front_door",
models.BooleanField(db_column="Access Front Door?"),
),
(
"access_card_number",
models.TextField(
blank=True, db_column="Access Card Number", null=True
),
),
(
"access_card_facility_code",
models.TextField(
blank=True, db_column="Access Card Facility Code", null=True
),
),
(
"auto_billing_id",
models.TextField(
blank=True, db_column="Auto Billing ID", null=True
),
),
(
"billing_method",
models.TextField(blank=True, db_column="Billing Method", null=True),
),
(
"renewal_date",
models.DateField(blank=True, db_column="Renewal Date", null=True),
),
(
"join_date",
models.DateField(blank=True, db_column="Join Date", null=True),
),
(
"admin_note",
models.TextField(blank=True, db_column="Admin note", null=True),
),
(
"profile_gallery_image_url",
models.TextField(
blank=True, db_column="Profile gallery image URL", null=True
),
),
(
"business_card_image_url",
models.TextField(
blank=True, db_column="Business card image URL", null=True
),
),
(
"instagram",
models.TextField(blank=True, db_column="Instagram", null=True),
),
(
"pinterest",
models.TextField(blank=True, db_column="Pinterest", null=True),
),
(
"youtube",
models.TextField(blank=True, db_column="Youtube", null=True),
),
("yelp", models.TextField(blank=True, db_column="Yelp", null=True)),
(
"google",
models.TextField(blank=True, db_column="Google+", null=True),
),
("bbb", models.TextField(blank=True, db_column="BBB", null=True)),
(
"twitter",
models.TextField(blank=True, db_column="Twitter", null=True),
),
(
"facebook",
models.TextField(blank=True, db_column="Facebook", null=True),
),
(
"linked_in",
models.TextField(blank=True, db_column="LinkedIn", null=True),
),
(
"do_not_show_street_address_in_profile",
models.TextField(
blank=True,
db_column="Do not show street address in profile",
null=True,
),
),
(
"do_not_list_in_directory",
models.TextField(
blank=True, db_column="Do not list in directory", null=True
),
),
(
"how_did_you_hear",
models.TextField(blank=True, db_column="HowDidYouHear", null=True),
),
(
"authorize_charge",
models.TextField(
blank=True, db_column="authorizeCharge", null=True
),
),
(
"policy_agreement",
models.TextField(
blank=True, db_column="policyAgreement", null=True
),
),
(
"waiver_form_signed_and_on_file_date",
models.DateField(
blank=True,
db_column="Waiver form signed and on file date.",
null=True,
),
),
(
"membership_agreement_signed_and_on_file_date",
models.DateField(
blank=True,
db_column="Membership Agreement signed and on file date.",
null=True,
),
),
(
"ip_address",
models.TextField(blank=True, db_column="IP Address", null=True),
),
(
"audit_date",
models.DateField(blank=True, db_column="Audit Date", null=True),
),
(
"agreement_version",
models.TextField(
blank=True, db_column="Agreement Version", null=True
),
),
(
"paperwork_status",
models.TextField(
blank=True, db_column="Paperwork status", null=True
),
),
(
"membership_agreement_dated",
models.BooleanField(db_column="Membership agreement dated"),
),
(
"membership_agreement_acknowledgement_page_filled_out",
models.BooleanField(
db_column="Membership Agreement Acknowledgement Page Filled Out"
),
),
(
"membership_agreement_signed",
models.BooleanField(db_column="Membership Agreement Signed"),
),
(
"liability_form_filled_out",
models.BooleanField(db_column="Liability Form Filled Out"),
),
("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,
),
),
],
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="HistoricalMemberFlag",
fields=[
(
"id",
models.BigIntegerField(
auto_created=True, blank=True, db_index=True, verbose_name="ID"
),
),
("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,
),
),
(
"flag",
simple_history.models.HistoricForeignKey(
blank=True,
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
to="membershipworks.flag",
),
),
(
"history_user",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="+",
to=settings.AUTH_USER_MODEL,
),
),
(
"member",
simple_history.models.HistoricForeignKey(
blank=True,
db_column="uid",
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
to="membershipworks.member",
),
),
],
options={
"verbose_name": "historical member flag",
"verbose_name_plural": "historical member flags",
"ordering": ("-history_date", "-history_id"),
"get_latest_by": ("history_date", "history_id"),
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.AlterField(
model_name="memberflag",
name="flag",
field=simple_history.models.HistoricForeignKey(
on_delete=django.db.models.deletion.PROTECT, to="membershipworks.flag"
),
),
migrations.AlterField(
model_name="memberflag",
name="member",
field=simple_history.models.HistoricForeignKey(
db_column="uid",
db_constraint=False,
on_delete=django.db.models.deletion.PROTECT,
to="membershipworks.member",
),
),
]

View File

@ -28,6 +28,7 @@ from django.utils import timezone
import nh3 import nh3
from django_db_views.db_view import DBView from django_db_views.db_view import DBView
from django_stubs_ext import WithAnnotations from django_stubs_ext import WithAnnotations
from simple_history.models import HistoricalRecords, HistoricForeignKey
from reservations.models import Reservation from reservations.models import Reservation
@ -89,6 +90,8 @@ class Flag(BaseModel):
name = models.TextField(null=True, blank=True) name = models.TextField(null=True, blank=True)
type = models.CharField(max_length=6) type = models.CharField(max_length=6)
history = HistoricalRecords()
class Meta: class Meta:
db_table = "flag" db_table = "flag"
ordering = ("name",) ordering = ("name",)
@ -121,7 +124,6 @@ class MemberQuerySet(models.QuerySet):
) )
# TODO: is this still a temporal table?
class Member(BaseModel): class Member(BaseModel):
uid = models.CharField(max_length=24, primary_key=True) uid = models.CharField(max_length=24, primary_key=True)
year_of_birth = models.TextField(db_column="Year of Birth", null=True, blank=True) year_of_birth = models.TextField(db_column="Year of Birth", null=True, blank=True)
@ -248,6 +250,8 @@ class Member(BaseModel):
) )
flags = models.ManyToManyField(Flag, through="MemberFlag", related_name="members") flags = models.ManyToManyField(Flag, through="MemberFlag", related_name="members")
history = HistoricalRecords()
_api_names_override = { _api_names_override = {
"uid": "Account ID", "uid": "Account ID",
"how_did_you_hear": "Please tell us how you heard about the Claremont MakerSpace and what tools or shops you are most excited to start using:", "how_did_you_hear": "Please tell us how you heard about the Claremont MakerSpace and what tools or shops you are most excited to start using:",
@ -300,10 +304,12 @@ class Member(BaseModel):
class MemberFlag(BaseModel): class MemberFlag(BaseModel):
member = models.ForeignKey( member = HistoricForeignKey(
Member, on_delete=models.PROTECT, db_column="uid", db_constraint=False Member, on_delete=models.PROTECT, db_column="uid", db_constraint=False
) )
flag = models.ForeignKey(Flag, on_delete=models.PROTECT) flag = HistoricForeignKey(Flag, on_delete=models.PROTECT)
history = HistoricalRecords()
class Meta: class Meta:
db_table = "memberflag" db_table = "memberflag"

View File

@ -5,7 +5,7 @@
groups = ["default", "debug", "dev", "lint", "server", "typing"] groups = ["default", "debug", "dev", "lint", "server", "typing"]
strategy = ["inherit_metadata"] strategy = ["inherit_metadata"]
lock_version = "4.5.0" lock_version = "4.5.0"
content_hash = "sha256:0c560e1f2a81810e95ba8993538337bfc8b39576b6923280cfbfad91ad5b58f2" content_hash = "sha256:f908c755f35f0769821be54914ad53a3de6e9a0c654baf877045c161c7dacfc7"
[[metadata.targets]] [[metadata.targets]]
requires_python = "==3.11.*" requires_python = "==3.11.*"
@ -650,6 +650,21 @@ files = [
{file = "django_sendfile2-0.7.1-py3-none-any.whl", hash = "sha256:81971df1db77f688c4834b8540930902d0d929c64cf374bd604b81f5ac52c04a"}, {file = "django_sendfile2-0.7.1-py3-none-any.whl", hash = "sha256:81971df1db77f688c4834b8540930902d0d929c64cf374bd604b81f5ac52c04a"},
] ]
[[package]]
name = "django-simple-history"
version = "3.7.0"
requires_python = ">=3.8"
summary = "Store model history and view/revert changes from admin site."
groups = ["default"]
marker = "python_version == \"3.11\""
dependencies = [
"django>=4.2",
]
files = [
{file = "django_simple_history-3.7.0-py3-none-any.whl", hash = "sha256:282cb2c4aa63f51547f17da7f2130abaa81ba01694676d19b88d52c94a57a52c"},
{file = "django_simple_history-3.7.0.tar.gz", hash = "sha256:ac3b7ca8b0d33f7ea6be8fe7fc98cf43415efa500ff5dfe736fbd1ebc0cf39f9"},
]
[[package]] [[package]]
name = "django-stubs" name = "django-stubs"
version = "5.0.2" version = "5.0.2"

View File

@ -43,6 +43,7 @@ dependencies = [
"google-auth-oauthlib~=1.2", "google-auth-oauthlib~=1.2",
"django-model-utils~=4.5", "django-model-utils~=4.5",
"psycopg[binary,pool]~=3.2", "psycopg[binary,pool]~=3.2",
"django-simple-history~=3.7",
] ]
requires-python = ">=3.11" requires-python = ">=3.11"