diff --git a/gms/gms/settings.py b/gms/gms/settings.py index 606e6a2f0c791429ff761d1eba6aa29625289a0f..4c34c04ea0f446a64e487601c9e15a40f1b2123e 100644 --- a/gms/gms/settings.py +++ b/gms/gms/settings.py @@ -21,6 +21,7 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.staticfiles', 'memberdb', + 'import_members', ) MIDDLEWARE = [ @@ -54,6 +55,8 @@ USE_TZ = True LOGIN_URL = 'memberdb:login' LOGIN_REDIRECT_URL = 'memberdb:index' +DATABASE_ROUTERS = ['import_members.db.MemberDbRouter'] + # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.7/howto/static-files/ diff --git a/gms/gms/settings_local.example.py b/gms/gms/settings_local.example.py index 5d5edde7367035293fd7186f5bb5b84901d8c47f..f8984ca920cd8bfd05cc5fb8c02477bfb1b333a9 100644 --- a/gms/gms/settings_local.example.py +++ b/gms/gms/settings_local.example.py @@ -14,13 +14,21 @@ ADMINS = ( ### Database connection options ### DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. + 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql', 'mysql', 'sqlite3' or 'oracle'. # this should end up in uccportal/.db/members.db 'NAME': os.path.join(ROOT_DIR, '.db', 'members.db'), # Or path to database file if using sqlite3. 'USER': '', # Not used with sqlite3. 'PASSWORD': '', # Not used with sqlite3. 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 'PORT': '', # Set to empty string for default. Not used with sqlite3. + }, + 'memberdb_old': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'uccmemberdb_2018', + 'USER': 'uccmemberdb', + 'PASSWORD': 'something-secret-here', + 'HOST': 'mussel.ucc.gu.uwa.edu.au', + 'PORT': '', } } diff --git a/gms/import_members/actions.py b/gms/import_members/actions.py new file mode 100644 index 0000000000000000000000000000000000000000..26639bff9fbb073d2c90e79c43a20f3ddc672b38 --- /dev/null +++ b/gms/import_members/actions.py @@ -0,0 +1,42 @@ +from django.contrib import messages +from django.core.exceptions import ValidationError +from django.db import IntegrityError + +from .models import OldMember +from memberdb.models import Member + +def import_old_member(modeladmin, request, queryset): + """ + admin action: Import the selected OldMember records into the new MemberDB format + don't overwrite records if they already exist (matching by username) + """ + num_success = 0 + total = queryset.count() + for om in queryset: + try: + # create a new Member object + nm = Member(username=om.username) + + # fudge the data as much as we can, people will have to renew memberships and check this anyway + nm.first_name, nm.last_name = om.real_name.split(" ", 1) + nm.display_name = om.real_name + nm.is_guild = om.guild_member + nm.phone_number = om.phone_number + nm.id_number = om.student_no + nm.email_address = om.email_address + if (om.membership_type == 1 or om.membership_type == 2): # O'day special or student + nm.is_student = True + else: + nm.is_student = False + + if (nm.username == '' or nm.username is None): + raise ValidationError("username cannot be blank") + nm.save() + num_success += 1 + except BaseException as e: + modeladmin.message_user(request, 'Could not import record (%s): %s' % (om, e), level=messages.ERROR) + + if (num_success > 0): + modeladmin.message_user(request, 'Successfully imported %d of %d records.' % (num_success, total)) + +import_old_member.short_description = "Import selected records to new MemberDB" \ No newline at end of file diff --git a/gms/import_members/admin.py b/gms/import_members/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..32ca9c7bd743ca1d9a5ac0941284fd76157e9f99 --- /dev/null +++ b/gms/import_members/admin.py @@ -0,0 +1,41 @@ +from django.db.models import Q +from gms import admin +from memberdb.actions import download_as_csv + +from .models import OldMember +from .actions import import_old_member + +class UsernameNullListFilter(admin.SimpleListFilter): + """ + see https://docs.djangoproject.com/en/2.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter + """ + title = 'membership type' + parameter_name = 'type' + + def lookups(self, request, model_admin): + return ( + ('fresh', 'first time member (blank username)'), + ('stale', 'recurring member'), + ) + + def queryset(self, request, queryset): + """ + Returns the filtered queryset based on the value + provided in the query string and retrievable via + `self.value()`. + """ + fresh = Q(username__isnull=True) | Q(username__exact='') + if self.value() == 'fresh': + return queryset.filter(fresh) + if self.value() == 'stale': + return queryset.filter(~fresh) + + +class MemberAdmin(admin.ModelAdmin): + list_display = ('real_name', 'username', 'membership_type', 'guild_member') + list_filter = ['guild_member', UsernameNullListFilter, 'membership_type'] + search_fields = ('real_name', 'username', ) + actions = [download_as_csv, import_old_member] + +# Register your models here. +admin.site.register(OldMember, MemberAdmin) \ No newline at end of file diff --git a/gms/import_members/apps.py b/gms/import_members/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..8ab6072b2bb9e1e246d5a801faf4b75de419581d --- /dev/null +++ b/gms/import_members/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + +class ImportMembersConfig(AppConfig): + name = 'import_members' + label = name + verbose_name = 'Import members from old MemberDB' diff --git a/gms/import_members/db.py b/gms/import_members/db.py new file mode 100644 index 0000000000000000000000000000000000000000..32e0d744eab5a2ecba5ee431a246dc304596db28 --- /dev/null +++ b/gms/import_members/db.py @@ -0,0 +1,30 @@ +""" +Route model operations to the correct database (when using more than one) +see https://docs.djangoproject.com/en/2.1/topics/db/multi-db/ +""" + +class MemberDbRouter: + """ + Send all operations to the default database except for the old memberdb stuff (which goes to the memberdb_old database) + """ + def db_for_read(self, model, **hints): + if model._meta.app_label == 'import_members': + return 'memberdb_old' + return None + + def db_for_write(self, model, **hints): + if model._meta.app_label == 'import_members': + return 'memberdb_old' + return None + + def allow_relation(self, obj1, obj2, **hints): + # Return the default setting + return None + + def allow_migrate(self, db, app_label, model_name=None, **hints): + """ + Make sure we don't do any migrations to the old database, it would only break things. + """ + if app_label == 'import_members': + return False + return None \ No newline at end of file diff --git a/gms/import_members/models.py b/gms/import_members/models.py new file mode 100644 index 0000000000000000000000000000000000000000..3e5a04e1e129bcb256d51f5d4939bb007fad744f --- /dev/null +++ b/gms/import_members/models.py @@ -0,0 +1,31 @@ +from django.db import models + +import datetime + +MEMBERSHIP_TYPES = ( + (1, 'O\' Day Special'), + (2, 'Student'), + (3, 'Non Student'), +) + +class OldMember (models.Model): + real_name = models.CharField ('Real Name', max_length=200,) + username = models.CharField ('Username', max_length=16, blank=True) + email_address = models.CharField ('Email Address', max_length=200, blank=True) + membership_type = models.IntegerField ('Membership Type', choices=MEMBERSHIP_TYPES,) + guild_member = models.BooleanField ('Guild Member', default=False, blank=True) + student_no = models.CharField ('Student Number or ID Number', max_length=20, blank=True) + phone_number = models.CharField ('Phone Number', max_length=14, blank=True) + date_of_birth = models.DateField ('Date of Birth', null=True, blank=True) + signed_up = models.DateField ('Signed up') + + def __str__(self): + if len (self.username) > 0: + return "%s [%s]" % (self.real_name, self.username) + else: + return self.real_name + + class Meta: + managed = False + db_table = 'memberdb_member' + verbose_name = 'Old member record' \ No newline at end of file diff --git a/gms/memberdb/register.py b/gms/memberdb/register.py index add57372bac5d74bfffdb5349c8ea46ce178cf27..a62863264fcc392b4f4ad11b87b0867f2a6f12df 100644 --- a/gms/memberdb/register.py +++ b/gms/memberdb/register.py @@ -97,7 +97,15 @@ class RenewView(LoginRequiredMixin, MyUpdateView): model = Member def get_object(self): - obj = Member.objects.filter(username__exact=self.request.user.username).first() + u = self.request.user + + obj = Member.objects.filter(username__exact=u.username).first() + if (obj is None): + # make a new Member object and prefill some data + obj = Member(username=u.username) + obj.first_name = u.first_name + obj.last_name = u.last_name + obj.email_address = u.email return obj def get_context_data(self, **kwargs): @@ -107,17 +115,6 @@ class RenewView(LoginRequiredMixin, MyUpdateView): }) return context - # get the initial data with which to pre-fill the form - def get_initial(self): - data = super().get_initial() - u = self.request.user - data.update({ - 'first_name': u.first_name, - 'last_name': u.last_name, - 'email_address': u.email, - }) - return data - def form_valid(self, form): m, ms = form.save() return HttpResponseRedirect(reverse("memberdb:index"))