text: # ==========================================
  # Title       : Weekly Shuffle Pattern Generator
  # Repository  : https://github.com/DSC-ASEB/Weekly-Shuffle-Partner-Generator
  # ==========================================
  
  import os
  import sys
  import numpy as np
  import pandas as pd
  
  
  def check_database(database):
  	'''
  	It checks and prints the status if any duplicates exist by column wise in the input dataframe
  
  	Parameters:
  	-----
  	Pandas dataframe (Participant Database)
  
  	Returns:
  	-----
  	None
  	'''
  
  	for column in database.columns:
  		if database[column].duplicated().sum():
  			print(f'{column} - Duplicates were found in this database')
  		else:
  			print(f'{column} - Perfect')
  
  	print()
  
  
  def load_data(private_filepath, public_filepath=None):
  	'''
  	It will load the data from the input
  
  	Parameters:
  	-----
  	private_filepath : contains user xslx filepath
  	public_filepath  : contains past week partners filepath
  
  	Returns:
  	-----
  	database    : Pandas dataframe containing participant database
  	register    : Pandas dataframe containing next week participant details
  	weeks       : Contains past week participant details as pandas dataframe
  	'''
  
  	private_db = pd.ExcelFile(private_filepath)
  
  	database = private_db.parse('Final')
  	register = private_db.parse('Register')
  
  	if public_filepath is not None:
  		public_db = pd.ExcelFile(public_filepath)
  		weeks = [public_db.parse(week) for week in public_db.sheet_names if week.startswith('Week_') ]
  		return (database, register, weeks)
  
  	return (database, register, None)
  
  
  def split_partners(week, database):
  	'''
  	It splits both partner columns and status
  
  	Parameters:
  	-------
  	week           : Contain single week user data
  	database       : Contains entire participant database
  
  	Returns:
  	-------
  	partner_list_1 : partner 1 names
  	partner_list_2 : partner 2 names
  	partner_status : status of the participants
  	'''
  
  	def_partners_parse = lambda partners: [participant if participant is not np.nan else None for participant in partners]
  
  	partner_list_1 = def_partners_parse(week.Partner_1.tolist())
  	partner_list_2 = def_partners_parse(week.Partner_2.tolist())
  	partner_status = (week.Status == 'Started')
  	return (partner_list_1, partner_list_2), partner_status
  
  
  def check_partners(*, p1, p2, db_names):
  	'''
  	It checks whether partner names exist in the private database
  
  	Parameters:
  	-----
  	p1  : particpant group 1 details
  	p2  : particpant group 2 details
  	db_names : entire participant details
  
  	Returns:
  	-----
  	list of unmatched partner names to the database and None if every name matches to the database
  	'''
  
  	not_matched = []
  
  	for a, b in zip(p1, p2):
  	    if (a is not None) and (a not in db_names):
  	        not_matched.append(a)
  	    if (b is not None) and (b not in db_names):
  	        not_matched.append(b)
  
  	return None if len(not_matched) == 0 else not_matched
  
  
  def parse_weeks(weeks, database):
  	'''
  	It creates dictionary holding important information regarding Week's partners and status
  
  	Parameters:
  	-----
  	weeks    : list of week dataframes
  	database : entire user dataframe
  
  	Returns:
  	-----
  	user_dict: well structured weeks extracted data in dictionary datatype
  	'''
  
  	print('Checking for any errors in weeks:')
  
  	user_dict = {}
  
  	for (index, week) in enumerate(weeks, 1):
  
  		(week_p1, week_p2), week_status = split_partners(week, database)
  		verify = check_partners(p1=week_p1, p2=week_p2, db_names=database['Full name'].tolist())
  		print(f'Week_{index} : {verify}')
  
  		user_dict['Week_'+str(index)] = {
  			'week_p1' : week_p1,
  			'week_p2' : week_p2,
  			'week_status' : week_status,
  			'verify' : verify
  		}
  
  	print()
  
  	return user_dict
  
  
  def generate_old_pair_json(user_dict, database):
  	'''
  	It creates json of all the old weekly shuffle pairs
  
  	Parameters:
  	-----
  	user_dict : well structured weeks extracted data in dictionary datatype
  	database  : entire user dataframe
  
  	Returns:
  	-----
  	connections : dictionary generated based on users number as key and value as old pair's (partner) number. [Phone Number]
  	'''
  
  	connections = {}
  	parse_number = lambda participant: database[database['Full name'] == participant]['WhatsApp Number'].tolist()[0]
  
  	for week_name in user_dict.keys():
  		week = user_dict[week_name]
  
  		for p1, p2 in zip(week['week_p1'], week['week_p2']):
  			if (p1 is None) or (p2 is None):
  				continue
  
  			p1_num = parse_number(p1)
  			p2_num = parse_number(p2)
  
  			dict_array = connections.get(p1_num, [])
  			dict_array.append(p2_num)
  			connections[p1_num] = dict_array
  
  			dict_array = connections.get(p2_num, [])
  			dict_array.append(p1_num)
  			connections[p2_num] = dict_array
  
  	return connections
  
  
  def validate_and_parse_register(database, register):
  	'''
  	It verifies whether the register information exist in user database or not
  
  	Parameters:
  	-----
  	database  : entire user dataframe
  	register  : new week applicants dataframe
  
  	Returns:
  	-----
  	new_connections : dictionary containing their names as keys and values as email and phone number.
  	'''
  
  	retrieve_data = lambda dataframe, column : dataframe[column].tolist()
  
  	db_email = retrieve_data(database, 'Email')
  	db_number = retrieve_data(database, 'WhatsApp Number')
  	register_email = retrieve_data(register, 'Email')
  	register_number = retrieve_data(register, 'WhatsApp Number')
  	new_connections = {}
  	present = True
  
  	for user_email, user_number in zip(register_email, register_number):
  
  		if user_email in db_email:
  			usr_db = database[database['Email'] == user_email]
  			new_connections[usr_db['Full name'].tolist()[0]] = [usr_db['Email'].tolist()[0], usr_db['WhatsApp Number'].tolist()[0]]
  
  		elif user_number in db_number:
  			usr_db = database[database['WhatsApp Number'] == user_number]
  			new_connections[usr_db['Full name'].tolist()[0]] = [usr_db['Email'].tolist()[0], usr_db['WhatsApp Number'].tolist()[0]]
  		else:
  			print('[Error - Not Present - Database]')
  			print(user_email, user_number)
  			present = False
  			print()
  
  
  	return new_connections if present else None
  
  
  def generate_random_pairs(new_connections, connections=None):
  	'''
  	It generates a list of pairs using numpy's permutation function
  
  	Parameters:
  	-----
  	connections     : contains past week's participants data
  	new_connections : contains present week's particpants data
  
  	Returns:
  	-----
  	list of random pairs generated
  	'''
  	random_pair = np.random.permutation(list(new_connections.keys())).reshape(-1, 2)
  
  	if connections is None:
  		return random_pair
  
  	for p1, p2 in random_pair:
  
  		p1_num = new_connections[p1][1]
  		p2_num = new_connections[p2][1]
  
  		if p2_num in connections.get(p1_num, []):
  			return([])
  
  		if p1_num in connections.get(p2_num, []):
  			return([])
  
  	return random_pair
  
  def create_output_dataframes(random_pair, new_connections):
  	'''
  	It generates a dataframe, so it can be converted to a csv file later on
  
  	Parameters:
  	-----
  	random_pair : list of randomly generated particpants data
  	new_connections : dictionary holding register users data
  
  	Returns:
  	-----
  	output : dataframe containing participants name, email and phone number
  	'''
  
  	output = pd.DataFrame([], columns=['Partner_1','Partner_2', 'P1_Number', 'P2_Number', 'P1_Email', 'P2_Email'])
  	for p1, p2 in random_pair:
  
  		p1_details, p2_details = new_connections[p1], new_connections[p2]
  
  		usr_data = {
  			# Partner Names
  			'Partner_1': p1,'Partner_2': p2,
  
  			# Partner Whatsapp Numbers
  			'P1_Number': p1_details[1],
  			'P2_Number': p2_details[1],
  
  			# Partner Email
  			'P1_Email': p1_details[0],
  			'P2_Email': p2_details[0]
  		}
  
  		output = output.append(usr_data, ignore_index=True)
  
  	return output
  
  
  if __name__ == '__main__':
  
  	if (1 < len(sys.argv) > 4):
  		print('Incorrect number of arguments')
  		print('Usage: python run-script.py database.xlsx week_shuffle.xlsx')
  		print('Usage: python run-script.py database.xlsx # if you are just using this tool for first time')
  		sys.exit()
  
  	if (len(sys.argv) == 3):
  
  		private_url = sys.argv[1]
  		public_url  = sys.argv[2]
  
  		database, register, weeks = load_data(private_url, public_url)
  
  		print('Checking entire user database for any duplicates')
  		check_database(database)
  		user_dict = parse_weeks(weeks, database)
  		connections = generate_old_pair_json(user_dict, database)
  
  		print('Checking new register database for any duplicates')
  		check_database(register)
  
  		new_connections = validate_and_parse_register(database, register)
  
  		if new_connections is None:
  			print('\nCheck for errors in the register or database.')
  			sys.exit()
  		elif len(new_connections) % 2 != 0:
  			print('\nOdd number of participants exist in register sheet. Make it even to form pairs.')
  			sys.exit()
  
  		random_pair = []
  
  		while(len(random_pair) <= 0):
  			print("[Reshuffling]")
  			random_pair = generate_random_pairs(new_connections, connections)
  
  		output = create_output_dataframes(random_pair, new_connections)
  		print(output[['Partner_1', 'Partner_2']])
  		output.to_csv('Week_'+str(len(weeks)+1)+'.csv')
  		sys.exit()
  
  	if (len(sys.argv) == 2):
  
  		private_url = sys.argv[1]
  		database, register, _ = load_data(private_url)
  
  		print('Checking entire user database for any duplicates')
  		check_database(database)
  
  		print('Checking new register database for any duplicates')
  		check_database(register)
  
  		new_connections = validate_and_parse_register(database, register)
  
  		if new_connections is None:
  			print('\nCheck for errors in the register or database.')
  			sys.exit()
  		elif len(new_connections) % 2 != 0:
  			print('\nOdd number of participants exist in register sheet. Make it even to form pairs.')
  			sys.exit()
  
  		random_pair = generate_random_pairs(new_connections)
  
  		output = create_output_dataframes(random_pair, new_connections)
  		print(output[['Partner_1', 'Partner_2']])
  		output.to_csv('Week_1.csv')
  		sys.exit()
  