PKNkkmentormatch/__init__.py"""J&J Cross-Sector Mentoring Program utility that matches mentors with mentees.""" __version__ = "0.1.6" PKm_N"mentormatch/applicants/__init__.py PK0N_.#mentormatch/applicants/applicant.pyfrom bin.schema.schema import Schema class Applicant: def __init__(self, applicant_data, applicant_group): fields = Schema.get_fields_namedtuple(applicant_group) self.data = fields(**applicant_data) self.__applicant_group = applicant_group @property def applicant_group(self): return self.__applicant_group def get_full_name(self): full_name = ' '.join([self.data.first_name, self.data.last_name]) return full_name.strip() def is_same_person_as(self, other): # This is used in case a person applied more than once. # Also used to makes sure a mentee doesn't get matched with herself. return self.data.wwid == other.data.wwid def __str__(self): return f'WWID: {self.data.wwid}\t Name: {self.get_full_name()}' def has_this_much_more_experience_than(self, other): # Mentees can only be paired with mentors who have more experience than them. years_diff = self.data.years - other.data.years level_diff = self.data.position_level - other.data.position_level if 0 < level_diff: return level_diff elif 0 == level_diff and 7 <= years_diff: return 0 else: return -1 def get_variable_data(self): # TODO bad function get_name # TODO pseudocode # experience years # genders # sites # check the Schema for full list pass if __name__ == '__main__': pass PKN .mentormatch/applicants/applicants_generator.pyfrom bin.excel_data_handling.excel_data_handler import ExcelDataHandler from bin.applicants.applicants_group import ApplicantsGroup from bin.schema.schema import Schema class ApplicantsGenerator: def __init__(self, applicant_data): self.__ready = False if applicant_data: self.__applicant_data = applicant_data self.__ready = True def generate_applicants(self): if self.__ready: # Convert Excel Data to Objects applicants = dict() for group in Schema.applicant_groups: applicants[group] = ApplicantsGroup(self.__applicant_data[group], group) # TODO # Quality check on mentor/ee containers. # Sites names and counts # Gender names and counts # Number of mentors and mentees else: return False # TODO should return None instead PKNGL*mentormatch/applicants/applicants_group.pyfrom bin.applicants.mentor_mentee import MentorApplicant, MenteeApplicant class ApplicantsGroup: """Objects of this class will house either all mentors or all mentees""" def __init__(self, worksheet_, applicant_group): self.__worksheet = worksheet_ self.__applicant_group = applicant_group def generate_unique_applicants(self): applicants = [self.__generate_applicant(row_) for row_ in self.__worksheet] unique_wwids = set([applicant.data.wwid for applicant in applicants]) unique_applicants = [] for wwid in unique_wwids: applicants_with_this_wwid = [applicant for applicant in applicants if applicant.data.wwid == wwid] most_recent_of_these_applicants = self.__get_most_recent_applicant(applicants_with_this_wwid) unique_applicants.append(most_recent_of_these_applicants) return unique_applicants def __generate_applicant(self, row_): available_classes = {'mentors': MentorApplicant, 'mentees': MenteeApplicant} applicant_class = available_classes[self.__applicant_group] applicant = applicant_class(row_) return applicant @staticmethod def __get_most_recent_applicant(applicants): # TODO need to code this # for now, I'll just return the first return applicants[0] def log_report(self): # TODO pseudocode # for each mentor/ee # collect all values from these 'keys': # sites, gender, etc... # store counter of each (e.g. I want to know how many times Fort Washington appears # (use counters) https://docs.python.org/2/library/collections.html#collections.Counter pass PKqNvv'mentormatch/applicants/mentor_mentee.pyfrom bin.applicants.applicant import Applicant class MentorApplicant(Applicant): def __init__(self, applicant_data): super().__init__(applicant_data, 'mentors') class MenteeApplicant(Applicant): def __init__(self, applicant_data): super().__init__(applicant_data, 'mentees') def ranking_of_this_mentor(self, mentor): if 'preferred_mentors' in self.preferences: preferred_mentors = self.preferences['preferred_mentors'] if type(preferred_mentors) is list: preferred_mentors.identification() else: return -1 PKN+mentormatch/excel_data_handling/__init__.pyPKkN֑  .mentormatch/excel_data_handling/data_fields.pyfrom bin.schema.schema import Schema import logging class ApplicationDataFields: #################### # MAGIC METHODS #################### def __init__(self, applicant_group): self.__applicant_group = applicant_group self.__fields = Schema.get_fields(applicant_group) self.__cur_index = 0 # For iterating over the fields def get_fields(self): return self.__fields #################### # INSTANCE METHODS #################### def log_missing_and_unused_fields(self, worksheet_): stuff = {'missing fields': self.__get_missing_fields(self.__fields), 'unused fields': self.__get_unused_fields(worksheet_)} for kind_of_fields, fields in stuff.items(): logging.info(f'Checking `{self.__applicant_group}` worksheet for {kind_of_fields} ...') if len(fields) == 0: logging.info(f'No {kind_of_fields} found. Nice job!\n') else: logging.warning(f'Found these {kind_of_fields}: {fields}\n') #################### # STATIC METHODS #################### @staticmethod def __get_missing_fields(fields): missing_fields = [] for field in fields: if field.was_found_missing(): missing_fields.append(field.get_name()) return sorted(missing_fields) @staticmethod def __get_unused_fields(worksheet_): unused_fields = set() for row in worksheet_: unused_fields.update(row.keys()) return sorted(unused_fields) if __name__ == '__main__': df_ = ApplicationDataFields('mentors') for field_ in df_: print(field_) print(field_.validate_value('hello')) PKNe""5mentormatch/excel_data_handling/excel_data_handler.pyimport random import logging import openpyxl from pathlib import Path from bin.excel_data_handling.data_fields import ApplicationDataFields from bin.schema.schema import Schema # TODO ExcelDataHandler probably shouldn't be considered a "utility" anymore. # It's grown very matchmaker-specific class ExcelDataHandler: def __init__(self, data_path): """Return cleaned up, ready-to-be-used excel applicant while logging applicant-quality messages to user :param data_path: Path object - path to excel file. :return: False if there were errors """ self.__workbook_is_validated = False self.__path = data_path self.__validated_workbook = None def __validate_workbook(self, unvalidated_workbook): self.__log_unneeded_worksheets(unvalidated_workbook) validated_workbook = dict() for applicant_group in Schema.applicant_groups: if self.__worksheet_exists(unvalidated_workbook, applicant_group): unvalidated_worksheet = unvalidated_workbook[applicant_group] wb = self.__validate_worksheet(unvalidated_worksheet, applicant_group) validated_workbook[applicant_group] = wb else: return False return validated_workbook def __validate_worksheet(self, unvalidated_worksheet, applicant_group): validated_worksheet = [] fields = ApplicationDataFields(applicant_group) for unvalidated_applicant in unvalidated_worksheet: validated_applicant = self.__validate_applicant(unvalidated_applicant, fields.get_fields()) validated_worksheet.append(validated_applicant) fields.log_missing_and_unused_fields(unvalidated_worksheet) self.__log_worksheet_summary(validated_worksheet, applicant_group) return validated_worksheet @staticmethod def __log_worksheet_summary(worksheet_, applicant_group): applicant_count = len(worksheet_) logging.info(f'Counting {applicant_group} ...') if applicant_count == 0: logging.warning('None found!\n') else: logging.info(f'Found {applicant_count}. Nice job!') string_of_applicants = 'Here are several randomly selected __applicants:\n' for i in list(range(3)): rand_index = random.randint(0, applicant_count - 1) string_of_applicants += f'\n{worksheet_[rand_index]}\n' logging.info(string_of_applicants) @staticmethod def __worksheet_exists(workbook_, worksheet_name): logging.info(f'Looking for `{worksheet_name}` worksheet ...') found = False if worksheet_name in workbook_: logging.info('Found. Nice job!\n') found = True else: logging.error(f'Not found! Make sure your workbook contains a `{worksheet_name}`` worksheet\n') return found @staticmethod def __log_unneeded_worksheets(workbook_): """Report to user if there are worksheets other than 'mentors' and 'mentees'.""" logging.info('Checking for unneeded worksheets ...') all_workbooks = set(workbook_.keys()) desired_workbooks = set(Schema.applicant_groups) unneeded_workbooks = all_workbooks - desired_workbooks if len(unneeded_workbooks) == 0: logging.info('None found. Nice job!\n') else: logging.warning(f'Found some unneeded workbooks: {unneeded_workbooks}\n') @staticmethod def __validate_applicant(unvalidated_applicant, fields): validated_applicant = dict() for field in fields: key = field.get_name() value = field.validate_field(unvalidated_applicant) validated_applicant[key] = value return validated_applicant def generate_validated_workbook_(self): if not self.__workbook_is_validated: unvalidated_workbook = self.__get_dicts_from_excel(self.__path) self.__validated_workbook = self.__validate_workbook(unvalidated_workbook) self.__workbook_is_validated = True return self.__validated_workbook # TODO this should return None is there's an error, not False. @staticmethod def __get_dicts_from_excel(path): """Get excel applicant. Log status. :param path: pathlib.Path() object """ # Return a dictionary. Keys = worksheet names; Values = worksheet contents # Those Values are themselves lists of dictionaries. Each list item is a worksheet row. # Each worksheet row is a dictionary. Keys = column names; Values = cell values logging.info(f'Looking for excel applicant in `{path}` ...') try: wb = openpyxl.load_workbook(path) logging.info('Found!\n') except: logging.error('Not found!\n') return False workbook_contents = dict() for worksheet_name in wb.sheetnames: ws = wb[worksheet_name] worksheet_contents = [] # Convert each row to a dictionary, using the column headers as keys for row in range(2, ws.max_row + 1): row_contents = dict() for col in range(1, ws.max_column + 1): key = ws.cell(1, col).value value = ws.cell(row, col).value # logging.debug(value) row_contents[key] = value worksheet_contents.append(row_contents) workbook_contents[worksheet_name] = worksheet_contents return workbook_contents if __name__ == '__main__': # Set up logging logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) # Function test excel_path = Path(__file__).parent.parent.parent / 'applicant' / 'private' / 'applications.xlsx' excel_data_handler = ExcelDataHandler(excel_path) excel_data = excel_data_handler.generate_validated_workbook_() # if excel_data: # for mentor in excel_data['mentors']: # print(mentor) # else: # logging.error('Oh no!!') PK4Nmentormatch/main/__init__.pyPKNQܙmentormatch/main/cli.py# -*- coding: utf-8 -*- """Console script for mentormatch.""" import click from .get_path_from_user import get_path_from_user @click.command() @click.option("--version", "-v", is_flag=True) def main(version): if version: string_ = "version: " + __version__ click.echo(string_) return fdr_excel_path = get_path_from_user() click.echo("hello") if __name__ == "__main__": sys.exit(main()) # pragma: no cover PKBNƹww&mentormatch/main/get_path_from_user.pyimport tkinter as tk from tkinter import filedialog from pathlib import Path def get_path_from_user(): root = tk.Tk() root.withdraw() file_path = Path(filedialog.askopenfilename()) return file_path if __name__ == "__main__": print("Hello World") print("This is the file you selected:", get_path()) input("Press Enter to exit: ")PK@Nmentormatch/roster/__init__.pyPK3Y\{Ϳ̒9k-$g̥h6^b&kC7^b}zPvcx[X\2GEpA)55+; n/D7Ƥm^zߺq.Se4HUkH |6>|KcIဂVưf8Bn=%L ik|r޵Dgu^L*^j=a<)5+|ܩ٩)e;8Ve`^@B\Vm)⧜hNCQ~ H՞㦨+wc~|2'#^~q_A[tɑvJZWG{!tͭ]+5VKڮjKR* K[9>mآ xĞʕv"zD(OEs\5W4٥ ^(iW.?M֣ב3.YёQI$OoGEgFZq1$k^$wqh4mU5h)uJx Dd,1Y sVX"?MWPb:5=q֕LcaH&Ȇ6; Pq}'pv0t[ߛ{\Kh)t K)T~Mnnn<"^^g1m&>E8$6尢W4XP1/98۶C}dj׊^^EspmN1v *b4"N-kw s(O2ebGk%E$UIj"%UC6sR)t&lNQ!kj5KQ6HP\n56ܛxIV8.Fi(SMlOQT]-Ͷ7PK!HI& "mentormatch-0.1.6.dist-info/RECORDI8[}3 (HLX4F:I@|v[{תu'&׳ΛBu[(Jʐ5#Ӏdkl/k\k#dkocP'@0*pAYT"_h7^ .ƾjB"͓w~uȃKpϸ߻OS?MsA(%F+4E'naҿeC?hOO2aFcZ3kD>wxF' љSgUںkQGc)$}GKIO u߯ >2L%U_KKZ21N{.0?iPEV%$fy -@wdtQ]oMN ?]3PYjx5<;#Q!2гM8[;ghC"6(q_-#:fFX?AVYމQ=N[rBAR*(iů s{>ACJzatpn9i/˄ˊ /1hr!3KQ8oERP{C:YӞu6uڇ]%dh}#h~3E6=۝vf$NSmQ}exlc| ˜8%|(eg+Gr)x+GXl+|+oE َnQmoLbAeQZ-31ë|}%^9hRR&Džq ~[ˑg˳V)Qb|Xdʊ e{A$-SyePu[] U9G"`¡3qs< d>^MJM(殛vzq3s[!9a {y\\xcO*.x{+06犹?( xc EV]W7u6LЀibHʒ'H6*T3tcc Kv%'@Yz%F|یx0bzP8 L_ݍ,￑Me݀;ZZN ^y<[@"L5%g#1qgނp]ߜU3!&?j;đzk) S°PKNkkmentormatch/__init__.pyPKm_N"mentormatch/applicants/__init__.pyPK0N_.#mentormatch/applicants/applicant.pyPKN .?mentormatch/applicants/applicants_generator.pyPKNGL*p mentormatch/applicants/applicants_group.pyPKqNvv'mentormatch/applicants/mentor_mentee.pyPKN+mentormatch/excel_data_handling/__init__.pyPKkN֑  .mentormatch/excel_data_handling/data_fields.pyPKNe""5$mentormatch/excel_data_handling/excel_data_handler.pyPK4N5mentormatch/main/__init__.pyPKNQܙ5mentormatch/main/cli.pyPKBNƹww&7mentormatch/main/get_path_from_user.pyPK@N9mentormatch/roster/__init__.pyPK3