from datetime import date, datetime from django.db import models from django.contrib.auth.models import User from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.utils.timezone import now, pytz from simple_history.models import HistoricalRecords from simple_history import register TIMEZONE_CALGARY = pytz.timezone('America/Edmonton') register(User) IGNORE = '+' def today_alberta_tz(): return datetime.now(TIMEZONE_CALGARY).date() class Member(models.Model): user = models.OneToOneField(User, related_name='member', blank=True, null=True, on_delete=models.SET_NULL) signup_helper = models.ForeignKey(User, related_name='signed_up', blank=True, null=True, on_delete=models.SET_NULL) sponsorship = models.ManyToManyField('self', related_name='sponsored_by', symmetrical=False, blank=True) old_email = models.CharField(max_length=254, blank=True, null=True) photo_large = models.CharField(max_length=64, blank=True, null=True) photo_medium = models.CharField(max_length=64, blank=True, null=True) photo_small = models.CharField(max_length=64, blank=True, null=True) member_forms = models.CharField(max_length=64, blank=True, null=True) set_details = models.BooleanField(default=False) first_name = models.CharField(max_length=32) last_name = models.CharField(max_length=32) preferred_name = models.CharField(max_length=32) phone = models.CharField(default='', max_length=32, null=True) emergency_contact_name = models.CharField(default='', max_length=64, blank=True) emergency_contact_phone = models.CharField(default='', max_length=32, blank=True) birthdate = models.DateField(blank=True, null=True) is_minor = models.BooleanField(default=False) guardian_name = models.CharField(max_length=32, blank=True, null=True) public_bio = models.CharField(max_length=512, blank=True) private_notes = models.CharField(max_length=512, blank=True) is_director = models.BooleanField(default=False) is_staff = models.BooleanField(default=False) is_instructor = models.BooleanField(default=False) status = models.CharField(max_length=32, blank=True, null=True) expire_date = models.DateField(default=today_alberta_tz, null=True) current_start_date = models.DateField(default=today_alberta_tz, null=True) application_date = models.DateField(default=today_alberta_tz, null=True) vetted_date = models.DateField(blank=True, null=True) orientation_date = models.DateField(blank=True, null=True, default=None) lathe_cert_date = models.DateField(blank=True, null=True, default=None) mill_cert_date = models.DateField(blank=True, null=True, default=None) wood_cert_date = models.DateField(blank=True, null=True, default=None) wood2_cert_date = models.DateField(blank=True, null=True, default=None) tormach_cnc_cert_date = models.DateField(blank=True, null=True, default=None) precix_cnc_cert_date = models.DateField(blank=True, null=True, default=None) rabbit_cert_date = models.DateField(blank=True, null=True, default=None) trotec_cert_date = models.DateField(blank=True, null=True, default=None) paused_date = models.DateField(blank=True, null=True) monthly_fees = models.IntegerField(default=55, blank=True, null=True) is_allowed_entry = models.BooleanField(default=True) discourse_username = models.CharField(default='', max_length=40, blank=True, null=True) mediawiki_username = models.CharField(default='', max_length=40, blank=True, null=True) allow_last_scanned = models.BooleanField(default=True) history = HistoricalRecords(excluded_fields=['member_forms']) list_display = ['user', 'preferred_name', 'last_name', 'status'] search_fields = ['user__username', 'preferred_name', 'last_name', 'status'] def __str__(self): return getattr(self.user, 'username', 'None') class Transaction(models.Model): user = models.ForeignKey(User, related_name='transactions', blank=True, null=True, on_delete=models.SET_NULL) recorder = models.ForeignKey(User, related_name=IGNORE, blank=True, null=True, on_delete=models.SET_NULL) member_id = models.IntegerField(blank=True, null=True) date = models.DateField(default=today_alberta_tz) amount = models.DecimalField(max_digits=7, decimal_places=2) reference_number = models.CharField(max_length=64, blank=True, null=True) memo = models.TextField(blank=True, null=True) number_of_membership_months = models.IntegerField(blank=True, null=True) payment_method = models.TextField(blank=True, null=True) category = models.TextField(blank=True, null=True) account_type = models.TextField(blank=True, null=True) info_source = models.TextField(blank=True, null=True) paypal_txn_id = models.CharField(max_length=17, blank=True, null=True, unique=True) paypal_txn_type = models.CharField(max_length=64, blank=True, null=True) paypal_payer_id = models.CharField(max_length=13, blank=True, null=True) protocoin = models.DecimalField(max_digits=7, decimal_places=2, default=0) report_type = models.TextField(blank=True, null=True) report_memo = models.TextField(blank=True, null=True) history = HistoricalRecords() list_display = ['date', 'user', 'amount', 'protocoin', 'account_type', 'category'] search_fields = ['date', 'user__username', 'account_type', 'category'] def __str__(self): return '%s tx %s' % (user.username, date) class PayPalHint(models.Model): user = models.ForeignKey(User, related_name='paypal_hints', blank=True, null=True, on_delete=models.SET_NULL) account = models.CharField(unique=True, max_length=13) member_id = models.IntegerField(null=True) history = HistoricalRecords() list_display = ['account', 'user'] search_fields = ['account', 'user__username'] def __str__(self): return self.account class IPN(models.Model): datetime = models.DateTimeField(auto_now_add=True) data = models.TextField() status = models.CharField(max_length=32) history = HistoricalRecords() list_display = ['datetime', 'status'] search_fields = ['datetime', 'status'] def __str__(self): return self.datetime class Card(models.Model): user = models.ForeignKey(User, related_name='cards', blank=True, null=True, on_delete=models.SET_NULL) member_id = models.IntegerField(blank=True, null=True) card_number = models.CharField(unique=True, max_length=16, blank=True, null=True) notes = models.TextField(blank=True, null=True) last_seen_at = models.DateField(blank=True, null=True) last_seen = models.DateTimeField(blank=True, null=True) active_status = models.CharField(max_length=32, blank=True, null=True) history = HistoricalRecords(excluded_fields=['last_seen_at', 'last_seen']) list_display = ['card_number', 'user', 'last_seen'] search_fields = ['card_number', 'user__username', 'last_seen'] def __str__(self): return self.card_number class Course(models.Model): name = models.TextField(blank=True, null=True) description = models.TextField(blank=True, null=True) is_old = models.BooleanField(default=False) tags = models.CharField(max_length=128, blank=True) history = HistoricalRecords() list_display = ['name', 'id'] search_fields = ['name', 'id'] def __str__(self): return self.name class Session(models.Model): instructor = models.ForeignKey(User, related_name='teaching', blank=True, null=True, on_delete=models.SET_NULL) course = models.ForeignKey(Course, related_name='sessions', blank=True, null=True, on_delete=models.SET_NULL) is_cancelled = models.BooleanField(default=False) old_instructor = models.TextField(blank=True, null=True) datetime = models.DateTimeField(blank=True, null=True) cost = models.DecimalField(max_digits=5, decimal_places=2) max_students = models.IntegerField(blank=True, null=True) history = HistoricalRecords() list_display = ['datetime', 'course', 'instructor'] search_fields = ['datetime', 'course__name', 'instructor__username'] def __str__(self): return '%s @ %s' % (self.course.name, self.datetime.astimezone(TIMEZONE_CALGARY).strftime('%Y-%m-%d %-I:%M %p')) class Training(models.Model): user = models.ForeignKey(User, related_name='training', blank=True, null=True, on_delete=models.SET_NULL) session = models.ForeignKey(Session, related_name='students', blank=True, null=True, on_delete=models.SET_NULL) member_id = models.IntegerField(blank=True, null=True) attendance_status = models.TextField(blank=True, null=True) sign_up_date = models.DateField(default=today_alberta_tz, blank=True, null=True) paid_date = models.DateField(blank=True, null=True) history = HistoricalRecords() list_display = ['session', 'user'] search_fields = ['session__course__name', 'user__username'] def __str__(self): return '%s taking %s @ %s' % (self.user, self.session.course.name, self.session.datetime) class Interest(models.Model): user = models.ForeignKey(User, related_name='interests', null=True, on_delete=models.SET_NULL) course = models.ForeignKey(Course, related_name='interests', null=True, on_delete=models.SET_NULL) satisfied_by = models.ForeignKey(Session, related_name='satisfies', null=True, on_delete=models.SET_NULL) list_display = ['user', 'course', 'satisfied_by'] search_fields = ['user__username', 'course__name'] def __str__(self): return '%s interested in %s' % (self.user, self.course) class MetaInfo(models.Model): backup_id = models.TextField() class StatsMemberCount(models.Model): date = models.DateField(default=today_alberta_tz) member_count = models.IntegerField() green_count = models.IntegerField() six_month_plus_count = models.IntegerField() vetted_count = models.IntegerField() subscriber_count = models.IntegerField() list_display = ['date', 'member_count', 'green_count', 'six_month_plus_count', 'vetted_count', 'subscriber_count'] search_fields = ['date', 'member_count', 'green_count', 'six_month_plus_count', 'vetted_count', 'subscriber_count'] class StatsSignupCount(models.Model): month = models.DateField() signup_count = models.IntegerField() retain_count = models.IntegerField(default=0) vetted_count = models.IntegerField(default=0) list_display = ['month', 'signup_count', 'retain_count', 'vetted_count'] search_fields = ['month', 'signup_count', 'retain_count', 'vetted_count'] class StatsSpaceActivity(models.Model): date = models.DateField(default=today_alberta_tz) card_scans = models.IntegerField() list_display = ['date', 'card_scans'] search_fields = ['date', 'card_scans'] class Usage(models.Model): user = models.ForeignKey(User, related_name='usages', blank=True, null=True, on_delete=models.SET_NULL) username = models.CharField(max_length=64, blank=True) # incase of LDAP-Spaceport mismatch device = models.CharField(max_length=64) started_at = models.DateTimeField(auto_now_add=True) finished_at = models.DateTimeField(null=True) deleted_at = models.DateTimeField(null=True, blank=True) num_seconds = models.IntegerField() num_reports = models.IntegerField() memo = models.TextField(blank=True) should_bill = models.BooleanField(default=True) history = HistoricalRecords(excluded_fields=['num_reports']) list_display = ['started_at', 'finished_at', 'user', 'num_seconds', 'should_bill'] search_fields = ['started_at', 'finished_at', 'user__username'] def __str__(self): return str(self.started_at) class PinballScore(models.Model): user = models.ForeignKey(User, related_name='scores', blank=True, null=True, on_delete=models.SET_NULL) started_at = models.DateTimeField(auto_now_add=True) finished_at = models.DateTimeField(null=True) game_id = models.IntegerField() player = models.IntegerField() score = models.IntegerField() # no history list_display = ['started_at', 'game_id', 'player', 'score', 'user'] search_fields = ['started_at', 'game_id', 'player', 'score', 'user__username'] def __str__(self): return str(self.started_at) class Hosting(models.Model): user = models.ForeignKey(User, related_name='hosting', blank=True, null=True, on_delete=models.SET_NULL) started_at = models.DateTimeField(auto_now_add=True) finished_at = models.DateTimeField() hours = models.DecimalField(max_digits=5, decimal_places=2) # no history list_display = ['started_at', 'hours', 'finished_at', 'user'] search_fields = ['started_at', 'hours', 'finished_at', 'user__username'] def __str__(self): return str(self.started_at) class StorageSpace(models.Model): user = models.ForeignKey(User, related_name='storage', blank=True, null=True, on_delete=models.SET_NULL) shelf_id = models.TextField(unique=True) location = models.TextField(choices=[ ('member_shelves', 'Member Shelves'), ('lockers', 'Lockers'), ('large_project_storage', 'Large Project Storage'), ]) memo = models.TextField(blank=True) history = HistoricalRecords() list_display = ['shelf_id', 'location', 'user', 'id'] search_fields = ['shelf_id', 'location', 'user__username', 'id'] def __str__(self): return self.shelf_id class HistoryIndex(models.Model): content_type = models.ForeignKey(ContentType, null=True, on_delete=models.SET_NULL) object_id = models.PositiveIntegerField() history = GenericForeignKey('content_type', 'object_id') owner_id = models.PositiveIntegerField() owner_name = models.TextField() object_name = models.TextField() history_user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL) history_date = models.DateTimeField() history_type = models.TextField() revert_url = models.TextField() is_system = models.BooleanField() is_admin = models.BooleanField() list_display = ['history_date', 'history_user', 'history_type', 'owner_name', 'object_name'] search_fields = ['history_date', 'history_user__username', 'history_type', 'owner_name', 'object_name'] def __str__(self): return '%s changed %s\'s %s' % (self.history_user, self.owner_name, self.object_name) class HistoryChange(models.Model): index = models.ForeignKey(HistoryIndex, related_name='changes', null=True, on_delete=models.SET_NULL) field = models.TextField() old = models.TextField() new = models.TextField() list_display = ['field', 'old', 'new', 'index'] search_fields = ['field', 'old', 'new', 'index__history_user__username'] def __str__(self): return self.field