class Click2Call_Log(models.Model): callid = models.CharField(max_length=34) caller = models.ForeignKey(MHLUser, related_name="click2call_log_caller") caller_number = MHLPhoneNumberField(blank=True) called_user = models.ForeignKey(MHLUser, blank=True, null=True, related_name="click2call_log_called") called_number = MHLPhoneNumberField(blank=True) source = models.CharField(max_length=3, choices=CLICK2CALL_SOURCES) connected = models.BooleanField(default=False) current_site = models.ForeignKey(Site, null=True, blank=True, related_name="click2caller_current_site") timestamp = models.DateTimeField(auto_now=True, editable=False) def save(self, *args, **kwargs): # if new record and current_site is defined and not 0, we add count to siteAnalytics if ((not self.id) and (self.current_site > 0)): try: siteStat = SiteAnalytics.objects.get(dateoflog=datetime.date.today(), site=self.current_site) except SiteAnalytics.DoesNotExist: siteStat = SiteAnalytics(dateoflog=datetime.date.today(), site=self.current_site, countPage=0, countMessage=0, countClick2Call=0) siteStat.save() siteStat.countClick2Call = F('countClick2Call') + 1 siteStat.save() super(Click2Call_Log, self).save(*args, **kwargs) class Meta: db_table = 'DoctorCom_click2call_log'
class MessageLog(models.Model): message = models.ForeignKey(MessageTemp, editable=False, related_name="messagelog_message") message_recipient = models.ForeignKey(MHLUser, editable=False, related_name="messagelog_recip") # Was the message successfully sent? success = models.BooleanField(editable=False) # The following field is for message confirmation numbers -- currently, # a set of numbers. Unfortunately, errors from the SMS service come back # as strings. If the message fails to be sent, we will store the error # in this field. Note that the following field is for Convergent Mobile SMS # messaging. confirmation = models.CharField(max_length=250, blank=True, editable=False) # Twilio SMS values twilio_sid = models.CharField(max_length=64, blank=True, editable=False, db_index=True) twilio_status = models.CharField(max_length=2, choices=TWILIO_SMS_STATUS_CHOICES) body_fragment = models.CharField(max_length=200, blank=True, editable=False) resend_of = models.ForeignKey('self', null=True) # Keep track of message re-sends via this field current_site = models.ForeignKey(Site, null=True, blank=True, related_name="messager_current_site") tx_number = MHLPhoneNumberField(editable=False) # we keep this copy since the Sender can be deleted or change their cell number rx_number = MHLPhoneNumberField(editable=False) # we keep this copy since the Recipient can be deleted or change their cell number tx_name = models.CharField(max_length=50, editable=False) # '' or change their name rx_name = models.CharField(max_length=50, editable=False) # '' or change their name # Timestamp for when this message send is attempted. timestamp = models.DateTimeField(auto_now_add=True, editable=False) def save(self, *args, **kwargs): # if new record and current_site is defined and not 0, we add count to siteAnalytics if ((not self.id) and (self.current_site > 0)): try: siteStat = SiteAnalytics.objects.get(dateoflog=datetime.date.today(), site=self.current_site) except SiteAnalytics.DoesNotExist: siteStat = SiteAnalytics(dateoflog=datetime.date.today(), site=self.current_site, countPage=0, countMessage=0, countClick2Call=0) siteStat.save() siteStat.countMessage = F('countMessage') + 1 siteStat.save() super(MessageLog, self).save(*args, **kwargs) def __unicode__(self): return "Message fragment from %s to %s" % (self.tx_name, self.rx_name) class Meta: db_table = 'DoctorCom_messagelog'
class SenderLookup(models.Model): # "Owner" of this lookup table entry -- original outbound message sender user = models.ForeignKey( MHLUser, related_name="User whose mobile phone this mapping belongs to") # Person/number to which this mapping points -- inbound reply messages will # be from this person mapped_user = models.ForeignKey( MHLUser, null=True, related_name='User for whom this mapping points to') # The number that the mapping uses. number = MHLPhoneNumberField(blank=True, db_index=True) timestamp = models.DateTimeField(auto_now=True) def __unicode__(self): return 'SMS Map between %s %s and %s %s' % ( self.user.first_name, self.user.last_name, self.mapped_user.first_name, self.mapped_user.last_name) class Meta: unique_together = (('mapped_user', 'number'), )
class PagerLog(models.Model): pager = models.ForeignKey(MHLUser, related_name="pagerlog_pager", null=True) paged = models.ForeignKey(MHLUser, related_name="pagerlog_paged") current_site = models.ForeignKey(Site, null=True, blank=True, related_name="pager_current_site") callback = MHLPhoneNumberField() timestamp = models.DateTimeField(auto_now=True, editable=False) def save(self, *args, **kwargs): # if new record and current_site is defined and not 0, we add count to siteAnalytics if ((not self.id) and (self.current_site > 0)): try: siteStat = SiteAnalytics.objects.get(dateoflog=datetime.date.today(), site=self.current_site) except SiteAnalytics.DoesNotExist: siteStat = SiteAnalytics(dateoflog=datetime.date.today(), site=self.current_site, countPage=0, countMessage=0, countClick2Call=0) siteStat.save() siteStat.countPage = F('countPage') + 1 siteStat.save() super(PagerLog, self).save(*args, **kwargs) class Meta: db_table = 'DoctorCom_pagerlog'
class callLog(models.Model): """ Call session information. This keeps track of call attributes that are across all Twilio requests, such as caller, called, phone numbers, call duration, etc. """ # Callers caller_type = models.ForeignKey(ContentType, null=True, blank=True, related_name="callLog_callertype") caller_id = models.PositiveIntegerField(null=True, blank=True) mdcom_caller = generic.GenericForeignKey('caller_type', 'caller_id') # The following is *always* set, just so that we know what number the call # was made from. This should be the number the caller is coming from, *not* # the caller's DoctorCom number if they have one. This is intentional # duplicative information. caller_number = MHLPhoneNumberField(blank=True) # Calleds (recipients) called_type = models.ForeignKey(ContentType, null=True, blank=True, related_name="callLog_calledtype") called_id = models.PositiveIntegerField(null=True, blank=True) mdcom_called = generic.GenericForeignKey('called_type', 'called_id') # The following is *always* set, just so that we know what number the call # was received at. This is intentional duplicative information. This may # be the user's DoctorCom number. It's the number that the user dialed. called_number = MHLPhoneNumberField(blank=True) caller_spoken_name = models.TextField() callSID = models.CharField(max_length=64, unique=True, db_index=True) call_connected = models.BooleanField(default=False) # C2C Call Data # FIXME: The following import creates a circular import, which results in # import failures a plenty. For the time being, we're going to use an # Integer field that Django would be using. #from MHLogin.DoctorCom.models import Click2Call_Log #c2c_entry = models.ForeignKey(DCom.models.Click2Call_Log, null=True, blank=True) c2c_entry_id = models.IntegerField(null=True, blank=True) call_duration = models.IntegerField(null=True) #current_site = models.ForeignKey(Site, null=True, blank=True, # related_name="ivr_calllog_current_site") # Book keeping timestamp = models.DateTimeField(auto_now_add=True) call_source = models.CharField(max_length=2, choices=SOURCE_CHOICES)
class VMMessage(models.Model): # The "individual" whose voicemail this is. We are going with a generic # relation here so that messages can be associated with any object type, # be it a user, office, site, or something we haven't thought of. owner_type = models.ForeignKey(ContentType) owner_id = models.PositiveIntegerField() owner = generic.GenericForeignKey('owner_type', 'owner_id') callerID = models.CharField(max_length=64) # Path to a recording of the user speaking his/her voice OR the twilio ID of # the recording. Note that the distinction shall be that paths start with a # forward slash character, while the Twilio recordings start with the # letters 'RE'. # # Please do not get the recording field value directly. Rather, please use # the getRecordingUrl method instead so that the read_timestamp can be set # and so that the correct URL is given back, regardless of where the # recording is stored. recording = models.TextField() # Management Fields deleted = models.BooleanField(default=False) # We don't delete just mark as deleted read_flag = models.BooleanField(default=False) read_timestamp = models.DateTimeField(null=True) # Book keeping timestamp = models.DateTimeField(auto_now_add=True) #answering service answeringservice = models.BooleanField(default=False) callbacknumber = MHLPhoneNumberField(blank=True) def delete(self, *args, **kwargs): # Don't delete this message. Just mark it as deleted. self.deleted = True self.save() # Alternatively #raise Exception('Voicemail messages may not be deleted.') def sanitize(self): """ Sanitizes any personally identifying data from this object. NOTE THAT THIS DOES NOT SAVE THE OBJECT!""" if (not settings.DEBUG): raise Exception('You must be in DEBUG mode to use this function.') self.callerID = '8004664411' self.recording = 'http://api.twilio.com/2008-08-01/Accounts/'\ 'AC087cabfd0a453a05acceb2810c100f69/Recordings/REf8afc497f43d8e1e9bc229a415ebe100' def getRecordingUrl(self): if (not self.read_timestamp): self.read_timestamp = datetime.datetime.now() self.read_flag = True self.save() # TODO_RECORDING: # check to see if this is a Twilio URL or a cloud storage URL, then # modify it appropriately if needed. return self.recording
class OfficeStaff(models.Model): # Be aware that if you're creating a OfficeStaff user, you need to manually # populate this field with the correct User object! user = models.ForeignKey(MHLUser, null=True, blank=True, related_name='user_officestaff') office_phone = MHLPhoneNumberField(blank=True, help_text=PHONE_NUMBER_HELP_TEXT) office_address1 = models.CharField(max_length=200, blank=True) office_address2 = models.CharField(max_length=200, blank=True) office_city = models.CharField(max_length=200, blank=True) office_state = models.CharField(max_length=2, choices=STATE_CHOICES, blank=True) office_zip = models.CharField(max_length=10, blank=True) # 10 for zip and zip+4 # Django IntegerField too small for US phone number with area code. pager = MHLPhoneNumberField(blank=True, verbose_name=_('pager'), help_text=PHONE_NUMBER_HELP_TEXT) pager_extension = models.CharField(max_length=100, blank=True, verbose_name=_('pager extension')) pager_confirmed = models.BooleanField(default=False) sites = models.ManyToManyField(Site, null=True, blank=True, related_name='site_officestaff') current_site = models.ForeignKey(Site, null=True, blank=True, related_name='site_officestaff_current') practices = models.ManyToManyField(PracticeLocation, null=True, blank=True, related_name='practice_officestaff') current_practice = models.ForeignKey(PracticeLocation, null=True, blank=True, related_name='practice_officestaff_current') vm_config = generic.GenericRelation(VMBox_Config, content_type_field='owner_type', object_id_field='owner_id') caller_anssvc = models.CharField(max_length=2, choices=CALLER_ANSSVC_CHOICES, default='') objects = models.Manager() active_objects = ActiveManagedUser() #add by xlin 120914 search_objects = SearchOfficeStaff() class Meta: verbose_name = _("Office Staff") verbose_name_plural = _("Office Staff") ordering = ['user'] def __unicode__(self): return "%s %s" % (self.user.first_name, self.user.last_name)
class AnsSvcDLFailure(models.Model): """ Keeps track of which answering service messages have had message fetch failures. """ # Meta practice_id = models.IntegerField() # The PK for the practice error_timestamp = models.DateTimeField(auto_now_add=True) resolved = models.BooleanField(default=False, db_index=True) resolution_timestamp = models.DateTimeField(null=True, blank=True) failure_type = models.CharField(max_length=2, choices=FAILURE_CHOICES, default='DL') # Call Data post_data = models.TextField() call_sid = models.CharField(max_length=255, db_index=True) caller = models.CharField(max_length=20) called = models.CharField(max_length=20) recording_url = models.TextField(null=True, blank=True) callback_number = MHLPhoneNumberField() # Message objects # The PK for the original error notice message. error_message_uuid = models.CharField(max_length=32, blank=True, null=True) # The PK for the resolution notification message. resolution_message_uuid = models.CharField(max_length=32, blank=True, null=True) def init_from_post_data(self, post_data_dict): """ Initialize this object based on POST data from Twilio. This is simply a shortcut/utility method. Warning: post_data_dict is assumed to be sanitized! """ self.post_data = repr(dict(post_data_dict)) self.call_sid = post_data_dict['CallSid'] self.caller = post_data_dict['Caller'] self.called = post_data_dict['Called'] def save(self, *args, **kwargs): if (self.pk): raise Exception(_('Saving existing entries is disallowed. Update this ' 'object through its access methods.')) super(AnsSvcDLFailure, self).save(*args, **kwargs) log = AnsSvcDLFailureActivityLog(call_sid=self.call_sid, action='NEW') log.save() def mark_resolved(self, message): self.resolved = True self.resolution_timestamp = datetime.datetime.now() if (hasattr(message, 'uuid')): self.resolution_message_uuid = message.uuid else: self.resolution_message_uuid = message self.recording_url = None super(AnsSvcDLFailure, self).save()
class AccessNumber(models.Model): practice = models.ForeignKey(PracticeLocation) description = models.CharField(max_length=50, blank=True) number = MHLPhoneNumberField(blank=False)
class PracticeLocation(models.Model): """ Practice location object. Represents physical location of practice storing address, doctor.com number, and members. Members consist of professionals, office staff, and office managers, of which office managers have change rights. """ practice_name = models.CharField(max_length=100, unique=True,\ verbose_name=_('Name')) practice_address1 = models.CharField(max_length=200, blank=True, verbose_name=_("Address1")) practice_address2 = models.CharField(max_length=200, blank=True, verbose_name=_("Address2")) practice_phone = MHLPhoneNumberField(blank=True, verbose_name=_("Office Phone")) backline_phone = MHLPhoneNumberField(blank=True, verbose_name=_("Backline Phone")) practice_city = models.CharField(max_length=200, blank=True, verbose_name=_("City")) practice_state = models.CharField(max_length=2, choices=STATE_CHOICES, blank=True, verbose_name=_("State")) practice_zip = models.CharField(max_length=10, blank=True, verbose_name=_("Zip")) practice_lat = models.FloatField(blank=True) practice_longit = models.FloatField(blank=True) practice_photo = models.ImageField( upload_to="images/practicepics/%Y/%m/%d", blank=True, verbose_name=_("Logo"), help_text=_("Recommended size 100*30")) #DoctorCom Provisioned Phone Number and confirmation flag mdcom_phone = MHLPhoneNumberField(blank=True) mdcom_phone_sid = models.CharField(max_length=34, blank=True) #time zone where practice is located, matches values in pytz time_zone = models.CharField(max_length=64, blank=False, choices=TIME_ZONES_CHOICES, verbose_name=_('time zone')) #what call group is associated with this practice call_group = models.ForeignKey('MHLCallGroups.CallGroup', null=True, blank=True) #as we develop more functionality we will put products subscribed, billing, tos etc here #columns needed by answering service pin = models.CharField(max_length=120, blank=True) name_greeting = models.TextField(blank=True) greeting_closed = models.TextField(blank=True) greeting_lunch = models.TextField(blank=True) skip_to_rmsg = models.BooleanField( default=False, help_text=_('skip to taking a nonurgent message instead of ' 'asking during open hours')) config_complete = models.BooleanField(default=False) call_groups = models.ManyToManyField( 'MHLCallGroups.CallGroup', null=True, blank=True, related_name="PracticeCallGroups", verbose_name=("List of all of the call groups for this practice")) gen_msg = models.BooleanField( default=True, help_text= ('read out option for leaving nonurgent message for generic mailbox of practice' )) # refactor organization added fields logo_position = models.IntegerField(choices=ORG_POSITION_TYPE, verbose_name=_("Logo Position"), default=0) logo_size = models.IntegerField(choices=ORG_SIZE_TYPE, verbose_name=_("Logo Size"), default=0) description = models.CharField(max_length=255, blank=True, null=True, verbose_name=_("Description")) create_date = models.DateTimeField(auto_now=True) status = models.IntegerField(default=1) short_name = models.CharField(max_length=30, blank=True) organization_type = models.ForeignKey(OrganizationType, null=True, blank=True) organization_setting = models.ForeignKey( 'MHLPractices.OrganizationSetting', null=True, blank=True) member_orgs = models.ManyToManyField( 'MHLPractices.PracticeLocation', through='MHLPractices.OrganizationMemberOrgs', related_name='member org', null=True, blank=True) member_org_pending = models.ManyToManyField('MHLPractices.PracticeLocation', through='MHLPractices.Pending_Org_Association',\ related_name='member org pending', null=True, blank=True) delete_flag = models.BooleanField(default=False) objects = ActiveOrganization() full_objects = models.Manager() active_practice = ActivePractice() def __unicode__(self): return self.practice_name def uses_original_answering_serice(self): """ Will return True in case this practice was set up using one call group per location version 2 requires NULL in practice.location call group and at least one entry in call_groups False in other case """ return self.call_group is not None __unicode__ = lambda self: self.practice_name def is_open(self, timestamp=None): """ :param timestamp: A timestamp (integer UNIX timestamp or a datetime object) indicating the date and time you want to check. Alternatively, set it to None to use the current time. Note that datetime objects *MUST* be timezone aware, or an exception will be raised. :returns: True if the practice is open, and False if the practice is closed, given the passed time. """ # Munge the timestamp argument so that we always have a datetime object # representative of that time, in the practice's time zone. if (not timestamp): timestamp = datetime.now(timezone(self.time_zone)) elif (timestamp.__class__.__name__ == 'datetime'): # If the timestamp is a datetime object if (timestamp.tzinfo == None): raise Exception( _('PracticeLocation.is_open() requires a ' 'timezone aware datetime object.')) timestamp = timestamp.astimezone(tz) else: # The timestamp is a UNIX timestamp. timestamp = datetime.fromtimestamp(timestamp, self.time_zone) if (PracticeHolidays.objects.filter( practice_location=self.id, designated_day=timestamp).exists()): return False # Shortcut to make access of the weekday easier. weekday = timestamp.isoweekday() today_hours = PracticeHours.objects.filter(practice_location=self.id, day_of_week=weekday) if (len(today_hours) == 0): # Days with no hours objects defined are considered closed. return False # Don't sweat len(today_hours) > 1 because the database schema forbids it. today_hours = today_hours[0] # Heavy lifting! :) if (timestamp.time() > today_hours.open and timestamp.time() < today_hours.close): return True else: return False def set_pin(self, raw_pin): """ Based on Django 1.1.1's password hash generator. """ import random from MHLogin.DoctorCom.IVR.models import get_hexdigest algo = 'sha1' salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5] hashe = get_hexdigest(algo, salt, raw_pin) self.pin = '%s$%s$%s' % (algo, salt, hashe) def verify_pin(self, raw_pin): """ Based on Django 1.1.1's password hash generator. :returns: a boolean of whether the raw_password was correct. Handles encryption formats behind the scenes. """ return check_pin(raw_pin, self.pin) def get_members(self): """ Return a queryset list of all MHLUsers belonging this practice """ from MHLogin.MHLUsers.models import MHLUser users = list(self.practice_provider.all().values_list('user', flat=True)) users.extend( list(self.practice_provider_current.all().values_list('user', flat=True))) users.extend( list(self.practice_officestaff.all().values_list('user', flat=True))) users.extend( list(self.practice_officestaff_current.all().values_list( 'user', flat=True))) users.extend( list(self.practice_office_manager.all().values_list('user__user', flat=True))) # unique'ify and return MHLUser list return MHLUser.objects.filter(id__in=set(users)) def sanitize(self): "Sanitizes any personally identifying data from this object." "NOTE THAT THIS DOES NOT SAVE THE OBJECT!" if (not settings.DEBUG): raise Exception( _('You must be in DEBUG mode to use this function.')) self.set_pin('1234') self.greeting = 'http://api.twilio.com/2008-08-01/Accounts/'\ 'AC087cabfd0a453a05acceb2810c100f69/Recordings/REf8afc497f43d8e1e9bc229a415ebe100' self.name = 'http://api.twilio.com/2008-08-01/Accounts/'\ 'AC087cabfd0a453a05acceb2810c100f69/Recordings/REf8afc497f43d8e1e9bc229a415ebe100' def multi_callgroup(self): return not bool(self.call_group) def save_parent_org(self, parent_org=None, billing_flag=None): """ Save organization parent relationship :param parent_org: is an instance of PracticeLocation :param billing_flag: billing_flag """ if parent_org: try: org_relation = OrganizationRelationship.active_objects.get( organization=self, parent=parent_org) org_relation.billing_flag = billing_flag org_relation.save() except OrganizationRelationship.DoesNotExist: org_relation = OrganizationRelationship.objects.create( organization=self, parent=parent_org, create_time=time.time()) else: OrganizationRelationship.active_objects.filter( organization=self, parent=parent_org).delete() def save_member_org(self, member_org=None, billing_flag=None): """ Save organization member relationship :param member_orgs: is an instance of PracticeLocation :param billing_flag: billing_flag """ if member_org: try: member = OrganizationMemberOrgs.objects.get( from_practicelocation=self, to_practicelocation=member_org) member.billing_flag = billing_flag member.save() except OrganizationMemberOrgs.DoesNotExist: OrganizationMemberOrgs.objects.create( from_practicelocation=self, to_practicelocation=member_org, create_time=time.time(), billing_flag=billing_flag) else: OrganizationMemberOrgs.objects.filter( from_practicelocation=self, to_practicelocation=member_org).delete() def get_setting(self): """ Get org's setting object. :returns: an instance of OrganizationSetting """ if self.organization_setting and not self.organization_setting.delete_flag: return self.organization_setting else: org_type = self.organization_type if org_type and org_type.organization_setting and not \ org_type.organization_setting.delete_flag: return org_type.organization_setting else: return None def get_setting_attr(self, key): """ Get org's setting's attribute value. :returns: setting's value """ setting = self.get_setting() if not setting: return False if not key or key not in dir(setting): return False return getattr(setting, key) def can_have_any_staff(self): setting = self.get_setting() if not setting: return None else: return setting.can_have_staff or setting.can_have_manager or \ setting.can_have_nurse or setting.can_have_dietician def can_have_staff_member(self): setting = self.get_setting() if not setting: return None else: return setting.can_have_staff or setting.can_have_nurse or \ setting.can_have_dietician def can_have_any_provider(self): setting = self.get_setting() if not setting: return None else: return setting.can_have_physician or setting.can_have_nppa or \ setting.can_have_medical_student def get_org_sub_user_types_tuple(self, role_category=None, include_manager=True, include_sub_staff_type=True): """ Get user's types that can save for this organization. :param role_category: int value if role_category is 1, return provider's type tuple if role_category is 2, return staff's type tuple, others(include None), return all user's type tuple :returns: tuple """ org_setting = self.get_setting() if org_setting is None: return () provider_types = () staff_types = () if org_setting.can_have_physician: provider_types += ((USER_TYPE_DOCTOR, _('Doctor')), ) if org_setting.can_have_nppa: provider_types += ((USER_TYPE_NPPA, _('NP/PA/Midwife')), ) if org_setting.can_have_medical_student: provider_types += ((USER_TYPE_MEDICAL_STUDENT, _('Med/Dental Student')), ) if include_manager and org_setting.can_have_manager: staff_types += ((USER_TYPE_OFFICE_MANAGER, _('Office Manager')), ) if include_sub_staff_type: if org_setting.can_have_staff: staff_types += ((USER_TYPE_OFFICE_STAFF, _('Staff')), ) if org_setting.can_have_nurse: staff_types += ((USER_TYPE_NURSE, _('Nurse')), ) if org_setting.can_have_dietician: staff_types += ((USER_TYPE_DIETICIAN, _('Dietician')), ) else: if org_setting.can_have_staff or org_setting.can_have_nurse or\ org_setting.can_have_dietician: staff_types += ((USER_TYPE_OFFICE_STAFF, _('Staff')), ) user_types = provider_types + staff_types if org_setting.can_have_tech_admin: user_types += ((USER_TYPE_TECH_ADMIN, _('Tech Admin')), ) if role_category is None: return user_types if role_category == 1: return provider_types if role_category == 2: return staff_types return user_types def get_org_sub_user_types(self, format=0, role_category=None): """ Get user's types that can save for this organization. :param format: format is 0 or 1, if format is 0, function will return list with user type flag if format is 1, function will return list with user type string :returns: list(id or type string) """ user_types = self.get_org_sub_user_types_tuple( role_category=role_category) return [t[format] for t in user_types] def get_parent_org(self): org_rss = OrganizationRelationship.objects.filter(organization=self) if org_rss and org_rss[0] and org_rss[0].parent and \ org_rss[0].parent.id != RESERVED_ORGANIZATION_ID_SYSTEM: return org_rss[0].parent return None
class MHLUser(User): uuid = UUIDField(auto=True, primary_key=False) gender = models.CharField(max_length=1, choices=GENDER_CHOICES, verbose_name=_("Gender"), default='M') title=models.CharField(max_length=30,blank=True,null=True) mobile_phone = MHLPhoneNumberField(blank=True, verbose_name=_("Mobile Phone"), validators=[validate_phone]) phone = MHLPhoneNumberField(blank=True, verbose_name=_("Other Phone"), help_text=PHONE_NUMBER_HELP_TEXT, validators=[validate_phone]) address1 = models.CharField(max_length=200, blank=True, verbose_name=_("Address1")) address2 = models.CharField(max_length=200, blank=True, verbose_name=_("Address2")) city = models.CharField(max_length=200, blank=True, verbose_name=_("City")) state = models.CharField(max_length=2, choices=STATE_CHOICES, blank=True, verbose_name=_("State")) zip = models.CharField(max_length=10, blank=True, verbose_name=_("Zip")) # 10 for zip and zip+4 lat = models.FloatField(blank=True, null=True) longit = models.FloatField(blank=True, null=True) photo = models.ImageField(upload_to="images/userBioPics/%Y/%m/%d", blank=True, verbose_name=_("Photo"), help_text=_("Recommended size 100*130")) email_confirmed = models.BooleanField(default=False) mobile_confirmed = models.BooleanField(default=False) tos_accepted = models.BooleanField(default=False, help_text=_("Has the user accepted the terms of service?")) billing_account_accepted = models.BooleanField(default=False, help_text=_("Has the user created a billing account?")) force_pass_change = models.BooleanField(default=False) password_change_time = models.DateTimeField(auto_now_add=True) #add by xlin 20120924 to add user skill skill = models.CharField(max_length=200, null=True, blank=True, verbose_name=_("Special Skill")) public_notes = models.TextField(blank=True, help_text="Special notes, contact preferences, etc..") #add by xlin 121017 for todo1045 that add setting time field False is 24h;True is 12h time_setting = models.IntegerField(choices=SETTING_TIME_CHOICES, default=0, verbose_name=_('Time Setting')) refer_to_manager = models.BooleanField(default=True, verbose_name=_('CC refer to manager')) refer_forward = models.IntegerField(choices=REFER_FORWARD_CHOICES, default=REFER_FORWARD_CHOICES_BOTH, verbose_name=_('Refer Forward Setting')) #time zone where practice is located, matches values in pytz time_zone = models.CharField(max_length=64, blank=True, null=True, choices=TIME_ZONES_CHOICES, verbose_name=_('Time Zone')) def __unicode__(self): return "%s %s" % (self.first_name, self.last_name) def clean(self): if (self.pk): # This user already exists if (self.mobile_phone and MHLUser.objects.filter( mobile_phone=self.mobile_phone).exclude(pk=self.pk).exists()): raise ValidationError(VALIDATION_ERROR_MOBILE_EXISTS) else: # This user doesn't exist yet. This would be for new accounts. if (self.mobile_phone and MHLUser.objects.filter(mobile_phone=self.mobile_phone).exists()): raise ValidationError(VALIDATION_ERROR_MOBILE_EXISTS) if (self.email): query_set = MHLUser.objects.filter(email=self.email) if (self.pk): query_set = query_set.exclude(pk=self.pk) if (query_set.exists()): raise ValidationError(VALIDATION_ERROR_EMAIL_EXISTS) def save(self, *args, **kwargs): # Check to ensure that no other user exists with the same mobile phone # number. if (self.mobile_phone and MHLUser.objects.filter( mobile_phone=self.mobile_phone).exclude(pk=self.pk).exists()): raise Exception(VALIDATION_ERROR_MOBILE_EXISTS) if (self.email): query_set = MHLUser.objects.filter(email=self.email) if (self.pk): query_set = query_set.exclude(pk=self.pk) if (query_set.exists()): raise ValidationError(VALIDATION_ERROR_EMAIL_EXISTS) super(MHLUser, self).save(*args, **kwargs) def set_password(self, newpass): super(MHLUser, self).set_password(newpass) self.password_change_time = datetime.now() def change_smartphone_perm(self, is_enable=True): perm, is_created = Permission.objects.get_or_create(codename='access_smartphone', name='Can use smartphone app', content_type=ContentType.objects.get_for_model(MHLUser)) if is_enable and not self.has_perm('MHLUsers.access_smartphone'): self.user_permissions.add(perm) self.save() if not is_enable and self.has_perm('MHLUsers.access_smartphone'): self.user_permissions.remove(perm) self.save() class Meta: ordering = ['last_name', 'first_name'] permissions = ( ("access_smartphone", "Can use smartphone app"), ("can_call_transfer", "Can transfer incoming calls"), )
class Broker(models.Model): """ Broker objects which allow us to declare MHLUsers to be broker, and to allow us to collect additional data on these users. DEPRECATION WARNING: Please read the deprecation warning in the code before using this object. """ user = models.ForeignKey(MHLUser, null=True, blank=True, related_name='user_broker') office_address1 = models.CharField(max_length=200, blank=True) office_address2 = models.CharField(max_length=200, blank=True) office_phone = MHLPhoneNumberField(verbose_name=_("Office Phone"), blank=True, help_text=PHONE_NUMBER_HELP_TEXT, validators=[validate_phone]) office_city = models.CharField(max_length=200, blank=True) office_state = models.CharField(max_length=2, choices=STATE_CHOICES, blank=True) office_zip = models.CharField(max_length=10, blank=True) # 10 for zip and zip+4 office_lat = models.FloatField(blank=True) office_longit = models.FloatField(blank=True) # Django IntegerField too small for US phone number with area code. pager = MHLPhoneNumberField(blank=True, help_text=PHONE_NUMBER_HELP_TEXT) pager_extension = models.CharField(max_length=100, blank=True) pager_confirmed = models.BooleanField(default=False) # DoctorCom Provisioned Phone Number and confirmation flag mdcom_phone = MHLPhoneNumberField(blank=True) mdcom_phone_sid = models.CharField(max_length=34, blank=True) forward_mobile = models.BooleanField(default=True) forward_office = models.BooleanField(default=False) forward_other = models.BooleanField(default=False) forward_vmail = models.BooleanField(default=False) forward_voicemail = models.CharField(max_length=2, choices=FORWARD_CHOICES, default='MO') forward_anssvc = models.CharField(max_length=2, choices=FORWARD_CHOICES, default='VM') # Voicemail vm_config = generic.GenericRelation(VMBox_Config, content_type_field='owner_type', object_id_field='owner_id') vm_msgs = generic.GenericRelation(VMMessage, content_type_field='owner_type', object_id_field='owner_id') licensure_states = models.ManyToManyField(States, null=True, blank=True, verbose_name=_("States of Licensure")) #Flag to set provider as a medical student clinical_clerk = models.BooleanField(default=False) status_verified = models.BooleanField(default=False) status_verifier = models.ForeignKey(MHLUser, null=True, blank=True, related_name='verifier_broker') objects = models.Manager() active_objects = ActiveManagedUser() def __unicode__(self): return self.user.username def clean(self): pass class Meta: ordering = ['user']
class Provider(MHLUser): # TODO_KCV new provider this will inherit from models.Model """ Provider objects which allow us to declare MHLUsers to be providers, and to allow us to collect additional data on these users. Changing model to not inherit from MHLUser and just use foreign key into MHLUser grep for TODO_PROVINH tags in code for places where we assume inheritance """ # Be aware that if you're creating a Provider, you need to manually populate # this field with the correct User object! user = models.ForeignKey(MHLUser, null=True, blank=True, related_name='user_provider') office_address1 = models.CharField(max_length=200, blank=True, verbose_name=_("Office address1")) office_address2 = models.CharField(max_length=200, blank=True, verbose_name=_("Office address2")) office_phone = MHLPhoneNumberField(verbose_name=_("Office Phone"), blank=True, help_text=PHONE_NUMBER_HELP_TEXT, validators=[validate_phone]) office_city = models.CharField(max_length=200, blank=True, verbose_name=_("Office city")) office_state = models.CharField(max_length=2, choices=STATE_CHOICES, blank=True, verbose_name=_("Office state")) office_zip = models.CharField(max_length=10, blank=True, verbose_name=_("Office zip")) # 10 for zip and zip+4 office_lat = models.FloatField(blank=True) office_longit = models.FloatField(blank=True) # Django IntegerField too small for US phone number with area code. pager = MHLPhoneNumberField(blank=True, verbose_name=_("Pager"), help_text=PHONE_NUMBER_HELP_TEXT) pager_extension = models.CharField(max_length=100, blank=True, verbose_name=_("Pager extension")) pager_confirmed = models.BooleanField(default=False) #DoctorCom Provisioned Phone Number and confirmation flag mdcom_phone = MHLPhoneNumberField(blank=True) mdcom_phone_sid = models.CharField(max_length=34, blank=True) forward_mobile = models.BooleanField(default=True) forward_office = models.BooleanField(default=False) forward_other = models.BooleanField(default=False) forward_vmail = models.BooleanField(default=False) forward_voicemail = models.CharField(max_length=2, choices=FORWARD_CHOICES, default='MO') forward_anssvc = models.CharField(max_length=2, choices=FORWARD_CHOICES, default='VM') # Voicemail vm_config = generic.GenericRelation(VMBox_Config, content_type_field='owner_type', object_id_field='owner_id') vm_msgs = generic.GenericRelation(VMMessage, content_type_field='owner_type', object_id_field='owner_id') sites = models.ManyToManyField(Site, null=True, blank=True, related_name='site_provider') current_site = models.ForeignKey(Site, null=True, blank=True, related_name='site_provider_current') practices = models.ManyToManyField(PracticeLocation, null=True, blank=True, related_name='practice_provider') current_practice = models.ForeignKey(PracticeLocation, null=True, blank=True, related_name='practice_provider_current') licensure_states = models.ManyToManyField(States, null=True, blank=True, verbose_name=_("States of Licensure")) #Flag to set provider as a medical student clinical_clerk = models.BooleanField(default=False) status_verified = models.BooleanField(default=False) status_verifier = models.ForeignKey(MHLUser, null=True, blank=True, related_name='verifier_provider') certification = models.TextField(null=True, blank=True, verbose_name=_("certification")) objects = models.Manager() active_objects = ActiveManagedUser() #add by xlin 120914 search_objects = SearchProviders() def __unicode__(self): return "%s %s" % (self.first_name, self.last_name) def __getattribute__(self, name): if settings.DEBUG_PROVIDER == True and name in MHLUSER_FIELDS: # array of sets (file, line#, function, expr) tracebuf = traceback.extract_stack() buf = "" for t in tracebuf: buf = buf + "File: ..." + t[0][-30:] + " line: " + str(t[1]) + \ " func: " + t[2] + " expr: " + t[3] + "\n" logger.warn("Provider will soon no longer inherit from MHLUser,"\ "use foreign key 'user' to get to field '%s', stacktrace:"\ "\n%s\n" % (name, buf)) return super(Provider, self).__getattribute__(name) def clean(self): if (self.pk): # This user already exists if (self.mobile_phone and MHLUser.objects.filter( mobile_phone=self.mobile_phone).exclude(pk=self.pk).exists()): raise ValidationError(VALIDATION_ERROR_MOBILE_EXISTS) else: # This user doesn't exist yet. This would be for new accounts. if (self.mobile_phone and MHLUser.objects.filter( mobile_phone=self.mobile_phone).exists()): raise ValidationError(VALIDATION_ERROR_MOBILE_EXISTS) if (self.email): query_set = MHLUser.objects.filter(email=self.email) if (self.pk): query_set = query_set.exclude(pk=self.pk) if (query_set.exists()): raise ValidationError(VALIDATION_ERROR_EMAIL_EXISTS) def save(self, *args, **kwargs): # Check to ensure that no other user exists with the same mobile phone number. if (self.mobile_phone and MHLUser.objects.filter( mobile_phone=self.mobile_phone).exclude(pk=self.pk).exists()): raise Exception(VALIDATION_ERROR_MOBILE_EXISTS) if (self.email): query_set = MHLUser.objects.filter(email=self.email) if (self.pk): query_set = query_set.exclude(pk=self.pk) if (query_set.exists()): raise ValidationError(VALIDATION_ERROR_EMAIL_EXISTS) super(MHLUser, self).save(*args, **kwargs) class Meta: ordering = ['user']