def handle(self, text): # make sure they are registered with the system if not (self.msg.contact and self.msg.contact.is_help_admin): self.respond(self.UNGREGISTERED) return text = text.strip() if not text: self.help() return location_slug = text.split()[0][0:6] #get only PPDDFF from 1st token try: txt_count = text.split()[1] ic = InputCleaner() count = ic.words_to_digits(txt_count) except (IndexError, ValueError, AttributeError): count = 5 if count == 0: count = 5 try: location = Location.objects.get(slug__iexact=location_slug) except Location.DoesNotExist: self.respond("Sorry, I don't know about a location with code " "%(code)s. Please check your code and try again.", code=location_slug) return active_contacts = Contact.active.filter(Q(location=location) | Q(location__parent=location), Q(types= const.get_clinic_worker_type()))\ .order_by('pk') if active_contacts: contact_list = " ****".join(contact.name + ";" + contact.default_connection.identity + "." for contact in active_contacts[0:count]) self.respond("Contacts at %s: %s" % (location.name, contact_list)) else: self.respond("There are no active contacts at %s" % location.name)
def handle(self, text): original_text = text if not self.msg.contact: self.respond(UNGREGISTERED) return b = InputCleaner() try: count = int(b.try_replace_oil_with_011(text.strip())) except ValueError: text = b.words_to_digits(text) if not text: text= self.get_only_number(original_text) if text: count = int(text) else: self.respond("%s %s" % (SORRY, HELP)) return else: self.info("Converted %s to %s" % (original_text, text)) count = int(text) count = abs(count) #just in case we change our general cleaning routine if count < 1: self.respond("Sorry, the number of DBS samples sent must be greater than 0 (zero).") return # record this in our records SampleNotification.objects.create(contact=self.msg.contact, location=self.msg.contact.location, count=count, count_in_text=original_text[0:160]) clinic = get_clinic_or_default(self.msg.contact) self.respond(SENT, name=self.msg.contact.name, count=count, clinic=clinic)
def handle(self, text): # make sure they are registered with the system if not (self.msg.contact and self.msg.contact.is_help_admin): self.respond(self.UNGREGISTERED) return text = text.strip() if not text: self.help() return start_days_ago = end_days_ago = 0 ic = InputCleaner() try: txt_start_days_ago = text.split()[0] start_days_ago = int(ic.words_to_digits(txt_start_days_ago)) except (IndexError, ValueError, AttributeError, TypeError): start_days_ago = 0 try: txt_end_days_ago = text.split()[1] end_days_ago = int(ic.words_to_digits(txt_end_days_ago)) except (IndexError, ValueError, AttributeError, TypeError): end_days_ago = 0 if start_days_ago < end_days_ago: start_days_ago, end_days_ago = end_days_ago, start_days_ago now = datetime.date.today() today = datetime.datetime(now.year, now.month, now.day) startdate = today - datetime.timedelta(days=start_days_ago) enddate = today - datetime.timedelta(days=end_days_ago - 1) - \ datetime.timedelta(seconds=0.1) # Not sure if uncommenting the code below will improve performance. # payloads = Payload.objects.filter(Q(incoming_date__gt=startdate) | # Q(incoming_date=startdate), # Q(incoming_date__lt=enddate) | # Q(incoming_date=enddate)) # if not payloads: # self.respond("Period %(startdate)s to %(enddate)s. No payloads", # startdate=startdate.strftime("%d/%m/%Y"), # enddate=enddate.strftime("%d/%m/%Y")) # return from django.db import connection cursor = connection.cursor() cursor.execute('select source, count(*) as count from \ labresults_payload where incoming_date BETWEEN %s AND %s group by \ source', [startdate, enddate]) rows = cursor.fetchall() if not rows: self.respond("Period %(startdate)s to %(enddate)s. No payloads", startdate=startdate.strftime("%d/%m/%Y"), enddate=enddate.strftime("%d/%m/%Y")) return #build formartted message msg_header = 'PAYLOADS. Period: %s to %s. ' % (startdate.strftime("%d/%m/%Y") , enddate.strftime("%d/%m/%Y")) msg_data = ' ****'.join(row[0] + ";" + str(row[1]) for row in rows) full_msg = msg_header + msg_data self.respond(full_msg)
def handle(self, text): b = InputCleaner() if not is_eligible_for_results(self.msg.connection): self.respond(self.NOT_ELIGIBLE) return if not text or not text.strip(): return clinic_code = text.split()[0] #staff with zeros in case someone just send PP or PPDD if b.try_replace_oil_with_011(clinic_code[0:6]).isdigit(): clinic_code = clinic_code + "00000" clinic_code = clinic_code[0:6] district_facilities = None province_facilities = None try: location = Location.objects.get(slug__iexact=clinic_code) if location.type.slug == 'districts': district_facilities = Location.objects.filter( parent=location, type__slug__in=const.CLINIC_SLUGS) elif location.type.slug == 'provinces': province_facilities = Location.objects.filter( parent__parent=location, type__slug__in=const.CLINIC_SLUGS) except Location.DoesNotExist: # maybe it's a district like 403000 try: clinic_code = clinic_code.replace('000', '0') district_facilities = Location.objects.filter( slug__startswith=clinic_code, type__slug__in=const.CLINIC_SLUGS) location = district_facilities[0].parent except IndexError: #maybe it's a province like 400000 try: clinic_code = clinic_code.replace('000', '0') province_facilities = Location.objects.filter( slug__startswith=clinic_code, type__slug__in=const.CLINIC_SLUGS) location = province_facilities[0].parent.parent except IndexError: self.respond( "Sorry, I don't know about a location with code %(code)s. Please check your code and try again.", code=clinic_code) return text = text.strip() text = b.remove_double_spaces(text) today = datetime.datetime.today() try: month = int(b.words_to_digits(text.split()[1][0:3])) except (IndexError, TypeError): month = today.month if month not in range(1, 13): month = today.month startdate = datetime.datetime(today.year, month, 1) if month == 12: enddate = datetime.datetime(today.year, 12, 31) else: enddate = datetime.datetime(today.year, month + 1, 1) - datetime.timedelta(seconds=1) report_values = self.get_facility_report(location, startdate, enddate, district_facilities, province_facilities) rpt_header = "SENT RESULTS\n%s\n%s to %s" % ( location.name, startdate.strftime("%d/%m/%Y"), enddate.strftime("%d/%m/%Y")) rpt_data = '\n'.join(key + ";" + str(value) for key, value in report_values.items()) msg = rpt_header + '\n' + rpt_data self.respond(msg)
def check_message_valid_and_clean(self, text): ''' Checks the message for general validity (correct pin length, number of keywords, etc) and returns False if message is somehow invalid (after firing off a useful response message) Returns cleaned message in tokenized format (tuple) ''' original_text = text cleaner = InputCleaner() text = text.strip() text = cleaner.remove_double_spaces(text) if len(text) < (self.PIN_LENGTH + self.MIN_CLINIC_CODE_LENGTH + self.MIN_NAME_LENGTH + 1): self.mulformed_msg_help() return False #signed pin if text[-5:-4] == '-' or text[-5:-4] == '+': self.invalid_pin(text[-5:]) return False #too long pin if ' ' in text and text[1 + text.rindex(' '):].isdigit() and len( text[1 + text.rindex(' '):]) > self.PIN_LENGTH: self.invalid_pin(text[1 + text.rindex(' '):]) return False #non-white space before pin if text[-5:-4] != ' ' and text[-4:-3] != ' ': self.respond( "Sorry, you should put a space before your pin. " "Please make sure your code is a %s-digit number like %s. " "Send JOIN <LOCATION CODE> <YOUR NAME> <SECURITY CODE>." % (self.PIN_LENGTH, ''.join( str(i) for i in range(1, int(self.PIN_LENGTH) + 1)))) return False #reject invalid pin user_pin = text[-4:] if not user_pin: self.help() return False elif len(user_pin) < 4: self.invalid_pin(user_pin) return False elif not user_pin.isdigit(): self.invalid_pin(user_pin) return False group = self.PATTERN.search(original_text) if group is None: self.mulformed_msg_help() return False tokens = group.groups() if not tokens: self.mulformed_msg_help() return False #sanitize! group = self.PATTERN.search(text) tokens = group.groups() tokens = list(tokens) tokens[0] = tokens[0].strip() #location code tokens[2] = tokens[2].title().strip() #name tokens[4] = tokens[4].strip() #pin #more error checking if len(tokens[4]) != self.PIN_LENGTH: self.respond(self.INVALID_PIN) return False if not tokens[2]: self.respond("Sorry, you must provide a name to register. %s" % self.HELP_TEXT) return False elif len(tokens[2]) < self.MIN_NAME_LENGTH: self.respond( "Sorry, you must provide a valid name to register. %s" % self.HELP_TEXT) return False return tuple(tokens)
def handle(self, text): b = InputCleaner() if not is_eligible_for_results(self.msg.connection): # essentially checking for an active clinic_worker self.respond(self.INELIGIBLE) return text = text.strip() text = b.remove_double_spaces(text) worker = self.msg.contact location = worker.location if location.type == const.get_zone_type(): location = location.parent cba = None # we expect phone numbers like +260977123456, 0977123456, 977123456 # (a phone number is unique to each cba at a clinic) if text[1:].isdigit() and len(text) >= self.MIN_PHONE_LENGTH: try: cba = \ Contact.active.get(connection__identity__endswith=text, location__parent=location, types=const.get_cba_type()) except Contact.DoesNotExist: self.respond( 'The phone number %(phone)s does not belong to any' ' CBA at %(clinic)s. Make sure you typed it ' 'correctly', phone=text, clinic=location) # we do not expect this to happen. phone number is excpected to be # unique (>=9 chars) project wide except Contact.MultipleObjectsReturned: logger.warning( "Bug. phone number %s is used by multiple cba's " "at same clinic" % text) self.respond( "Sorry %(name)s, the CBA with phone number %(phone)s" " could not be deregistered. This matter will be" " followed up by Support Staff immediately", name=worker.name, phone=text) msg = (self.FOLLOWUP_MESSAGE % (text, worker.name, worker.default_connection.identity, location.name)) self.notify_help_admins(msg) return if not cba: cbas = \ Contact.active.filter(name__icontains=text, location__parent=location, types=const.get_cba_type()) if not cbas: self.respond( 'The name %(name)s does not belong to any' ' CBA at %(clinic)s. Make sure you typed it ' 'correctly', name=text, clinic=location) return if len(cbas) == 1: cba = cbas[0] elif len(cbas) < 5: self.respond("Try sending REMOVE <CBA_PHONE_NUMBER>. Which " + "CBA did you mean? %(cbas)s", cbas=' or '.join(cba.name + ":" + cba.default_connection.identity for cba in cbas)) return else: self.respond( "There are %(len)s CBA's who's names match %(name)s" + " at %(clinic)s. Try to use the phone number " + "instead", len=len(cbas), name=text, clinic=location.name) return if cba: cba.is_active = False cba.save() self.respond("You have successfully deregistered %(name)s:" + "%(phone)s of zone %(zone)s at %(clinic)s", name=cba.name, phone=cba.default_connection.identity, zone=cba.location.name, clinic=location.name) msg = ("%s:%s has deregistered %s:%s of zone %s at %s" % (worker.name, worker.default_connection.identity, cba.name, cba.default_connection.identity, cba.location.name, location.name)) self.notify_help_admins(msg)
class TestApp(TestScript): ic = InputCleaner() def testSoundEx(self): self.assertEqual(self.ic.soundex('thri'), self.ic.soundex('three')) def testWordsToDigits(self): self.assertEqual(2, self.ic.words_to_digits('two')) self.assertEqual(2, self.ic.words_to_digits('too')) self.assertEqual(302, self.ic.words_to_digits('thri hundred two')) self.assertEqual(302, self.ic.words_to_digits('thri hundred and two')) self.assertEqual(26, self.ic.words_to_digits('twenti six')) self.assertEqual(8002, self.ic.words_to_digits('Eight thousand and two')) self.assertEqual( 2001082, self.ic.words_to_digits( '2 milion one thouzand Eighty too samples')) def testReplaceoilWith011(self): self.assertEqual('00111', self.ic.try_replace_oil_with_011('oOiIl')) self.assertEqual('403012', self.ic.try_replace_oil_with_011('4o3oi2')) def testRemoveDoubleSpaces(self): self.assertEqual( 'request 10 for db samples', self.ic.remove_double_spaces( 'request 10 for db samples')) def testDigitToWord(self): self.assertEqual('One', self.ic.digit_to_word(1)) self.assertEqual('Two', self.ic.digit_to_word(2)) self.assertEqual('Thirty', self.ic.digit_to_word(30)) self.assertEqual(None, self.ic.digit_to_word(31)) def testLdistance(self): self.assertEqual(0, self.ic.ldistance('pea', 'PeA')) self.assertEqual(1, self.ic.ldistance('peac', 'PeA')) self.assertEqual(1, self.ic.ldistance('pea', 'PeAc')) self.assertEqual(1, self.ic.ldistance('pea', 'PeAc')) self.assertEqual(4, self.ic.ldistance('trev', 'nanc')) def testConditonalStringCleaning(self): """ Tests string cleaning based on keywords """ self.assertEqual(0, Contact.objects.count()) ctr = LocationType.objects.create(slug=const.CLINIC_SLUGS[0]) kdh = Location.objects.create(name="Kafue District Hospital", slug="kdh", type=ctr) Location.objects.create(name="Central Clinic", slug="403012", type=ctr) #in JOIN clean separators including '/' script = """ 0979565992 > join 403012/jichael,mackson;1111 0979565992 < Hi Jichael Mackson, thanks for registering for Results160 from Central Clinic. Your PIN is 1111. Reply with keyword 'HELP' if this is incorrect """ self.runScript(script) #in AGENT clean separators including '/' script = """ cba1 > agent 403012/2,peter;phiri cba1 < Thank you Peter Phiri! You have successfully registered as a RemindMi Agent for zone 2 of Central Clinic. cba2 > agent 403012/2,james;banda cba2 < Thank you James Banda! You have successfully registered as a RemindMi Agent for zone 2 of Central Clinic. """ self.runScript(script) # in RESULT don't clean '/' script = """ 0979565992 > results 403012/10 0979565992 < There are currently no results available for 403012/10. Please check if the SampleID is correct or sms HELP if you have been waiting for 2 months or more """ self.runScript(script) # in broadcasts don't clean script = """ cba1 > clinic 403012/10. Not 402012/09; 0979565992 < 403012/10. Not 402012/09; [from Peter Phiri to CLINIC] cba1 > cba dont't filter , or / or ; or * or + or - in broadcasts cba2 < dont't filter , or / or ; or * or + or - in broadcasts [from Peter Phiri to CBA] """ self.runScript(script) script = """ 0979565993 > join 403012/princess,obama;1111 0979565993 < Hi Princess Obama, thanks for registering for Results160 from Central Clinic. Your PIN is 1111. Reply with keyword 'HELP' if this is incorrect """ self.runScript(script) admin = Contact.active.get(connection__identity='0979565993') admin.is_help_admin = True admin.save() script = """ 0979565993 > blaster in blaster we dont clean , or / or ; or * or + or - 0979565992 < in blaster we dont clean , or / or ; or * or + or - [from Princess Obama to Mwana Users] cba1 < in blaster we dont clean , or / or ; or * or + or - [from Princess Obama to Mwana Users] cba2 < in blaster we dont clean , or / or ; or * or + or - [from Princess Obama to Mwana Users] """ self.runScript(script)