From c33c6d880a5acf52850b01120f3db803d045d1e5 Mon Sep 17 00:00:00 2001 From: frekk <frekk@ucc.asn.au> Date: Sun, 30 Dec 2018 22:35:20 +0800 Subject: [PATCH] add import function from old memberdb --- gms/gms/settings.py | 3 +++ gms/gms/settings_local.example.py | 10 +++++++- gms/import_members/actions.py | 42 +++++++++++++++++++++++++++++++ gms/import_members/admin.py | 41 ++++++++++++++++++++++++++++++ gms/import_members/apps.py | 6 +++++ gms/import_members/db.py | 30 ++++++++++++++++++++++ gms/import_members/models.py | 31 +++++++++++++++++++++++ gms/memberdb/register.py | 21 +++++++--------- 8 files changed, 171 insertions(+), 13 deletions(-) create mode 100644 gms/import_members/actions.py create mode 100644 gms/import_members/admin.py create mode 100644 gms/import_members/apps.py create mode 100644 gms/import_members/db.py create mode 100644 gms/import_members/models.py diff --git a/gms/gms/settings.py b/gms/gms/settings.py index 606e6a2..4c34c04 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 5d5edde..f8984ca 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 0000000..26639bf --- /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 0000000..32ca9c7 --- /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 0000000..8ab6072 --- /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 0000000..32e0d74 --- /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 0000000..3e5a04e --- /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 add5737..a628632 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")) -- GitLab