class EmergencyContact(models.Model): user = models.OneToOneField(User, blank=False) name = models.CharField(max_length=254, blank=True) relationship = models.CharField(max_length=254, blank=True) phone = PhoneNumberField(blank=True, null=True) email = models.EmailField(blank=True, null=True) last_updated = models.DateTimeField(auto_now_add=True)
class UserProfile(models.Model): user = models.ForeignKey(User, unique=True) invitation_code = models.CharField(max_length=20) max_num_domains = models.IntegerField(default=8) contract_term_years = models.IntegerField(default=1, choices=TERM_CHOICES) pin = models.CharField(max_length=4, blank=True, default="") mobile_phone_number = PhoneNumberField(max_length=15, blank=True) organization_name = models.CharField(max_length=256) country = models.CharField(max_length=2, default="US") state = models.CharField( blank=True, max_length=2, choices=US_STATES, ) city = models.CharField(max_length=256) preferred_language = models.CharField(max_length=2, blank=True, default="en") npi = models.CharField(max_length=20, default="", blank=True) email_verified = models.BooleanField(default=False) sms_verified = models.BooleanField(default=False) partner_code = models.CharField(max_length=20, default="", blank=True) creation_date = models.DateField(auto_now_add=True) renewal_date = models.DateField(auto_now_add=True) def __unicode__(self): return '%s, %s (%s)' % (self.user.last_name, self.user.first_name, self.user.username)
class Phone(models.Model): person = models.ForeignKey(Person) start_date = models.DateField('service start date', help_text= 'Enter date this phone number went into service for this person \ with this carrier. Leave blank if that date cannot be determined.', blank=True, null=True, default=None) end_date = models.DateField('service end date', help_text= 'Enter date this phone number for this person ended with this carrier. \ Leave blank if that date cannot be determined.', blank=True, null=True, default=None) valid_date = models.DateField(help_text='Enter a date when this phone \ number was a valid way to contact this person.') invalid_date = models.DateField(help_text'Enter a date when this phone \ number was no longer a valid way to contact this person.') phone_number = PhoneNumberField() carrier = models.CharField('carrier and type', help_text= "Enter phone-service provider's name and/or type \ (contract/prepaid/landline/voip/virtual). \ Leave blank if both provider and type cannot be determined.", blank=True, max_length=64) private = models.BooleanField(help_text='If marked as private, this phone \ number will not be visible to other users of the database unless \ given on a form like a rental applicaton. Anyone owning rental \ units may gain access to any rental application in the database.') belongs = models.NullBooleanField('belongs to this person', help_text= 'Does/did this phone number belong to this person? Should be \ checked if a service start date was entered.') def clean(self): if bool(start_date) and not belongs==True: raise ValidationError("Since there's a date for this phone number \ going into service for this person, you must check off the \ box showing this phone number does/did belong to this person. \ If this phone number never really belonged to this person, \ delete the service start date.")
class Customer(User): #inheriting all fields from auth.User too street1 = models.CharField(max_length=60, verbose_name="Address 1") street2 = models.CharField(max_length=60, blank=True, verbose_name="Address 2") city = models.CharField(max_length=30) state = USPostalCodeField(blank=True) zip_code = models.CharField(max_length=10, verbose_name="Zip Code") phone = PhoneNumberField() owner = models.ForeignKey('self', null=True)
class Call(models.Model): phone_number = PhoneNumberField(blank=False) caller_id_name = models.CharField(max_length=30, blank=False) rang_at = models.DateTimeField() customer = models.ForeignKey(Customer) def __str__(self): callid = "{} from {} {}".format(self.rang_at, self.phone_number, self.caller_id_name) return callid
class EmergencyContact(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=False, on_delete=models.CASCADE) name = models.CharField(max_length=254, blank=True) relationship = models.CharField(max_length=254, blank=True) phone = PhoneNumberField(blank=True, null=True) email = models.EmailField(blank=True, null=True) last_updated = models.DateTimeField(auto_now_add=True) def __str__(self): return '%s - %s' % (self.user.username, self.name)
class Customer(models.Model): first_name = models.CharField(max_length=15, default="") last_name = models.CharField(max_length=15, blank=False, default="") home_phone = PhoneNumberField(blank=False, unique=True) work_phone = PhoneNumberField(blank=True) email = models.EmailField(blank=True) veterinarian = models.CharField(max_length=30, blank=True) notes = models.TextField(blank=True) class Meta: ordering = ['last_name', 'first_name'] def _get_full_name(self): "Returns the person's full name." return '%s %s' % (self.first_name, self.last_name) name = property(_get_full_name) def __str__(self): return '%s %s' % (self.first_name, self.last_name) def get_absolute_url(self): return reverse('customer_detail', kwargs={'pk': self.pk})
class UserProfile(models.Model): MAX_PHOTO_SIZE = 1024 user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=False, related_name="profile", on_delete=models.CASCADE) phone = PhoneNumberField(blank=True, null=True) phone2 = PhoneNumberField("Alternate Phone", blank=True, null=True) address1 = models.CharField(max_length=128, blank=True) address2 = models.CharField(max_length=128, blank=True) city = models.CharField(max_length=128, blank=True) state = models.CharField(max_length=2, blank=True) zipcode = models.CharField(max_length=16, blank=True) bio = models.TextField(blank=True, null=True) public_profile = models.BooleanField(default=False) gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default="U") pronouns = models.CharField(max_length=64, blank=True, null=True) howHeard = models.ForeignKey(HowHeard, blank=True, null=True, on_delete=models.CASCADE) #referred_by = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name="Referred By", related_name="referral", blank=True, null=True, on_delete=models.CASCADE) industry = models.ForeignKey(Industry, blank=True, null=True, on_delete=models.CASCADE) neighborhood = models.ForeignKey(Neighborhood, blank=True, null=True, on_delete=models.CASCADE) has_kids = models.NullBooleanField(blank=True, null=True) self_employed = models.NullBooleanField(blank=True, null=True) last_modified = models.DateField(auto_now=True, editable=False) photo = models.ImageField(upload_to=user_photo_path, blank=True, null=True) tags = TaggableManager(blank=True) valid_billing = models.NullBooleanField(blank=True, null=True) websites = models.ManyToManyField(Website, blank=True) @property def url_personal(self): return self.websites.filter(url_type__name="personal").first() @property def url_professional(self): return self.websites.filter(url_type__name="professional").first() @property def url_facebook(self): return self.websites.filter(url_type__name="facebook").first() @property def url_twitter(self): return self.websites.filter(url_type__name="twitter").first() @property def url_linkedin(self): return self.websites.filter(url_type__name="linkedin").first() @property def url_github(self): return self.websites.filter(url_type__name="github").first() def save_url(self, url_type, url_value): if url_type and url_value: w = self.websites.filter(url_type__name=url_type).first() if w: w.url = url_value w.save() else: t = URLType.objects.get(name=url_type) self.websites.create(url_type=t, url=url_value) def active_organization_memberships(self, target_date=None): if not target_date: target_date = localtime(now()).date() future = Q(end_date__isnull=True) unending = Q(end_date__gte=target_date) return self.user.organizationmember_set.filter(start_date__lte=target_date).filter(future | unending) def past_organization_memberships(self, target_date=None): if not target_date: target_date = localtime(now()).date() return self.user.organizationmember_set.filter(end_date__lte=target_date) def active_organizations(self, target_date=None): active = self.active_organization_memberships(target_date) return Organization.objects.filter(id__in=active.values('organization')) def outstanding_bills(self): """Returns all outstanding bills for this user """ from nadine.models.billing import UserBill return UserBill.objects.outstanding().filter(user=self.user) @property def outstanding_amount(self): """Returns total of all open bills for this user """ total = 0 for b in self.outstanding_bills(): total += b.total_owed return total def open_xero_invoices(self): from nadine.utils.xero_api import XeroAPI xero_api = XeroAPI() return xero_api.get_open_invoices(self.user) def pay_bills_form(self): from nadine.forms import PaymentForm return PaymentForm(initial={'username': self.user.username, 'amount': self.open_bills_amount}) def days_used(self, target_date=None): membership = Membership.objects.for_user(self.user, target_date) days = membership.coworking_days_in_period(target_date) billable = days.filter(payment="Bill") allowed = membership.coworking_day_allowance(target_date) return (days.count(), allowed, billable.count()) def all_emails(self): # Done in two queries so that the primary email address is always on top. primary = self.user.emailaddress_set.filter(is_primary=True) non_primary = self.user.emailaddress_set.filter(is_primary=False) return list(primary) + list(non_primary) def non_primary_emails(self): return self.user.emailaddress_set.filter(is_primary=False) def duration(self): return relativedelta(localtime(now()).date(), self.first_visit) def duration_str(self, include_days=False): retval = "" delta = self.duration() if delta.years: if delta.years == 1: retval = "1 year" else: retval = "%d years" % delta.years if delta.months or delta.days: retval += " and " if delta.months: if delta.months == 1: retval += "1 month" else: retval += "%d months" % delta.months if include_days and delta.days: retval += " and " if include_days and delta.days: if delta.days == 1: retval += "1 day" else: retval += "%d days" % delta.days return retval def hosted_days(self): return CoworkingDay.objects.filter(paid_by=self.user).order_by('-visit_date') def has_file_uploads(self): return FileUpload.objects.filter(user=self.user).count() > 0 def has_file(self, doc_type): return FileUpload.objects.filter(user=self.user, document_type=doc_type).count() > 0 def file_uploads(self): files = {} # Only want the latest one if there are duplicates for f in FileUpload.objects.filter(user=self.user).order_by('uploadTS').reverse(): files[f.name] = f return list(files.values()) def files_by_type(self): files = {} # Only want the latest one if there are duplicates for f in FileUpload.objects.filter(user=self.user).order_by('uploadTS').reverse(): files[f.document_type] = f return files def alerts_by_key(self, include_resolved=False): from nadine.models.alerts import MemberAlert if include_resolved: alerts = MemberAlert.objects.filter(user=self.user) else: alerts = MemberAlert.objects.filter(user=self.user, resolved_ts__isnull=True, muted_ts__isnull=True) alerts_by_key = {} for alert in alerts: if alert.key in alerts_by_key: alerts_by_key[alert.key].append(alert) else: alerts_by_key[alert.key] = [alert] return alerts_by_key def alerts(self): from nadine.models.alerts import MemberAlert return MemberAlert.objects.filter(user=self.user).order_by('-created_ts') def open_alerts(self): from nadine.models.alerts import MemberAlert return MemberAlert.objects.filter(user=self.user, resolved_ts__isnull=True, muted_ts__isnull=True).order_by('-created_ts') def resolve_alerts(self, alert_key, resolved_by=None): logger.debug("resolve_alerts: user=%s, key=%s, resolved_by=%s" % (self.user, alert_key, resolved_by)) from nadine.models.alerts import MemberAlert alerts = MemberAlert.objects.filter(user=self.user, key=alert_key, resolved_ts__isnull=True, muted_ts__isnull=True).order_by('-created_ts') if alerts: for alert in alerts: alert.resolve(resolved_by) @property def first_visit(self): first_visit = date.max first_day = CoworkingDay.objects.filter(user=self.user).order_by('visit_date').first() if first_day: if first_day.visit_date < first_visit: first_visit = first_day.visit_date individual_membership = IndividualMembership.objects.get(user=self.user) first_subscription = ResourceSubscription.objects.filter(membership=individual_membership).order_by('start_date').first() if first_subscription: if first_subscription.start_date < first_visit: first_visit = first_subscription.start_date # TODO - pull the first time we saw them in an organization if first_visit < date.max: return first_visit return None @property def last_visit(self): last_visit = date.min last_day = CoworkingDay.objects.filter(user=self.user).order_by('visit_date').last() if last_day: if last_day.visit_date > last_visit: last_visit = last_day.visit_date individual_membership = IndividualMembership.objects.get(user=self.user) last_subscription = ResourceSubscription.objects.filter(membership=individual_membership).order_by('start_date').last() if last_subscription and last_subscription.end_date: if last_subscription.end_date > last_visit: last_visit = last_subscription.end_date # TODO - pull the last time we saw them in an organization if last_visit > date.min: return last_visit return None @property def membership_type(self): membership_type = "" membership = Membership.objects.for_user(self.user) if membership: if not membership.is_active(): membership_type += "Ex " if membership.is_organization: membership_type += "Org " # Evaluate our package name package_name = membership.package_name() if package_name: membership_type += package_name if not membership.package_is_pure(): membership_type += " *" if len(membership_type) > 0: return membership_type # Now check daily logs drop_ins = CoworkingDay.objects.filter(user=self.user).count() if drop_ins == 0: return "New User" elif drop_ins == 1: return "First Day" else: return "Drop-in" def active_subscriptions(self, target_date=None): return ResourceSubscription.objects.active_subscriptions_with_username(target_date).filter(username=self.user.username) def is_active(self, target_date=None): return self.active_subscriptions(target_date).count() > 0 def is_guest(self, target_date=None): for s in self.active_subscriptions(target_date): if s.paid_by is not None: return True return False def hosts(self, target_date=None): hosts = [] for s in self.active_subscriptions(target_date): if s.paid_by is not None and s.paid_by not in hosts: hosts.append(s.paid_by) return hosts def guests(self, target_date=None): guests = [] for subscription in ResourceSubscription.objects.active_subscriptions_with_username(target_date).filter(paid_by=self.user): guest = User.objects.get(username=subscription.username) if guest not in guests: guests.append(guest) return guests def has_valid_billing(self, lookup_new_card=True): hosts = self.hosts() if hosts: for host in hosts: if not host.profile.has_valid_billing(): # If just one host has invalid billing... return False # All hosts have valid billing return True if self.valid_billing is None and lookup_new_card: logger.debug("%s: Null Valid Billing. Looking for new card..." % self) if self.has_new_card(): logger.debug("%s: Found new card. Marking billing valid." % self) self.valid_billing = True self.save() else: return False return self.valid_billing def has_billing_profile(self): try: api = PaymentAPI() if api.get_customers(self.user.username): return True except Exception: pass return False def has_new_card(self): # Check for a new card. WARNING: kinda expensive try: api = PaymentAPI return api.has_new_card(self.user.username) except Exception: pass return False def auto_bill_enabled(self): if not hasattr(settings, 'USA_EPAY_SOAP_KEY'): return None api = PaymentAPI() return api.auto_bill_enabled(self.user.username) def membership_days(self): total_days = 0 for membership in self.membership_history(): end = membership.end_date if not end: end = localtime(now()).date() diff = end - membership.start_date days = diff.days total_days = total_days + days return total_days def average_bill(self): from nadine.models.billing import UserBill bills = UserBill.objects.filter(user=self.user) if bills: bill_totals = 0 for b in bills: bill_totals = bill_totals + b.amount return bill_totals / len(bills) return 0 def is_manager(self): if self.user.is_staff: return True managers = User.helper.managers(include_future=True) return self.user in managers def __str__(self): return '%s %s' % (smart_str(self.user.first_name), smart_str(self.user.last_name)) def get_absolute_url(self): return reverse('member:profile:view', kwargs={'username': self.user.username}) def get_staff_url(self): return reverse('staff:members:detail', kwargs={'username': self.user.username}) class Meta: app_label = 'nadine' ordering = ['user__first_name', 'user__last_name'] get_latest_by = "last_modified"
class Player(models.Model): """Game-related data about a user""" TEAMS = { "H": "Humans", "Z": "Zombies", } UPGRADES = { 'O': "Original Zombie", 'P': "Pacifist", 'n': "Noodler", 'N': "Noodler II", 'B': "Black Ops", 'C': "Charger", "D": "Double Tap", "S": "Screamer", "E": "Bone Zombie", } user = models.ForeignKey(User) cell = PhoneNumberField(blank=True, null=True) game = models.ForeignKey(Game) school = models.ForeignKey(School, related_name="player_set") mailbox = models.CharField(max_length=20, null=True, blank=True) dorm = models.ForeignKey(Building) grad_year = models.PositiveIntegerField(blank=True, null=True) can_oz = models.BooleanField(default=False) feed = FeedCodeField() brains = models.IntegerField(default=0) notes = models.CharField(max_length=255, null=True, blank=True) team = models.CharField( max_length=1, choices=TEAMS.items(), default="H", ) clan = models.CharField( max_length=32, null=True, blank=True, ) upgrade = models.CharField( max_length=1, choices=UPGRADES.items(), blank=True, null=True, ) def __unicode__(self): return u"Player: {}".format(self.user.get_full_name()) @classmethod def current_players(cls): """Return all Players in the current Game.""" return cls.objects.filter(game=Game.nearest_game()) @classmethod def logged_in_player(cls, request): """Return the currently logged in Player.""" return cls.current_players().get(user=request.user) @classmethod def user_to_player(cls, u, game=None): """Return the most current Player corresponding to the given User. Because a User has multiple players, you can specify which player to retrieve by passing a Game. """ if u.is_anonymous(): raise Player.DoesNotExist game = game or Game.nearest_game() return cls.objects.get(game=game, user=u) class Meta: # A User can only have one Player per Game, and a feed code # can only correspond to one player per game. unique_together = (('user', 'game'), ('feed', 'game'))
class User(AbstractUser): email = models.EmailField(_('email address'), unique=True) uuid = models.UUIDField(unique=True, db_index=True, default=uuid4) title = models.CharField(max_length=255, null=True, blank=True) organizations = models.ManyToManyField('organizations.Organization') timezone = TimeZoneField(default=settings.TIME_ZONE, blank=True) access_new_projects = models.BooleanField(default=False) # Phone numbers fax = PhoneNumberField(null=True, blank=True) home = PhoneNumberField(null=True, blank=True) mobile = PhoneNumberField(null=True, blank=True) office = PhoneNumberField(null=True, blank=True) ext = models.CharField(max_length=10, null=True, blank=True) last_updated = models.DateTimeField(auto_now=True) removed = models.DateField(null=True) avatar_url = models.URLField(max_length=1200) objects = UserManager() USERNAME_FIELD = 'email' REQUIRED_FIELDS = [] class Meta: ordering = ['first_name', 'last_name'] db_table = 'kala_user' def set_active(self, active): self.is_active = active if not self.is_active: self.removed = datetime.date.today() self.save() def get_organizations_with_create(self): if self.is_superuser: return organizations.models.Organization.objects.all() return organizations.models.Organization.objects.filter( uuid__in=Permissions.objects.filter( user=self, permission__codename='add_organization'). values_list('object_uuid', flat=True)) def get_organizations(self): if self.is_superuser: return organizations.models.Organization.objects.active() project_uuids = Permissions.objects.filter( user=self, permission__codename__in=[ 'change_project', 'add_project', 'delete_project' ]).values_list('object_uuid', flat=True) project_org_uuids = projects.models.Project.objects.filter( uuid__in=project_uuids).values_list('organization__uuid', flat=True) org_uuids = Permissions.objects.filter(user=self, permission__codename__in=[ 'change_organization', 'add_organization', 'delete_organization' ]).values_list('object_uuid', flat=True) document_project_uuids = Permissions.objects.filter( permission__codename__in=[ 'change_document', 'add_document', 'delete_document' ], user=self).values_list('object_uuid', flat=True) document_projects = documents.models.Document.objects.filter( uuid__in=document_project_uuids).values_list( 'project__organization__uuid', flat=True) return organizations.models.Organization.objects.filter( uuid__in=list(project_org_uuids) + list(org_uuids) + list(document_projects)) def get_projects(self): if self.is_superuser: return projects.models.Project.objects.active() else: return projects.models.Project.objects.active().filter( organization__id__in=self.get_organizations().values_list( 'pk', flat=True)) def get_documents(self): if self.is_superuser: return documents.models.Document.objects.all() else: projects = self.get_organizations().values_list('project__uuid', flat=True) document_uuids = documents.models.Document.objects.filter( project__uuid__in=projects).values_list('uuid', flat=True) perm_uuids = Permissions.objects.filter( user=self, object_uuid__in=document_uuids).values_list('object_uuid', flat=True) return documents.models.Document.objects.filter( uuid__in=list(perm_uuids) + list(document_uuids)).prefetch_related( 'documentversion_set', 'documentversion_set__user', ).select_related('project') def get_users(self): if self.is_superuser: return User.objects.all() else: organizations = self.get_organizations().values_list('pk') return User.objects.filter(organizations__in=organizations) def send_invite(self): pass def add_perm(self, perm, uuid): Permissions.add_perm(perm=perm, user=self, uuid=uuid) def has_perm(self, perm, uuid): return Permissions.has_perm(perm=perm, user=self, uuid=uuid) def add_read(self, user): perm = Permission.objects.get(codename='change_user') Permissions.add_perm(perm=perm, user=user, uuid=self.uuid) def has_read(self, user): perm = Permission.objects.get(codename='change_user') return Permissions.has_perm(perm=perm, user=user, uuid=self.uuid) def add_delete(self, user): perm = Permission.objects.get(codename='delete_user') Permissions.add_perm(perm=perm, user=user, uuid=self.uuid) def has_delete(self, user): perm = Permission.objects.get(codename='delete_user') return Permissions.has_perm(perm=perm, user=user, uuid=self.uuid) def add_create(self, user): perm = Permission.objects.get(codename='add_user') Permissions.add_perm(perm=perm, user=user, uuid=self.uuid) def has_create(self, user): perm = Permission.objects.get(codename='add_user') return Permissions.has_perm(perm=perm, user=user, uuid=self.uuid) def __str__(self): # pragma: no cover return "{0} {1}".format(self.first_name, self.last_name)
class Organization(models.Model): name = models.CharField(max_length=255, unique=True) uuid = models.UUIDField(unique=True, db_index=True, default=uuid4) address = models.CharField(max_length=255, null=True, blank=True) address1 = models.CharField(max_length=255, null=True, blank=True) city = models.CharField(max_length=255, null=True, blank=True) state = models.CharField(max_length=80, null=True, blank=True) zip = models.CharField(max_length=25, null=True, blank=True) country = models.CharField(max_length=80, null=True, blank=True, default='US') fax = PhoneNumberField(null=True, blank=True) phone = PhoneNumberField(null=True, blank=True) locale = models.CharField(max_length=2, null=True, blank=True, default='en') removed = models.DateField(null=True) timezone = TimeZoneField(default=settings.TIME_ZONE) website = models.URLField(null=True, blank=True) is_active = models.BooleanField(default=True) objects = ActiveManager() with_projects = OrganizationsWithProjectManager() class Meta: ordering = ['name'] db_table = 'kala_companies' def set_active(self, active): self.is_active = active for person in self.user_set.all(): person.set_active(active) for project in Project.objects.filter(organization=self): project.set_active(active) if not self.is_active: self.removed = datetime.date.today() self.save() def get_projects(self, user): if user.is_superuser: return Project.objects.active().filter(organization=self) if Permissions.has_perms( ['change_organization', 'add_organization', 'delete_organization'], user, self.uuid): return self.project_set.all() else: document_project_uuids = Permissions.objects.filter( permission__codename__in=[ 'change_document', 'add_document', 'delete_document' ], user=user).values_list('object_uuid', flat=True) document_projects = documents.models.Document.objects.filter( project__organization=self, uuid__in=document_project_uuids).values_list('project__uuid', flat=True) project__uuids = self.project_set.all().values_list('uuid', flat=True) perm_uuids = Permissions.objects.filter( user=user, object_uuid__in=project__uuids).values_list('object_uuid', flat=True) return Project.objects.filter(uuid__in=list(perm_uuids) + list(document_projects)) def get_people(self, user): # If you are a super user or you have permissions on # an organization, then you can see everyone. if user.is_superuser or Permissions.has_perms( ['change_organization', 'add_organization', 'delete_organization'], user, self.uuid): return User.objects.all() else: return None def __str__(self): return self.name def add_change(self, user): perm = Permission.objects.get(codename='change_organization') Permissions.add_perm(perm=perm, user=user, uuid=self.uuid) def has_change(self, user): perm = Permission.objects.get(codename='change_organization') return Permissions.has_perm(perm=perm, user=user, uuid=self.uuid) def add_delete(self, user): perm = Permission.objects.get(codename='delete_organization') Permissions.add_perm(perm=perm, user=user, uuid=self.uuid) def has_delete(self, user): perm = Permission.objects.get(codename='delete_organization') return Permissions.has_perm(perm=perm, user=user, uuid=self.uuid) def add_create(self, user): perm = Permission.objects.get(codename='add_organization') Permissions.add_perm(perm=perm, user=user, uuid=self.uuid) def has_create(self, user): perm = Permission.objects.get(codename='add_organization') return Permissions.has_perm(perm=perm, user=user, uuid=self.uuid)
class User(AbstractUser): email = models.EmailField(_('email address'), unique=True) uuid = models.UUIDField(unique=True, db_index=True, default=uuid4) title = models.CharField(max_length=255, null=True, blank=True) organizations = models.ManyToManyField('organizations.Organization') timezone = TimeZoneField(default=settings.TIME_ZONE, blank=True) access_new_projects = models.BooleanField(default=False) # Phone numbers fax = PhoneNumberField(null=True, blank=True) home = PhoneNumberField(null=True, blank=True) mobile = PhoneNumberField(null=True, blank=True) office = PhoneNumberField(null=True, blank=True) ext = models.CharField(max_length=10, null=True, blank=True) last_updated = models.DateTimeField(auto_now=True) removed = models.DateField(null=True) avatar_url = models.URLField(max_length=1200) objects = KalaUserManager() USERNAME_FIELD = 'email' REQUIRED_FIELDS = [] class Meta: ordering = ['first_name', 'last_name'] db_table = 'kala_user' def set_active(self, active): self.is_active = active if not self.is_active: self.removed = datetime.date.today() self.save() def get_organizations_with_create(self): if self.is_superuser: return organizations.models.Organization.objects.active() return organizations.models.Organization.objects.active().filter( id__in=organizations.models.OrganizationPermission.objects.filter( user=self).values_list('organization__id', flat=True)) def get_organizations(self, permission=None): if self.is_superuser: return organizations.models.Organization.objects.active() else: kwargs = {'user': self} if permission: kwargs['permission__codename__in'] = permission return organizations.models.Organization.objects.active( ).filter(id__in=set().union(*[ list( organizations.models.OrganizationPermission.objects.filter( **kwargs).values_list('organization__id', flat=True)), list( projects.models.ProjectPermission.objects.filter( **kwargs).values_list('project__organization__id', flat=True).values_list( 'id', flat=True)), list( documents.models.DocumentPermission.objects.filter( **kwargs).values_list( 'document__project__organization__id', flat=True)) ])) def get_projects(self, permission=None): if self.is_superuser: return projects.models.Project.objects.active() else: kwargs = {'user': self} if permission: kwargs['permission__codename__in'] = permission return projects.models.Project.objects.active().filter( id__in=set().union(*[ list( projects.models.ProjectPermission.objects.filter( **kwargs).values_list('project__id', flat=True)), list( projects.models.Project.objects.filter( organization__id__in=organizations.models. OrganizationPermission.objects.filter( **kwargs).values_list('organization__id', flat=True)).values_list( 'id', flat=True)), list( documents.models.DocumentPermission.objects.filter( **kwargs).values_list('document__project__id', flat=True)) ])) def get_documents(self): if self.is_superuser: return documents.models.Document.objects.active() else: return documents.models.Document.objects.active().filter( id__in=set().union(*[ list( documents.models.DocumentPermission.objects.filter( user=self).values_list('document__id', flat=True)), list( documents.models.Document.objects.filter( project__id__in=projects.models.ProjectPermission. objects.filter(user=self).values_list( 'project__id', flat=True)).values_list( 'id', flat=True)), list( documents.models.Document.objects.filter( project__organization__id__in=organizations.models. OrganizationPermission.objects.filter( user=self).values_list('organization__id', flat=True)).values_list( 'id', flat=True)) ])) def get_users(self): if self.is_superuser: return User.objects.all() else: organization_ids = self.get_organizations().values_list('id') return User.objects.filter(pk__in=set().union(*[ list( organizations.models.OrganizationPermission.objects.filter( organization__id__in=organization_ids).values_list( 'user__id', flat=True)), list( projects.models.ProjectPermission.objects.filter( project__organization__id__in=organization_ids). values_list('user_id', flat=True)), list( documents.models.DocumentPermission.objects.filter( document__project__organization__id__in=organization_ids ).values_list('user_id', flat=True)) ])) def send_invite(self, app, template, subject, object): template_txt = '{0}/{1}.txt'.format(app, template) if settings.USE_HTML_EMAIL: template_html = loader.get_template('{0}/{1}.html'.format( app, template)) context = { 'object': object, 'user': self, 'uid': urlsafe_base64_encode(force_bytes(self.pk)).decode(), 'token': default_token_generator.make_token(self), 'application_url': settings.APPLICATION_URL, 'help_email': settings.HELP_EMAIL, } send_mail( subject, render_to_string(template_txt, context), settings.FROM_EMAIL, [self.email], fail_silently=False, #html_message=render(None, template_html, context) if settings.USE_HTML_EMAIL else None ) def __str__(self): # pragma: no cover return "{0} {1}".format(self.first_name, self.last_name)
class DomainBoundCertificate(models.Model): trust_anchor = models.ForeignKey(TrustAnchorCertificate) status = models.CharField(max_length=10, default="incomplete", choices=STATUS_CHOICES) sha256_digest = models.CharField( max_length=64, default="", blank=True, ) sha1_fingerprint = models.CharField( max_length=64, default="", blank=True, ) #editable=False) serial_number = models.CharField( max_length=64, default=-01, blank=True, ) #editable=False) domain = models.CharField(max_length=512, default="", help_text="This value should match the email.") common_name = models.CharField(max_length=512, default="") dns = models.CharField( max_length=512, default="", verbose_name="DNS", help_text="""This should always match the email field, unless you are creating a "bad" certificate for testing. """) email = models.CharField(max_length=512, default="", verbose_name="Email or Domain", help_text="""For email-bound certificate use an email address (e.g. [email protected]) For a domain-bound certificate use a domain (e.g. "direct.example.com).""") rsa_keysize = models.IntegerField(max_length=4, default=2048, choices=RSA_KEYSIZE_CHOICES) country = models.CharField(max_length=2, default="US") state = models.CharField(blank=True, max_length=2, choices=US_STATES) city = models.CharField( max_length=64, help_text="Letters, numbers, and dashes okay. No slashes") organization = models.CharField( max_length=64, help_text="Letters, numbers, and dashes okay. No slashes") completed_dir_path = models.CharField(max_length=1024, default="", blank=True) public_key_path = models.CharField(max_length=1024, default="", blank=True) private_expire_days = models.IntegerField(max_length=2, default="1", blank=True) private_zip_name = models.CharField(max_length=512, default="", blank=True) presigned_zip_url = models.CharField(max_length=512, default="", blank=True) presigned_zip_s3 = models.CharField(max_length=512, default="", blank=True) public_cert_der_url = models.CharField(max_length=1024, default="", blank=True) public_cert_der_s3 = models.CharField(max_length=1024, default="", blank=True) public_cert_pem_url = models.CharField(max_length=1024, default="", blank=True) public_cert_pem_s3 = models.CharField(max_length=1024, default="", blank=True) public_cert_der_url = models.CharField(max_length=1024, default="", blank=True) public_cert_der_s3 = models.CharField(max_length=1024, default="", blank=True) public_cert_status_url = models.CharField(max_length=1024, default="", blank=True) public_cert_status_sha1_url = models.CharField(max_length=1024, default="", blank=True) public_cert_x5c_url = models.CharField(max_length=1024, default="", blank=True) revoke = models.BooleanField() revoked_note = models.TextField(max_length=512, blank=True, default="") verified = models.BooleanField() verified_message_sent = models.BooleanField() rcsp_response = models.TextField(max_length=512, blank=True, default="") notes = models.TextField(max_length=1024, blank=True, default="") npi = models.CharField(max_length=20, default="", blank=True) contact_first_name = models.CharField(max_length=100, default="", blank=True) contact_last_name = models.CharField(max_length=100, default="", blank=True) contact_email = models.EmailField() contact_mobile_phone = PhoneNumberField(max_length=15, blank=True) contact_land_phone = PhoneNumberField(max_length=15, blank=True) contact_fax = PhoneNumberField(max_length=15, blank=True) expiration_date = models.DateField(blank=True, editable=False) expire_days = models.IntegerField(max_length=5, default=365, choices=EXPIRE_CHOICES) creation_date = models.DateField(auto_now_add=True) def __unicode__(self): return '%s (%s) Status=%s, Created %s, Issued by %s' % ( self.domain, self.serial_number, self.status, self.creation_date, self.trust_anchor.organization) class Meta: get_latest_by = "creation_date" ordering = ('-creation_date', ) def save(self, **kwargs): if not self.sha256_digest and self.status == "incomplete": print "We've only just begun...I'm new." today = datetime.date.today() self.expiration_date = today + datetime.timedelta( days=self.expire_days) result = create_endpoint_certificate( common_name=self.common_name, email=self.email, dns=self.dns, anchor_dns=self.trust_anchor.dns, expires=self.expire_days, organization=self.organization, city=self.city, state=self.state, country=self.country, rsakey=self.rsa_keysize, user=self.trust_anchor.owner.username, public_key_path=self.trust_anchor.public_key_path, private_key_path=self.trust_anchor.private_key_path, completed_anchor_dir=self.trust_anchor.completed_dir_path) sha256_digest = result['sha256_digest'] self.serial_number = result['serial_number'] self.sha1_fingerprint = result['sha1_fingerprint'] self.notes = result['notes'] self.private_zip_name = result['anchor_zip_download_file_name'] self.status = result['status'] self.completed_dir_path = result['completed_dir_path'] self.public_key_path = result['public_key_path'] #send the verifier an email notification msg = """ <html> <head> </head> <body> A new Direct Domain Bound certificate was created by %s and requires your review. Here is a link: <ul> <li><a href="https://console.directca.org/admin/certificates/domainboundcertificate/%s">%s</a></li> </ul> </body> </html> """ % ( self.organization, self.id, self.domain, ) if settings.SEND_CA_EMAIL: msg = EmailMessage( '[DirectCA]A new Domain-Bound Certificate requires verification', msg, settings.EMAIL_HOST_USER, [ settings.CA_VERIFIER_EMAIL, ]) msg.content_subtype = "html" # Main content is now text/html msg.send() super(DomainBoundCertificate, self).save(**kwargs) return if self.verified and not self.verified_message_sent and \ self.status in ('unverified', 'good'): print "VERIFY ----------------------------" """ Mark the certificate as verified""" self.verified = True self.status = "good" # RCSP ------------------------------------------------------------ rcsp_result = write_verification_message( self.serial_number, self.common_name, "good", self.sha1_fingerprint, ) #Write it to db self.rcsp_response = rcsp_result #set the filename fn = "%s.json" % (self.serial_number) #Write it to file fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(rcsp_result)) f.close() #Upload the RCSP file to S3 s = SimpleS3() if settings.USE_S3: self.public_cert_status_url = s.store_in_s3( fn, fp, bucket=settings.RCSP_BUCKET, public=True) self.public_cert_status_url = s.build_pretty_url( self.public_cert_status_url, settings.RCSP_BUCKET) #JOSE ------------------------------------------------------------- #get all the files certfilelist = [ settings.CA_PUBLIC_CERT, self.trust_anchor.public_key_path, self.public_key_path ] fn = "%s-chain.pem" % (self.dns) chained_cert_path = os.path.join(self.completed_dir_path, fn) certlist = chain_keys_in_list(chained_cert_path, certfilelist) #write the json x5c_json = write_x5c_message(self.email, certlist) # set the filename ------------------------------------------------ fn = "%s-x5c.json" % (self.serial_number) # Write it to file ------------------------------------------------ fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(x5c_json)) f.close() #Upload the x5c file to S3 s = SimpleS3() if settings.USE_S3: key = "x5c/" + fn self.public_cert_x5c_url = s.store_in_s3( key, fp, bucket=settings.X5C_BUCKET, public=True) self.public_cert_x5c_url = s.build_pretty_url( self.public_cert_x5c_url, settings.X5C_BUCKET) #Calculate the SHA1 fingerprint & write it to a file digestsha1 = json.dumps(sha.sha1_from_filepath(fp), indent=4) fn = "%s-sha1.json" % (self.serial_number) fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(digestsha1)) f.close() #Upload the RCSP SHA! Digest to S3 if settings.USE_S3: self.public_cert_status_sha1_url = s.store_in_s3( fn, fp, bucket=settings.RCSPSHA1_BUCKET, public=True) #Upload the PEM and DER public certificates fn = "%s.pem" % (self.dns) key = "%s/%s/endpoints/%s" % (self.trust_anchor.owner.username, self.trust_anchor.dns, fn) fp = os.path.join(self.completed_dir_path, fn) if settings.USE_S3: self.public_cert_pem_url = s.store_in_s3( key, fp, bucket=settings.PUBCERT_BUCKET, public=True) self.public_cert_pem_url = s.build_pretty_url( self.public_cert_pem_url, settings.PUBCERT_BUCKET) self.public_cert_pem_s3 = json.dumps({ "bucket": settings.PUBCERT_BUCKET, "key": key }) fn = "%s.der" % (self.dns) key = "%s/%s/%s" % (self.trust_anchor.owner.username, self.dns, fn) fp = os.path.join(self.completed_dir_path, fn) #print "S3 --------------------", key, fp if settings.USE_S3: self.public_cert_der_url = s.store_in_s3( key, fp, bucket=settings.PUBCERT_BUCKET, public=True) self.public_cert_der_url = s.build_pretty_url( self.public_cert_der_url, settings.PUBCERT_BUCKET) self.public_cert_der_s3 = json.dumps({ "bucket": settings.PUBCERT_BUCKET, "key": key }) #Send the zip file and expire in one week fp = os.path.join(self.completed_dir_path, self.private_zip_name) key = str(self.private_zip_name) if settings.USE_S3: url = s.store_in_s3(key, fp, bucket=settings.PRIVCERT_BUCKET) self.presigned_zip_url = s.get_presignedurl( key, bucket=settings.PRIVCERT_BUCKET) self.presigned_zip_s3 = json.dumps({ "bucket": settings.PRIVCERT_BUCKET, "key": key }) #send the verification email. msg = """ <html> <head> </head> <body> Congratulations. Your domain bound certificate has been verified. Below are links to your public certificates and related status information. Please login into <a href="https://console.directca.org">console.directca.org</a> to retrieve your private certificates for this domain. <ul> <li><a href="%s">PEM File - %s </a></li> <li><a href="%s">DER File - %s </a></li> <li><a href="%s">Status - %s </a></li> <li><a href="%s">Status SHA1 Digest - %s </a></li> <li><a href="%s">Certificate chain in JOSE x5c format - %s </a></li> </ul> <p>For security purposes you must <a href="https://console.directca.org">login</a> and download the private certificates within 72 hours of this email. </p> </body> </html> """ % ( self.public_cert_pem_url, self.public_cert_pem_url, self.public_cert_der_url, self.public_cert_der_url, self.public_cert_status_url, self.public_cert_status_url, self.public_cert_status_sha1_url, self.public_cert_status_sha1_url, self.public_cert_x5c_url, self.public_cert_x5c_url, ) if settings.SEND_CA_EMAIL: msg = EmailMessage( '[DirectCA]Your Domain-Bound Certificate has been verified', msg, settings.EMAIL_HOST_USER, [self.trust_anchor.owner.email, self.contact_email]) msg.content_subtype = "html" # Main content is now text/html msg.send() #send the verification email. self.verified_message_sent = True super(DomainBoundCertificate, self).save(**kwargs) return if self.revoke and self.status != "revoked": self.revoke = True self.status = "revoked" # Get the response rcsp_result = write_verification_message( self.serial_number, self.common_name, "revoked", self.sha1_fingerprint, ) #Write it to db self.rcsp_response = rcsp_result fn = "%s.json" % (self.serial_number) #Write it to file fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(rcsp_result)) f.close() #Upload the RCSP file to S3 s = SimpleS3() if settings.USE_S3: url = s.store_in_s3(fn, fp, bucket=settings.RCSP_BUCKET, public=True) #Calculate the SHA1 fingerprint & write it to a file digestsha1 = json.dumps(sha.sha1_from_filepath(fp), indent=4) fn = "%s-sha1.json" % (self.serial_number) fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(digestsha1)) f.close() #Upload the RCSP SHA! Digest to S3 if settings.USE_S3: url = s.store_in_s3(fn, fp, bucket=settings.RCSPSHA1_BUCKET, public=True) #Delete all the old files: #PEM, DIR, ZIP if self.presigned_zip_s3: s3info = json.loads(self.presigned_zip_s3) self.presigned_zip_url = s.delete_in_s3( s3info['bucket'], s3info['key'], ) if self.public_cert_der_s3: s3info = json.loads(self.public_cert_der_s3) self.public_cert_der_url = s.delete_in_s3( s3info['bucket'], s3info['key'], ) if self.public_cert_pem_s3: s3info = json.loads(self.public_cert_pem_s3) self.public_cert_pem_url = s.delete_in_s3( s3info['bucket'], s3info['key'], ) # Now perform the revcation on our index and delete old files. revoke_from_anchor(self) revoke(self) super(DomainBoundCertificate, self).save(**kwargs) def delete(self, **kwargs): self.revoke = True self.status = "revoked" # Get the response rcsp_result = write_verification_message( self.serial_number, self.common_name, "revoked", self.sha1_fingerprint, ) #Write it to db self.rcsp_response = rcsp_result fn = "%s.json" % (self.serial_number) #Write it to file fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(rcsp_result)) f.close() #Upload the RCSP file to S3 s = SimpleS3() if settings.USE_S3: url = s.store_in_s3(fn, fp, bucket=settings.RCSP_BUCKET, public=True) #Calculate the SHA1 fingerprint & write it to a file digestsha1 = json.dumps(sha.sha1_from_filepath(fp), indent=4) fn = "%s-sha1.json" % (self.serial_number) fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(digestsha1)) f.close() #Upload the RCSP SHA! Digest to S3 if settings.USE_S3: url = s.store_in_s3(fn, fp, bucket=settings.RCSPSHA1_BUCKET, public=True) revoke_from_anchor(self) revoke(self) super(DomainBoundCertificate, self).save(**kwargs)
class Member(models.Model): MAX_PHOTO_SIZE = 1024 """A person who has used the space and may or may not have a monthly membership""" objects = MemberManager() user = models.OneToOneField(User, blank=False) email2 = models.EmailField("Alternate Email", blank=True, null=True) phone = PhoneNumberField(blank=True, null=True) phone2 = PhoneNumberField("Alternate Phone", blank=True, null=True) address1 = models.CharField(max_length=128, blank=True) address2 = models.CharField(max_length=128, blank=True) city = models.CharField(max_length=128, blank=True) state = models.CharField(max_length=2, blank=True) zipcode = models.CharField(max_length=5, blank=True) url_personal = models.URLField(blank=True, null=True) url_professional = models.URLField(blank=True, null=True) url_facebook = models.URLField(blank=True, null=True) url_twitter = models.URLField(blank=True, null=True) url_linkedin = models.URLField(blank=True, null=True) url_aboutme = models.URLField(blank=True, null=True) url_github = models.URLField(blank=True, null=True) gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default="U") howHeard = models.ForeignKey(HowHeard, blank=True, null=True) #referred_by = models.ForeignKey(User, verbose_name="Referred By", related_name="referral", blank=True, null=True) industry = models.ForeignKey(Industry, blank=True, null=True) neighborhood = models.ForeignKey(Neighborhood, blank=True, null=True) has_kids = models.NullBooleanField(blank=True, null=True) self_employed = models.NullBooleanField(blank=True, null=True) company_name = models.CharField(max_length=128, blank=True, null=True) promised_followup = models.DateField(blank=True, null=True) last_modified = models.DateField(auto_now=True, editable=False) photo = models.ImageField(upload_to=user_photo_path, blank=True, null=True) tags = TaggableManager(blank=True) valid_billing = models.NullBooleanField(blank=True, null=True) @property def first_name(self): return smart_str(self.user.first_name) @property def last_name(self): return smart_str(self.user.last_name) @property def email(self): return self.user.email @property def full_name(self): return '%s %s' % (smart_str( self.user.first_name), smart_str(self.user.last_name)) def natural_key(self): return [self.user.id] def all_bills(self): """Returns all of the open bills, both for this member and any bills for other members which are marked to be paid by this member.""" from nadine.models.payment import Bill return Bill.objects.filter( models.Q(member=self) | models.Q(paid_by=self)).order_by('-bill_date') def open_bills(self): """Returns all of the open bills, both for this member and any bills for other members which are marked to be paid by this member.""" from nadine.models.payment import Bill return Bill.objects.filter( models.Q(member=self) | models.Q(paid_by=self)).filter( transactions=None).order_by('bill_date') def open_bill_amount(self): total = 0 for b in self.open_bills(): total = total + b.amount return total def open_bills_amount(self): """Returns the amount of all of the open bills, both for this member and any bills for other members which are marked to be paid by this member.""" from nadine.models.payment import Bill return Bill.objects.filter( models.Q(member=self) | models.Q(paid_by=self)).filter(transactions=None).aggregate( models.Sum('amount'))['amount__sum'] def open_xero_invoices(self): from nadine.xero_api import XeroAPI xero_api = XeroAPI() return xero_api.get_open_invoices(self.user) def pay_bills_form(self): from staff.forms import PayBillsForm return PayBillsForm(initial={ 'member_id': self.id, 'amount': self.open_bills_amount }) def last_bill(self): """Returns the latest Bill, or None if the member has not been billed. NOTE: This does not (and should not) return bills which are for other members but which are to be paid by this member.""" from nadine.models.payment import Bill bills = Bill.objects.filter(member=self) if len(bills) == 0: return None return bills[0] def membership_history(self): return Membership.objects.filter(member=self).order_by( '-start_date', 'end_date') def last_membership(self): """Returns the latest membership, even if it has an end date, or None if none exists""" memberships = Membership.objects.filter(member=self).order_by( '-start_date', 'end_date')[0:] if memberships == None or len(memberships) == 0: return None return memberships[0] def active_membership(self): for membership in self.membership_history(): if membership.is_active(): return membership return None def membership_for_day(self, day): return Membership.objects.active_memberships(target_date=day).filter( member=self).first() def activity_this_month(self, test_date=None): if not test_date: test_date = date.today() membership = self.active_membership() if membership: if membership.guest_of: # Return host's activity host = membership.guest_of return host.activity_this_month() month_start = membership.prev_billing_date(test_date) else: # Just go back one month from this date since there isn't a membership to work with month_start = test_date - MonthDelta(1) # print month_start activity = [] for m in [self] + self.guests(): for l in DailyLog.objects.filter(member=m, payment='Bill', visit_date__gte=month_start): activity.append(l) for l in DailyLog.objects.filter(guest_of=self, payment='Bill', visit_date__gte=month_start): activity.append(l) return activity def activity(self): return DailyLog.objects.filter(member=self) def paid_count(self): return self.activity().filter(payment='Bill').count() def first_visit(self): if Membership.objects.filter(member=self).count() > 0: return Membership.objects.filter( member=self).order_by('start_date')[0].start_date else: if DailyLog.objects.filter(member=self).count() > 0: return DailyLog.objects.filter( member=self).order_by('visit_date')[0].visit_date else: return None def duration(self): return relativedelta(timezone.now().date(), self.first_visit()) def duration_str(self, include_days=False): retval = "" delta = self.duration() if delta.years: if delta.years == 1: retval = "1 year" else: retval = "%d years" % delta.years if delta.months or delta.days: retval += " and " if delta.months: if delta.months == 1: retval += "1 month" else: retval += "%d months" % delta.months if include_days and delta.days: retval += " and " if include_days and delta.days: if delta.days == 1: retval += "1 day" else: retval += "%d days" % delta.days return retval def is_anniversary(self): return def host_daily_logs(self): return DailyLog.objects.filter(guest_of=self).order_by('-visit_date') def has_file_uploads(self): return FileUpload.objects.filter(user=self.user).count() > 0 def has_file(self, doc_type): return FileUpload.objects.filter(user=self.user, document_type=doc_type).count() > 0 def file_uploads(self): files = {} # Only want the latest one if there are duplicates for f in FileUpload.objects.filter( user=self.user).order_by('uploadTS').reverse(): files[f.name] = f return files.values() def files_by_type(self): files = {} # Only want the latest one if there are duplicates for f in FileUpload.objects.filter( user=self.user).order_by('uploadTS').reverse(): files[f.document_type] = f return files def alerts_by_key(self, include_resolved=False): from nadine.models.alerts import MemberAlert if include_resolved: alerts = MemberAlert.objects.filter(user=self.user) else: alerts = MemberAlert.objects.filter(user=self.user, resolved_ts__isnull=True, muted_ts__isnull=True) alerts_by_key = {} for alert in alerts: if alert.key in alerts_by_key: alerts_by_key[alert.key].append(alert) else: alerts_by_key[alert.key] = [alert] return alerts_by_key def alerts(self): from nadine.models.alerts import MemberAlert return MemberAlert.objects.filter( user=self.user).order_by('-created_ts') def open_alerts(self): from nadine.models.alerts import MemberAlert return MemberAlert.objects.filter( user=self.user, resolved_ts__isnull=True, muted_ts__isnull=True).order_by('-created_ts') def resolve_alerts(self, alert_key, resolved_by=None): logger.debug("resolve_alerts: user=%s, key=%s, resolved_by=%s" % (self.user, alert_key, resolved_by)) from nadine.models.alerts import MemberAlert alerts = MemberAlert.objects.filter( user=self.user, key=alert_key, resolved_ts__isnull=True, muted_ts__isnull=True).order_by('-created_ts') if alerts: for alert in alerts: alert.resolve(resolved_by) def member_since(self): first = self.first_visit() if first == None: return None return timezone.localtime(timezone.now()) - datetime.combine( first, time(0, 0, 0)) def last_visit(self): if DailyLog.objects.filter(member=self).count() > 0: return DailyLog.objects.filter( member=self).latest('visit_date').visit_date else: if Membership.objects.filter(member=self, end_date__isnull=False).count() > 0: return Membership.objects.filter( member=self, end_date__isnull=False).latest('end_date').end_date else: return None def membership_type(self): active_membership = self.active_membership() if active_membership: return active_membership.membership_plan else: last_monthly = self.last_membership() if last_monthly: return "Ex" + str(last_monthly.membership_plan) # Now check daily logs drop_ins = DailyLog.objects.filter(member=self).count() if drop_ins == 0: return "New User" elif drop_ins == 1: return "First Day" else: return "Drop-in" def is_active(self): m = self.active_membership() return m is not None def has_desk(self): m = self.active_membership() if not m: return False if m.is_active(): return m.has_desk return False def is_guest(self): m = self.active_membership() if m and m.is_active() and m.guest_of: return m.guest_of return None def has_valid_billing(self): host = self.is_guest() if host: return host.has_valid_billing() if self.valid_billing is None: logger.debug("%s: Null Valid Billing" % self) if self.has_new_card(): logger.debug("%s: Found new card. Marking billing valid." % self) self.valid_billing = True self.save() else: self.valid_billing = False return self.valid_billing def has_billing_profile(self): if usaepay.getAllCustomers(self.user.username): return True return False def has_new_card(self): # Check for a new card. WARNING: kinda expensive return usaepay.has_new_card(self.user.username) def guests(self): guests = [] for membership in Membership.objects.filter(guest_of=self): if membership.is_active(): guests.append(membership.member) return guests def deposits(self): return SecurityDeposit.objects.filter(member=self) def __str__(self): return '%s %s' % (smart_str( self.user.first_name), smart_str(self.user.last_name)) def usaepay_auth(self): return usaepay.get_auth_code(self.user.username) def auto_bill_enabled(self): return usaepay.auto_bill_enabled(self.user.username) def member_notes(self): return MemberNote.objects.filter(member=self) def special_days(self): return SpecialDay.objects.filter(member=self) def membership_days(self): total_days = 0 for membership in self.membership_history(): end = membership.end_date if not end: end = timezone.now().date() diff = end - membership.start_date days = diff.days total_days = total_days + days return total_days def average_bill(self): from nadine.models.payment import Bill bills = Bill.objects.filter(member=self) if bills: bill_totals = 0 for b in bills: bill_totals = bill_totals + b.amount return bill_totals / len(bills) return 0 def is_manager(self): if hasattr(settings, 'TEAM_MEMBERSHIP_PLAN'): management_plan = MembershipPlan.objects.filter( name=settings.TEAM_MEMBERSHIP_PLAN).first() if management_plan: active_membership = self.active_membership() if active_membership: return active_membership.membership_plan == management_plan return False @models.permalink def get_absolute_url(self): return ('staff.views.member_detail', (), {'member_id': self.id}) class Meta: app_label = 'nadine' ordering = ['user__first_name', 'user__last_name'] get_latest_by = "last_modified"
class Member(models.Model): """A person who has used the space and may or may not have a monthly membership""" objects = MemberManager() user = models.ForeignKey(User, unique=True, blank=False, related_name="user") email2 = models.EmailField("Alternate Email", blank=True, null=True) phone = PhoneNumberField(blank=True, null=True) phone2 = PhoneNumberField("Alternate Phone", blank=True, null=True) address1 = models.CharField(max_length = 128, blank = True) address2 = models.CharField(max_length = 128, blank = True) city = models.CharField(max_length = 128, blank = True) state = models.CharField(max_length = 2, blank = True) zipcode = models.CharField(max_length = 5, blank = True) url_personal = models.URLField(blank=True, null=True) url_professional = models.URLField(blank=True, null=True) url_facebook = models.URLField(blank=True, null=True) url_twitter = models.URLField(blank=True, null=True) url_biznik = models.URLField(blank=True, null=True) url_linkedin = models.URLField(blank=True, null=True) url_aboutme = models.URLField(blank=True, null=True) url_github = models.URLField(blank=True, null=True) gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default="U") howHeard = models.ForeignKey(HowHeard, blank=True, null=True) #referred_by = models.ForeignKey(User, verbose_name="Referred By", related_name="referred_by", blank=True, null=True) industry = models.ForeignKey(Industry, blank=True, null=True) neighborhood = models.ForeignKey(Neighborhood, blank=True, null=True) has_kids = models.NullBooleanField(blank=True, null=True) self_employed = models.NullBooleanField(blank=True, null=True) company_name = models.CharField(max_length=128, blank=True, null=True) promised_followup = models.DateField(blank=True, null=True) last_modified = models.DateField(auto_now=True, editable=False) notes = models.TextField(blank=True, null=True) photo = models.ImageField(upload_to='member_photo', blank=True, null=True) tags = TaggableManager(blank=True) valid_billing = models.BooleanField(default=False) @property def first_name(self): return smart_str(self.user.first_name) @property def last_name(self): return smart_str(self.user.last_name) @property def email(self): return self.user.email @property def full_name(self): return '%s %s' % (smart_str(self.user.first_name), smart_str(self.user.last_name)) def natural_key(self): return [self.user.id] def all_bills(self): """Returns all of the open bills, both for this member and any bills for other members which are marked to be paid by this member.""" return Bill.objects.filter(models.Q(member=self) | models.Q(paid_by=self)).order_by('-created') def open_bills(self): """Returns all of the open bills, both for this member and any bills for other members which are marked to be paid by this member.""" return Bill.objects.filter(models.Q(member=self) | models.Q(paid_by=self)).filter(transactions=None).order_by('created') def open_bills_amount(self): """Returns the amount of all of the open bills, both for this member and any bills for other members which are marked to be paid by this member.""" return Bill.objects.filter(models.Q(member=self) | models.Q(paid_by=self)).filter(transactions=None).aggregate(models.Sum('amount'))['amount__sum'] def pay_bills_form(self): from forms import PayBillsForm return PayBillsForm(initial={'member_id':self.id, 'amount':self.open_bills_amount }) def last_bill(self): """Returns the latest Bill, or None if the member has not been billed. NOTE: This does not (and should not) return bills which are for other members but which are to be paid by this member.""" bills = Bill.objects.filter(member=self) if len(bills) == 0: return None return bills[0] def last_membership(self): """Returns the latest membership, even if it has an end date, or None if none exists""" memberships = Membership.objects.filter(member=self).order_by('-start_date', 'end_date')[0:] if memberships == None or len(memberships) == 0: return None return memberships[0] def active_membership(self): for membership in Membership.objects.filter(member=self).order_by('-start_date', 'end_date'): if membership.is_active(): return membership return None def activity_this_month(self, test_date=date.today()): membership = self.active_membership() if not membership: # Not a member return None if membership.guest_of: # Return host's activity host = membership.guest_of return host.activity_this_month() month_start = membership.prev_billing_date(test_date) print month_start activity = [] for m in [self] + self.guests(): for l in DailyLog.objects.filter(member=m, payment='Bill', visit_date__gte=month_start): activity.append(l) for l in DailyLog.objects.filter(guest_of=self, payment='Bill', visit_date__gte=month_start): activity.append(l) return activity def paid_count(self): return DailyLog.objects.filter(member=self, payment='Bill').count() def first_visit(self): if Membership.objects.filter(member=self).count() > 0: return Membership.objects.filter(member=self).order_by('start_date')[0].start_date else: if DailyLog.objects.filter(member=self).count() > 0: return DailyLog.objects.filter(member=self).order_by('visit_date')[0].visit_date else: return None def host_daily_logs(self): return DailyLog.objects.filter(guest_of=self).order_by('-visit_date') def member_since(self): first = self.first_visit() if first == None: return None return timezone.localtime(timezone.now()) - datetime.combine(first, time(0,0,0)) def last_visit(self): if DailyLog.objects.filter(member=self).count() > 0: return DailyLog.objects.filter(member=self).latest('visit_date').visit_date else: if Membership.objects.filter(member=self, end_date__isnull=False).count() > 0: return Membership.objects.filter(member=self, end_date__isnull=False).latest('end_date').end_date else: return None def membership_type(self): # First check for existing monthly memberships = Membership.objects.filter(member=self) if self.last_membership(): last_monthly = self.last_membership() if last_monthly.end_date == None or last_monthly.end_date > timezone.now().date(): return last_monthly.membership_plan else: return "Ex" + str(last_monthly.membership_plan) # Now check daily logs drop_ins = DailyLog.objects.filter(member=self).count() if drop_ins == 0: return "New User" elif drop_ins == 1: return "First Day" else: return "Drop-in" def is_active(self): m = self.active_membership() return m is not None def has_desk(self): m = self.active_membership() if not m: return False if m.is_active(): return m.has_desk return False def is_guest(self): m = self.active_membership() if m and m.is_active() and m.guest_of: return m.guest_of return None def has_valid_billing(self): host = self.is_guest() if host: return host.has_valid_billing() return self.valid_billing def guests(self): guests = [] for membership in Membership.objects.filter(guest_of=self): if membership.is_active(): guests.append(membership.member) return guests def deposits(self): return SecurityDeposit.objects.filter(member=self) def onboard_tasks_status(self): """ Returns an array of tuples: (Onboard_Task, Onboard_Task_Completed) for this member. Onboard_Task_Completed may be None. """ return [(task, Onboard_Task_Completed.objects.for_member(task, self)) for task in Onboard_Task.objects.all()] def onboard_tasks_to_complete(self): return Onboard_Task.objects.count() - Onboard_Task_Completed.objects.filter(member=self).count() def qualifies_for_exit_tasks(self): last_log = self.last_membership() if not last_log or last_log.end_date == None: return False return last_log.end_date < timezone.now().date() def exit_tasks_status(self): """ Returns an array of tuples: (ExitTask, ExitTaskCompleted) for this member. ExitCompleted may be None. """ if not self.qualifies_for_exit_tasks(): return [] return [(task, ExitTaskCompleted.objects.for_member(task, self)) for task in ExitTask.objects.all()] def exit_tasks_to_complete(self): if not self.qualifies_for_exit_tasks(): return 0 return ExitTask.objects.count() - ExitTaskCompleted.objects.filter(member=self).count() def __str__(self): return '%s %s' % (smart_str(self.user.first_name), smart_str(self.user.last_name)) @models.permalink def get_absolute_url(self): return ('staff.views.member_detail', (), { 'member_id':self.id }) class Meta: ordering = ['user__first_name', 'user__last_name'] get_latest_by = "last_modified"
class Invoice(models.Model): invoice_date = models.DateTimeField(auto_now_add=True) paid_date = models.DateTimeField(blank=True, null=True) sent_date = models.DateTimeField(blank=True, null=True) customer = models.ForeignKey('customer.Customer', related_name='invoiced') owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, related_name='owns') shorturl = models.CharField(max_length=10, editable=False) #duplicate fields from customer so that we can archive the invoice. # If the customers' info changes in the future, we don't want all old invoices to update too first_name = models.CharField(max_length=30, blank=True, null=True) last_name = models.CharField(max_length=30, blank=True, null=True) street1 = models.CharField(max_length=60, verbose_name="Address 1", editable=False) street2 = models.CharField(max_length=60, blank=True, verbose_name="Address 2", editable=False) city = models.CharField(max_length=30, editable=False) state = USPostalCodeField() zip_code = models.CharField(max_length=10, verbose_name="Zip Code") email = models.CharField(max_length=65, editable=False) phone = PhoneNumberField() def __unicode__(self): return "%s #%d (%s)" % (self.customer.get_full_name(), self.pk, self.shorturl) def _get_grand_total(self): total = Decimal("0.0") taxable = Decimal("0.0") for li in self.lineitems.all(): if li.taxable: taxable += li.total else: total += li.total taxable = taxable * Decimal(settings.TAX_RATE) total += taxable return total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) def _get_taxable_total(self): total = Decimal("0.0") for li in self.lineitems.all(): if li.taxable: total += li.total return total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) def _get_total_tax(self): total = Decimal("0.0") inv_tax_rate = Decimal(settings.TAX_RATE) - Decimal("1.0") for li in self.lineitems.all(): if li.taxable: total += Decimal(li.unit_price * li.quantity) total = total * inv_tax_rate return total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) def _get_sub_total(self): total = Decimal("0.0") for li in self.lineitems.all(): total += Decimal(li.unit_price * li.quantity) return total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) grand_total = property(_get_grand_total) total_tax = property(_get_total_tax) taxable_total = property(_get_taxable_total) sub_total = property(_get_sub_total) def _is_paid(self): return True if self.paid_date else False _is_paid.admin_order_field = 'paid_date' _is_paid.boolean = True _is_paid.short_description = 'Paid?' is_paid = property(_is_paid) def num_to_base56(self, num): #list of randomized, url-safe, unambiguous chars charList = 'x5d8j9A3BmCZDsEtFGwHpJuKLaMkNzPQnS6TvUhV2WfX4cYb7egiqRry' if num > 56: return self.num_to_base56(math.floor(int(num)/56)) + charList[int(num) % 56] else: return charList[int(num)] def save(self, force_insert=False, force_update=False): if self.shorturl is None or self.shorturl == '': notpk = int(time.mktime(datetime.datetime.now().timetuple())) urlStr = self.num_to_base56(notpk) while len(urlStr) < 5: urlStr = self.num_to_base56('0') + urlStr self.shorturl = urlStr if self.pk is None: linked_customer = self.customer self.first_name = linked_customer.first_name self.last_name = linked_customer.last_name self.street1 = linked_customer.street1 self.street2 = linked_customer.street2 self.city = linked_customer.city self.state = linked_customer.state self.zip_code = linked_customer.zip_code self.phone = linked_customer.phone self.email = linked_customer.email super(Invoice, self).save(force_insert, force_update)
class Organization(models.Model): name = models.CharField(max_length=255, unique=True) uuid = models.UUIDField(unique=True, db_index=True, default=uuid4) address = models.CharField(max_length=255, null=True, blank=True) address1 = models.CharField(max_length=255, null=True, blank=True) city = models.CharField(max_length=255, null=True, blank=True) state = models.CharField(max_length=80, null=True, blank=True) zip = models.CharField(max_length=25, null=True, blank=True) country = models.CharField(max_length=80, null=True, blank=True, default='US') fax = PhoneNumberField(null=True, blank=True) phone = PhoneNumberField(null=True, blank=True) locale = models.CharField(max_length=2, null=True, blank=True, default='en') removed = models.DateField(null=True) timezone = TimeZoneField(default=settings.TIME_ZONE) website = models.URLField(null=True, blank=True) is_active = models.BooleanField(default=True) objects = ActiveManager() class Meta: ordering = ['name'] db_table = 'kala_companies' def set_active(self, active): self.is_active = active for person in self.user_set.all(): person.set_active(active) for project in Project.objects.filter(organization=self): project.set_active(active) if not self.is_active: self.removed = datetime.date.today() self.save() def get_projects(self, user): if user.is_superuser: return Project.objects.active().filter(organization=self) else: return Project.objects.active().filter( id__in=user.get_projects().values_list('id', flat=True), organization=self) def __str__(self): return self.name def can(self, user, _permissions): if user.is_superuser: return True return True if OrganizationPermission.objects.filter( permission__in=Permission.objects.filter( codename__in=_permissions, content_type__app_label='organizations'), user=user, organization=self).exists() else False def can_create(self, user): return self.can(user, ['can_create', 'can_invite', 'can_manage']) def can_invite(self, user): return self.can(user, ['can_invite', 'can_manage']) def can_manage(self, user): return self.can(user, ['can_manage']) def add_permission(self, user, permission): OrganizationPermission.objects.get_or_create( permission=Permission.objects.get( codename=permission, content_type__app_label='organizations'), user=user, organization=self) def add_create(self, user): self.add_permission(user, 'can_create') def add_invite(self, user): self.add_permission(user, 'can_invite') def add_manage(self, user): self.add_permission(user, 'can_manage') def delete_permission(self, user, permission): try: OrganizationPermission.objects.get( permission=Permission.objects.get( codename=permission, content_type__app_label='organizations'), user=user, organization=self).delete() except OrganizationPermission.DoesNotExist: return False def delete_create(self, user): self.delete_permission(user, 'can_create') def delete_invite(self, user): self.delete_permission(user, 'can_invite') def delete_manage(self, user): self.delete_permission(user, 'can_manage')
class UserProfile(models.Model): MAX_PHOTO_SIZE = 1024 user = models.OneToOneField(User, blank=False, related_name="profile") phone = PhoneNumberField(blank=True, null=True) phone2 = PhoneNumberField("Alternate Phone", blank=True, null=True) address1 = models.CharField(max_length=128, blank=True) address2 = models.CharField(max_length=128, blank=True) city = models.CharField(max_length=128, blank=True) state = models.CharField(max_length=2, blank=True) zipcode = models.CharField(max_length=16, blank=True) bio = models.TextField(blank=True, null=True) public_profile = models.BooleanField(default=False) gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default="U") howHeard = models.ForeignKey(HowHeard, blank=True, null=True) #referred_by = models.ForeignKey(User, verbose_name="Referred By", related_name="referral", blank=True, null=True) industry = models.ForeignKey(Industry, blank=True, null=True) neighborhood = models.ForeignKey(Neighborhood, blank=True, null=True) has_kids = models.NullBooleanField(blank=True, null=True) self_employed = models.NullBooleanField(blank=True, null=True) last_modified = models.DateField(auto_now=True, editable=False) photo = models.ImageField(upload_to=user_photo_path, blank=True, null=True) tags = TaggableManager(blank=True) valid_billing = models.NullBooleanField(blank=True, null=True) websites = models.ManyToManyField(Website, blank=True) @property def url_personal(self): return self.websites.filter(url_type__name="personal").first() @property def url_professional(self): return self.websites.filter(url_type__name="professional").first() @property def url_facebook(self): return self.websites.filter(url_type__name="facebook").first() @property def url_twitter(self): return self.websites.filter(url_type__name="twitter").first() @property def url_linkedin(self): return self.websites.filter(url_type__name="linkedin").first() @property def url_github(self): return self.websites.filter(url_type__name="github").first() def save_url(self, url_type, url_value): if url_type and url_value: w = self.websites.filter(url_type__name=url_type).first() if w: w.url = url_value w.save() else: t = URLType.objects.get(name=url_type) self.websites.create(url_type=t, url=url_value) def active_organization_memberships(self, on_date=None): if not on_date: on_date = timezone.now().date() future = Q(end_date__isnull=True) unending = Q(end_date__gte=on_date) return self.user.organizationmember_set.filter(start_date__lte=on_date).filter(future | unending) def past_organization_memberships(self, on_date=None): if not on_date: on_date = timezone.now().date() return self.user.organizationmember_set.filter(end_date__lte=on_date) def active_organizations(self, on_date=None): active = self.active_organization_memberships(on_date) return Organization.objects.filter(id__in=active.values('organization')) def all_bills(self): """Returns all of the open bills, both for this user and any bills for other members which are marked to be paid by this member.""" return Bill.objects.filter(models.Q(user=self.user) | models.Q(paid_by=self.user)).order_by('-bill_date') def open_bills(self): """Returns all of the open bills, both for this user and any bills for other members which are marked to be paid by this member.""" return Bill.objects.filter(models.Q(user=self.user) | models.Q(paid_by=self.user)).filter(transactions=None).order_by('bill_date') def open_bill_amount(self): total = 0 for b in self.open_bills(): total = total + b.amount return total def open_bills_amount(self): """Returns the amount of all of the open bills, both for this member and any bills for other members which are marked to be paid by this member.""" return Bill.objects.filter(models.Q(user=self.user) | models.Q(paid_by=self.user)).filter(transactions=None).aggregate(models.Sum('amount'))['amount__sum'] def open_xero_invoices(self): from nadine.utils.xero_api import XeroAPI xero_api = XeroAPI() return xero_api.get_open_invoices(self.user) def pay_bills_form(self): from nadine.forms import PayBillsForm return PayBillsForm(initial={'username': self.user.username, 'amount': self.open_bills_amount}) def last_bill(self): """Returns the latest Bill, or None if the member has not been billed. NOTE: This does not (and should not) return bills which are for other members but which are to be paid by this member.""" bills = Bill.objects.filter(user=self.user) if len(bills) == 0: return None return bills[0] def membership_history(self): return Membership.objects.filter(user=self.user).order_by('-start_date', 'end_date') def membership_on_date(self, day): return Membership.objects.filter(user=self.user, start_date__lte=day).filter(Q(end_date__isnull=True) | Q(end_date__gte=day)).first() def last_membership(self): """Returns the latest membership, even if it has an end date, or None if none exists""" memberships = Membership.objects.filter(user=self.user).order_by('-start_date', 'end_date')[0:] if memberships == None or len(memberships) == 0: return None return memberships[0] def active_membership(self): for membership in self.membership_history(): if membership.is_active(): return membership return None def membership_for_day(self, day): return Membership.objects.active_memberships(target_date=day).filter(user=self.user).first() def activity_this_month(self, test_date=None): if not test_date: test_date = date.today() membership = self.active_membership() if membership: if membership.paid_by: # Return host's activity host = membership.paid_by return host.profile.activity_this_month() month_start = membership.prev_billing_date(test_date) else: # Just go back one month from this date since there isn't a membership to work with month_start = test_date - MonthDelta(1) activity = [] for h in [self.user] + self.guests(): for l in CoworkingDay.objects.filter(user=h, payment='Bill', visit_date__gte=month_start).exclude(paid_by__isnull=False): activity.append(l) for l in CoworkingDay.objects.filter(paid_by=self.user, payment='Bill', visit_date__gte=month_start): activity.append(l) return activity def activity(self): return CoworkingDay.objects.filter(user=self.user) def paid_count(self): return self.activity().filter(payment='Bill').count() def first_visit(self): if Membership.objects.filter(user=self.user).count() > 0: return Membership.objects.filter(user=self.user).order_by('start_date')[0].start_date else: if CoworkingDay.objects.filter(user=self.user).count() > 0: return CoworkingDay.objects.filter(user=self.user).order_by('visit_date')[0].visit_date else: return None def all_emails(self): # Done in two queries so that the primary email address is always on top. primary = self.user.emailaddress_set.filter(is_primary=True) non_primary = self.user.emailaddress_set.filter(is_primary=False) return list(primary) + list(non_primary) def non_primary_emails(self): return self.user.emailaddress_set.filter(is_primary=False) def duration(self): return relativedelta(timezone.now().date(), self.first_visit()) def duration_str(self, include_days=False): retval = "" delta = self.duration() if delta.years: if delta.years == 1: retval = "1 year" else: retval = "%d years" % delta.years if delta.months or delta.days: retval += " and " if delta.months: if delta.months == 1: retval += "1 month" else: retval += "%d months" % delta.months if include_days and delta.days: retval += " and " if include_days and delta.days: if delta.days == 1: retval += "1 day" else: retval += "%d days" % delta.days return retval def hosted_days(self): return CoworkingDay.objects.filter(paid_by=self.user).order_by('-visit_date') def has_file_uploads(self): return FileUpload.objects.filter(user=self.user).count() > 0 def has_file(self, doc_type): return FileUpload.objects.filter(user=self.user, document_type=doc_type).count() > 0 def file_uploads(self): files = {} # Only want the latest one if there are duplicates for f in FileUpload.objects.filter(user=self.user).order_by('uploadTS').reverse(): files[f.name] = f return files.values() def files_by_type(self): files = {} # Only want the latest one if there are duplicates for f in FileUpload.objects.filter(user=self.user).order_by('uploadTS').reverse(): files[f.document_type] = f return files def alerts_by_key(self, include_resolved=False): from nadine.models.alerts import MemberAlert if include_resolved: alerts = MemberAlert.objects.filter(user=self.user) else: alerts = MemberAlert.objects.filter(user=self.user, resolved_ts__isnull=True, muted_ts__isnull=True) alerts_by_key = {} for alert in alerts: if alert.key in alerts_by_key: alerts_by_key[alert.key].append(alert) else: alerts_by_key[alert.key] = [alert] return alerts_by_key def alerts(self): from nadine.models.alerts import MemberAlert return MemberAlert.objects.filter(user=self.user).order_by('-created_ts') def open_alerts(self): from nadine.models.alerts import MemberAlert return MemberAlert.objects.filter(user=self.user, resolved_ts__isnull=True, muted_ts__isnull=True).order_by('-created_ts') def resolve_alerts(self, alert_key, resolved_by=None): logger.debug("resolve_alerts: user=%s, key=%s, resolved_by=%s" % (self.user, alert_key, resolved_by)) from nadine.models.alerts import MemberAlert alerts = MemberAlert.objects.filter(user=self.user, key=alert_key, resolved_ts__isnull=True, muted_ts__isnull=True).order_by('-created_ts') if alerts: for alert in alerts: alert.resolve(resolved_by) def member_since(self): first = self.first_visit() if first == None: return None return timezone.localtime(timezone.now()) - datetime.combine(first, time(0, 0, 0)) def last_visit(self): if CoworkingDay.objects.filter(user=self.user).count() > 0: return CoworkingDay.objects.filter(user=self.user).latest('visit_date').visit_date else: if Membership.objects.filter(user=self.user, end_date__isnull=False).count() > 0: return Membership.objects.filter(user=self.user, end_date__isnull=False).latest('end_date').end_date else: return None def membership_type(self): active_membership = self.active_membership() if active_membership: return active_membership.membership_plan else: last_monthly = self.last_membership() if last_monthly: return "Ex" + str(last_monthly.membership_plan) # Now check daily logs drop_ins = CoworkingDay.objects.filter(user=self.user).count() if drop_ins == 0: return "New User" elif drop_ins == 1: return "First Day" else: return "Drop-in" def is_active(self): m = self.active_membership() return m is not None def has_desk(self, target_date=None): if not target_date: target_date = timezone.now().date() m = self.membership_on_date(target_date) return m and m.has_desk def is_guest(self): m = self.active_membership() if m and m.is_active() and m.paid_by: return m.paid_by return None def guests(self): guests = [] for membership in Membership.objects.filter(paid_by=self.user): if membership.is_active(): guests.append(membership.user) return guests def has_valid_billing(self): host = self.is_guest() if host and host != self.user: return host.profile.has_valid_billing() if self.valid_billing is None: logger.debug("%s: Null Valid Billing" % self) if self.has_new_card(): logger.debug("%s: Found new card. Marking billing valid." % self) self.valid_billing = True self.save() else: self.valid_billing = False return self.valid_billing def has_billing_profile(self): try: api = PaymentAPI() if api.get_customers(self.user.username): return True except Exception: pass return False def has_new_card(self): # Check for a new card. WARNING: kinda expensive try: api = PaymentAPI return api.has_new_card(self.user.username) except Exception: pass return False # TODO - Remove def deposits(self): return SecurityDeposit.objects.filter(user=self.user) def __str__(self): return '%s %s' % (smart_str(self.user.first_name), smart_str(self.user.last_name)) def auto_bill_enabled(self): if not hasattr(settings, 'USA_EPAY_KEY'): return None api = PaymentAPI() return api.auto_bill_enabled(self.user.username) # TODO - Remove def member_notes(self): return MemberNote.objects.filter(user=self.user) # TODO - Remove def special_days(self): return SpecialDay.objects.filter(user=self.user) def membership_days(self): total_days = 0 for membership in self.membership_history(): end = membership.end_date if not end: end = timezone.now().date() diff = end - membership.start_date days = diff.days total_days = total_days + days return total_days def average_bill(self): from nadine.models.payment import Bill bills = Bill.objects.filter(user=self.user) if bills: bill_totals = 0 for b in bills: bill_totals = bill_totals + b.amount return bill_totals / len(bills) return 0 def is_manager(self): if self.user.is_staff: return True if hasattr(settings, 'TEAM_MEMBERSHIP_PLAN'): management_plan = MembershipPlan.objects.filter(name=settings.TEAM_MEMBERSHIP_PLAN).first() if management_plan: active_membership = self.active_membership() if active_membership: return active_membership.membership_plan == management_plan return False class Meta: app_label = 'nadine' ordering = ['user__first_name', 'user__last_name'] get_latest_by = "last_modified"
class TrustAnchorCertificate(models.Model): owner = models.ForeignKey(User) status = models.CharField(max_length=10, default="incomplete", choices=STATUS_CHOICES, editable=False) sha256_digest = models.CharField( max_length=64, default="", blank=True, ) sha1_fingerprint = models.CharField( max_length=64, default="", blank=True, ) #editable=False) serial_number = models.CharField( max_length=64, default=-01, blank=True, ) #editable=False) domain = models.CharField( max_length=512, default="", help_text= "We recommend using a top-level domain here (e.g. example.com)") common_name = models.CharField( max_length=512, default="", help_text="Always set this to the same value as domain") dns = models.CharField( verbose_name="DNS", max_length=512, default="", help_text= """We recommend using a top-level domain here (e.g. example.com). This field should match the email field exactly. """) email = models.CharField( max_length=512, default="", help_text= """We recommend using a top-level domain here (e.g. example.com). This field should match the DNS field exactly.""" ) rsa_keysize = models.IntegerField(max_length=4, default=2048, choices=RSA_KEYSIZE_CHOICES) country = models.CharField(max_length=2, default="US") state = models.CharField( blank=True, max_length=2, choices=US_STATES, ) city = models.CharField( max_length=64, help_text="No slashes. Letters, numbers, and dashes are okay. ") organization = models.CharField( max_length=64, help_text="No slashes. Letters, numbers, and dashes are okay. ") private_key_path = models.CharField(max_length=1024, default="", blank=True) public_key_path = models.CharField(max_length=1024, default="", blank=True) completed_dir_path = models.CharField(max_length=1024, default="", blank=True) private_expire_days = models.IntegerField(max_length=1024, default="1", blank=True) private_zip_name = models.CharField(max_length=1024, default="", blank=True) presigned_zip_url = models.CharField(max_length=1024, default="", blank=True) presigned_zip_s3 = models.CharField(max_length=1024, default="", blank=True) public_cert_pem_url = models.CharField(max_length=1024, default="", blank=True) public_cert_pem_s3 = models.CharField(max_length=1024, default="", blank=True) public_cert_der_url = models.CharField(max_length=1024, default="", blank=True) public_cert_der_s3 = models.CharField(max_length=1024, default="", blank=True) public_cert_status_url = models.CharField(max_length=1024, default="", blank=True) public_cert_status_sha1_url = models.CharField(max_length=1024, default="", blank=True) public_cert_x5c_url = models.CharField(max_length=1024, default="", blank=True) revoke = models.BooleanField(default=False) expired = models.BooleanField(default=False, editable=False) verified = models.BooleanField() verified_message_sent = models.BooleanField() rcsp_response = models.TextField(max_length=512, blank=True, default="") notes = models.TextField(max_length=1024, blank=True, default="") npi = models.CharField(max_length=20, default="", blank=True) contact_first_name = models.CharField(max_length=100, default="") contact_last_name = models.CharField(max_length=100, default="") contact_email = models.EmailField() contact_mobile_phone = PhoneNumberField(max_length=15, blank=True) contact_land_phone = PhoneNumberField(max_length=15, blank=True) contact_fax = PhoneNumberField(max_length=15, blank=True) expiration_date = models.DateField(blank=True, editable=False) expire_days = models.IntegerField(max_length=5, default=365, choices=EXPIRE_CHOICES) creation_date = models.DateField(auto_now_add=True) def __unicode__(self): return '%s (%s) Status=%s, Created %s.' % ( self.domain, self.serial_number, self.status, self.creation_date) class Meta: get_latest_by = "creation_date" ordering = ('-creation_date', ) def save(self, **kwargs): if not self.sha256_digest and self.revoke == False: """I'm a new certificate""" today = datetime.date.today() self.expiration_date = today + datetime.timedelta( days=self.expire_days) result = create_trust_anchor_certificate( common_name=self.common_name, email=self.email, dns=self.dns, expires=self.expire_days, organization=self.organization, city=self.city, state=self.state, country=self.country, rsakey=self.rsa_keysize, user=self.owner.username) self.sha256_digest = result['sha256_digest'] self.serial_number = result['serial_number'] self.sha1_fingerprint = result['sha1_fingerprint'] self.notes = result['notes'] self.private_zip_name = result['anchor_zip_download_file_name'] self.status = result['status'] self.private_key_path = result['private_key_path'] self.public_key_path = result['public_key_path'] self.completed_dir_path = result['completed_dir_path'] #send the verifier an email notification msg = """ <html> <head> </head> <body> A new Direct Trust Anchor was created by %s and requires your review. Here is a link for the domain %s: <ul> <li><a href="/admin/certificates/trustanchorcertificate/%s">%s</a></li> </ul> </body> </html> """ % (self.organization, self.domain, self.id, self.domain) if settings.SEND_CA_EMAIL: msg = EmailMessage( '[DirectCA]A new Trust Anchor certificate requires verification', msg, settings.EMAIL_HOST_USER, [ settings.CA_VERIFIER_EMAIL, ]) msg.content_subtype = "html" # Main content is now text/html msg.send() # Create the CRL config file crl_result = create_crl_conf( common_name=self.common_name, email=self.email, dns=self.dns, anchor_dns=self.dns, expires=self.expire_days, organization=self.organization, city=self.city, state=self.state, country=self.country, rsakey=self.rsa_keysize, user=self.owner.username, public_key_path=result['public_key_path'], private_key_path=result['private_key_path'], completed_anchor_dir=result['completed_dir_path']) return super(TrustAnchorCertificate, self).save(**kwargs) if self.verified and not self.verified_message_sent and \ self.status in ('unverified', 'good'): """This is the verify routine""" self.status = "good" # Get the response rcsp_result = write_verification_message( self.serial_number, self.common_name, "good", self.sha1_fingerprint, ) #Write it to db self.rcsp_response = rcsp_result fn = "%s.json" % (self.serial_number) #Write it to file fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(rcsp_result)) f.close() #Upload the RCSP file to S3 if settings.USE_S3: s = SimpleS3() self.public_cert_status_url = s.store_in_s3( fn, fp, bucket=settings.RCSP_BUCKET, public=True) self.public_cert_status_url = s.build_pretty_url( self.public_cert_status_url, settings.RCSP_BUCKET) #"JOSE -------------------------------------------------------------" #get all the files certfilelist = [settings.CA_PUBLIC_CERT, self.public_key_path] fn = "%s-chain.pem" % (self.dns) chained_cert_path = os.path.join(self.completed_dir_path, fn) certlist = chain_keys_in_list(chained_cert_path, certfilelist) #write the json x5c_json = write_x5c_message(self.email, certlist) # set the filename ------------------------------------------------ fn = "%s-x5c.json" % (self.serial_number) # Write it to file ------------------------------------------------ fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(x5c_json)) f.close() #Upload the x5c file to S3 s = SimpleS3() if settings.USE_S3: key = "x5c/" + fn self.public_cert_x5c_url = s.store_in_s3( key, fp, bucket=settings.X5C_BUCKET, public=True) self.public_cert_x5c_url = s.build_pretty_url( self.public_cert_x5c_url, settings.X5C_BUCKET) #Calculate the SHA1 fingerprint & write it to a file digestsha1 = json.dumps(sha.sha1_from_filepath(fp), indent=4) fn = "%s-sha1.json" % (self.serial_number) fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(digestsha1)) f.close() #Upload the RCSP SHA! Digest to S3 if settings.USE_S3: self.public_cert_status_sha1_url = s.store_in_s3( fn, fp, bucket=settings.RCSPSHA1_BUCKET, public=True) self.public_cert_status_sha1_url = s.build_pretty_url( self.public_cert_status_sha1_url, settings.RCSPSHA1_BUCKET) #Upload the PEM and DER public certificates fn = "%s.pem" % (self.dns) key = "%s/%s/%s" % (self.owner.username, self.dns, fn) fp = os.path.join(self.completed_dir_path, fn) if settings.USE_S3: self.public_cert_pem_url = s.store_in_s3( key, fp, bucket=settings.PUBCERT_BUCKET, public=True) self.public_cert_pem_url = s.build_pretty_url( self.public_cert_pem_url, settings.PUBCERT_BUCKET) self.public_cert_pem_s3 = json.dumps({ "bucket": settings.PUBCERT_BUCKET, "key": key }) fn = "%s.der" % (self.dns) key = "%s/%s/%s" % (self.owner.username, self.dns, fn) fp = os.path.join(self.completed_dir_path, fn) if settings.USE_S3: self.public_cert_der_url = s.store_in_s3( key, fp, bucket=settings.PUBCERT_BUCKET, public=True) self.public_cert_der_url = s.build_pretty_url( self.public_cert_der_url, settings.PUBCERT_BUCKET) self.public_cert_der_s3 = json.dumps({ "bucket": settings.PUBCERT_BUCKET, "key": key }) #Send the zip file and expire in one week fn = self.private_zip_name fp = os.path.join(self.completed_dir_path, self.private_zip_name) key = "%s/%s/%s" % (self.owner.username, self.dns, fn) if settings.USE_S3: self.presigned_zip_url = s.store_in_s3( key, fp, bucket=settings.PUBCERT_BUCKET, public=True) self.presigned_zip_url = s.build_pretty_url( self.presigned_zip_url, settings.PUBCERT_BUCKET) #We dont need this for trust anchos since there is no private key give. #self.presigned_zip_url = s.get_presignedurl(key, bucket = settings.PRIVCERT_BUCKET) self.presigned_zip_s3 = json.dumps({ "bucket": settings.PUBCERT_BUCKET, "key": key }) """ Mark th certificate as verified """ self.verified = True #send the verification email. msg = """ <html> <head> </head> <body> Congratulations. Your trust anchor has for %s been verified. Here are some links to your public certificates and related status information. <ul> <li><a href="%s">PEM File - %s</a></li> <li><a href="%s">DER File - %s</a></li> <li><a href="%s">Status - %s</a></li> <li><a href="%s">Status SHA1 Digest - %s</a></li> <li><a href="%s">Certificate Chain in JOSE x5c Format - %s</a></li> </ul> </body> </html> """ % (self.domain, self.public_cert_pem_url, self.public_cert_pem_url, self.public_cert_der_url, self.public_cert_der_url, self.public_cert_status_url, self.public_cert_status_url, self.public_cert_status_sha1_url, self.public_cert_status_sha1_url, self.public_cert_x5c_url, self.public_cert_x5c_url) if settings.SEND_CA_EMAIL: msg = EmailMessage( '[DirectCA]Your Trust Anchor Certificate has been verified', msg, settings.EMAIL_HOST_USER, [self.owner.email, self.contact_email]) msg.content_subtype = "html" # Main content is now text/html msg.send() self.verified_message_sent = True if self.revoke and self.status != "revoked": self.status = "revoked" # Build the RCSP response # Get the status self.rcsp_response = write_verification_message( self.serial_number, self.common_name, "revoked", self.sha1_fingerprint, ) fn = "%s.json" % (self.serial_number) #Write it to file fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(self.rcsp_response)) f.close() #Upload the RCSP file to S3 if settings.USE_S3: s = SimpleS3() url = s.store_in_s3(fn, fp, bucket=settings.RCSP_BUCKET, public=True) #Calculate the SHA1 fingerprint & write it to a file digestsha1 = json.dumps(sha.sha1_from_filepath(fp), indent=4) fn = "%s-sha1.json" % (self.serial_number) fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(digestsha1)) f.close() if settings.USE_S3: #Upload the RCSP SHA! Digest to S3 url = s.store_in_s3(fn, fp, bucket=settings.RCSPSHA1_BUCKET, public=True) #Delete all the old files: #PEM, DIR, ZIP if self.presigned_zip_s3: s3info = json.loads(self.presigned_zip_s3) self.presigned_zip_url = s.delete_in_s3( s3info['bucket'], s3info['key'], ) if self.public_cert_der_s3: s3info = json.loads(self.public_cert_der_s3) self.public_cert_der_url = s.delete_in_s3( s3info['bucket'], s3info['key'], ) if self.public_cert_pem_s3: s3info = json.loads(self.public_cert_pem_s3) self.public_cert_pem_url = s.delete_in_s3( s3info['bucket'], s3info['key'], ) #revoke the cert revoke(self) super(TrustAnchorCertificate, self).save(**kwargs) def delete(self, **kwargs): self.revoked = True self.status = "revoked" # Build the RCSP response rcsp_result = write_verification_message( self.serial_number, self.common_name, "revoked", self.sha1_fingerprint, ) #Write it to db self.rcsp_response = rcsp_result fn = "%s.json" % (self.serial_number) #Write it to file fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(rcsp_result)) f.close() #Upload the RCSP file to S3 s = SimpleS3() if settings.USE_S3: url = s.store_in_s3(fn, fp, bucket=settings.RCSP_BUCKET, public=True) #Calculate the SHA1 fingerprint & write it to a file digestsha1 = json.dumps(sha.sha1_from_filepath(fp), indent=4) fn = "%s-sha1.json" % (self.serial_number) fp = os.path.join(self.completed_dir_path, fn) f = open(fp, "w") f.write(str(digestsha1)) f.close() #Upload the RCSP SHA! Digest to S3 if settings.USE_S3: url = s.store_in_s3(fn, fp, bucket=settings.RCSPSHA1_BUCKET, public=True) #Revoke the cert. revoke(self) super(TrustAnchorCertificate, self).save(**kwargs)