def _load_config_file(self, config_file_yaml): ''' A config.yaml configuration file is expected to be in the same directory as this script ''' try: config_file = os.path.join( os.path.dirname(os.path.realpath(__file__)), config_file_yaml) self.config = yaml.load(open(config_file, 'r'), Loader=yaml.SafeLoader) self._username = self.config['username'] self._password = self.config['password'] self._dog_mode = self.config[ 'dog_mode'] if 'dog_mode' in self.config else False self._google_spreadsheet_key = self.config[ 'google_spreadsheet_key'] if 'google_spreadsheet_key' in self.config else None self._google_client_secret = self.config[ 'google_client_secret'] if 'google_client_secret' in self.config else None self._box_user_id = self.config[ 'box_user_id'] if 'box_user_id' in self.config else None self._box_file_id = self.config[ 'box_file_id'] if 'box_file_id' in self.config else None self._box_jwt = self.config[ 'box_jwt'] if 'box_jwt' in self.config else None if not (self._google_spreadsheet_key and self._google_client_secret ) and not (self._box_user_id and self._box_file_id and self._box_jwt): Log.error( f'ERROR: Incomplete mentor spreadsheet configuration: {config_file}' ) return False if self._dog_mode: Log.warn('** Dog Mode is Active **') self.BASE_ANIMAL_TYPE = 'feline_and_critters' if not self._dog_mode else 'canine' except yaml.YAMLError as err: Log.error( f'ERROR: Unable to parse configuration file: {config_file}, {err}' ) return False except IOError as err: Log.error( f'ERROR: Unable to read configuration file: {config_file}, {err}' ) return False except KeyError as err: Log.error( f'ERROR: Missing value in configuration file: {config_file}, {err}' ) return False return True
def check_for_surgery_sheet(self, worksheet): if any(worksheet.title in substr for substr in self._SURGERY_SHEET_NAMES): surgery_rows = worksheet.get_values( 'A1', f'H{worksheet.rows}', include_tailing_empty=False, include_tailing_empty_rows=False) date_col = -1 patient_col = -1 for col in range(0, len(surgery_rows[0])): # Allow for an extra header row (accounting for differences between Feline and Canine) # if any('date' in substr for substr in [ str(surgery_rows[0][col]).lower(), str(surgery_rows[1][col]).lower() ]): date_col = col elif 'patient' in (str(surgery_rows[0][col]).lower(), str(surgery_rows[1][col]).lower()): patient_col = col if date_col != -1 and patient_col != -1: for row in range(1, len(surgery_rows)): try: a_number = surgery_rows[row][patient_col] if a_number.isdigit(): a_number = int(a_number) # If there are multiple entries for a given a_number, assume the first is the most recent. # if a_number not in self._surgery_dates: self._surgery_dates[int( a_number)] = surgery_rows[row][date_col] except Exception as e: Log.warn( f'{worksheet.title} column {patient_col}, row {row} is empty. Assuming this is the end of the list.' ) break else: Log.error( f'Surgery form is not in expected format (date_col={date_col}, patient_col={patient_col}. Skipping.' ) Log.debug( f'Loaded {len(self._surgery_dates)} entries from the surgery sheet' ) return True return False
def _output_results(self, animal_data, foster_parents, persons_data, animals_not_in_foster, current_mentee_status, csv_filename): ''' Output all of our new super amazing results to a csv file ''' Log.success(f'Writing results to {csv_filename}...') csv_rows = [] csv_rows.append([]) csv_rows[-1].append('Kitten-Scraper Notes') csv_rows[-1].append('Loss Rate') csv_rows[-1].append('Name') csv_rows[-1].append('E-mail') csv_rows[-1].append('Phone') csv_rows[-1].append('Person ID') csv_rows[-1].append('Foster Experience') csv_rows[-1].append('Date Animals Received') if self._dog_mode: csv_rows[-1].append('"Name, Breed, Color"') csv_rows[-1].append('Animal Details') csv_rows[-1].append('Special Animal Message') # Build a row for each foster parent # for person_number in sorted(foster_parents, key=lambda p: persons_data[p]['notes']): person_data = persons_data[person_number] name = person_data['full_name'] report_notes = person_data['notes'] loss_rate = round(person_data['loss_rate']) animals_with_this_person = foster_parents[person_number] animal_details, animal_details_brief = self._get_animal_details_string( animals_with_this_person, animal_data) prev_animals_fostered = person_data['prev_animals_fostered'] foster_experience = 'NEW' if not prev_animals_fostered else prev_animals_fostered special_message = '' for a_number in animals_with_this_person: msg = animal_data[a_number]['message'] if msg: special_message += '{}{}: {}'.format( '\r\r' if special_message else '', a_number, msg) cell_number = person_data['cell_phone'] home_number = person_data['home_phone'] phone = '' if len(cell_number) >= 10: # ignore incomplete phone numbers phone = f'(C) {cell_number}' if len(home_number) >= 10: # ignore incomplete phone numbers phone += '{}(H) {}'.format('\r' if phone else '', home_number) emails_str = '' for email in person_data['emails']: emails_str += '{}{}'.format('\r' if emails_str else '', email) # I will assume all animals in this group went into foster on the same date. This should usually be true # since this is designed to processed with a "daily report". # date_received = animal_data[ animals_with_this_person[0]]['status_date'] # Explicitly wrap numbers/datestr with ="{}" to avoid Excel auto-formatting issues # csv_rows.append([]) csv_rows[-1].append(f'"{report_notes}"') csv_rows[-1].append(f'"{loss_rate}%"') csv_rows[-1].append(f'"{name}"') csv_rows[-1].append(f'"{emails_str}"') csv_rows[-1].append(f'"{phone}"') csv_rows[-1].append(f'="{person_number}"') csv_rows[-1].append(f'"{foster_experience}"') csv_rows[-1].append(f'="{date_received}"') if self._dog_mode: csv_rows[-1].append(f'"{animal_details_brief}"') csv_rows[-1].append(f'"{animal_details}"') csv_rows[-1].append(f'"{special_message}"') print('{} (Experience: {}, Loss Rate: {}%) {}{}{}'.format( name, foster_experience, loss_rate, Log.GREEN, report_notes.replace('\r', ', '), Log.END)) with open(csv_filename, 'w') as outfile: for row in csv_rows: outfile.write(','.join(row)) outfile.write('\n') if not foster_parents: outfile.write( '*** None of the animals in this report are currently in foster\n' ) Log.warn( 'None of the animals in this report are currently in foster. Nothing to do!' ) if animals_not_in_foster: outfile.write('\n\n\n*** Animals not in foster\n') Log.warn('\nAnimals not in foster') for a_number in animals_not_in_foster: outfile.write('{} {} - {}\n'.format( a_number, animal_data[a_number]['type'], animal_data[a_number]['status'])) print('{} {} - {}'.format(a_number, animal_data[a_number]['type'], animal_data[a_number]['status'])) if current_mentee_status: outfile.write( '\n\nMentor,Active Mentees,Last Assigned (days ago)\n') for current in current_mentee_status: days_ago = (datetime.now() - current['most_recent'] ).days if current['most_recent'] else 'N/A' outfile.write( f'{current["mentor"]},{current["active_count"]},{days_ago}\n' )