<commit_before>#!/usr/bin/env python
from __future__ import print_function

import random
import sys
from datetime import date
from uuid import uuid4

from six.moves import input

from config import GOOGLE_HANGOUT_URL, PAIRING_SIZE, SLACK_CHANNEL
from utils import YES, NO, initialize, nostdout


def get_google_hangout_url():
    return "{}{}?authuser=0".format(GOOGLE_HANGOUT_URL, uuid4())


def create_meetings(store, sc, size, whos_out, pairs, force_create=False, any_pair=False):
    """Randomly generates sets of pairs for (usually) 1 on 1 meetings for a Slack team.

    Given the `size`, list of all users and who is out today, it generates a randomized set of people
    to per group to meet and chat. It tries not to redo any groups from the past nCr weeks (experimental).

    Args:
        store (instance): A persistent, dictionary-like object used to keep information about past/future meetings
        sc (SlackClient): An instance of SlackClient
        size (int): Pair size - defaulted to PAIRING_SIZE in config.py
        whos_out (list): List of slack users who aren't available for the meeting
        pairs (list): List of slack users explictly pair up (elements of list are in the form of 'username+username')
        force_create (Optional[bool]): If True, generate the meeting and write it to storage without asking if it should.
        any_pair (Optional[bool]): If True, generate any pairing - regardless if it's happened in the past or not

    Returns:
        bool: True if successful, False otherwise.
    """
    todays_meeting = {'date': date.today(), 'attendees': []}
    found_upcoming = False
    if store.get('upcoming') and store['upcoming']['date'] == todays_meeting['date']:
        print("Found upcoming meeting, using appending whoever is listed as out from it.")
        whos_out = whos_out + store['upcoming']['out']
        found_upcoming = True

    names = [n for n in store['everyone'] if n not in whos_out]
    max_pair_size = size

    # == Handle Explicit Pairs ==
    for index, explicit_pair in enumerate(pairs):
        local_names = names[:]
        pairing = []

        try:
            members = explicit_pair.split('+')
            for member in members:
                local_names.remove(member)
                pairing.append(member)
        except:
            sys.exit("ERROR: The following explicit pair was either malformed, contained invalid user names, or has members listed as being out: {}".format(explicit_pair))

        # Store difference of names (remaining people to pair)
        max_pair_size = max(max_pair_size, len(pairing))
        names = [n for n in names if n in local_names]
        todays_meeting['attendees'].append(frozenset(pairing))

    # == Set up Random Pairing Numbers ==
    names_len = len(names)
    if names_len < 2:
        if found_upcoming:
            del store['upcoming']
        sc.api_call("chat.postMessage", channel=SLACK_CHANNEL, as_user=True,
                    text="Today's :coffee: and :bagel: has been canceled - not enough people are available!")
        sys.exit("ERROR: Not enough people to have a meeting, canceling request.")

    number_of_pairings = names_len // size
    out_remainder = names_len % size
    max_pair_size = max(max_pair_size, names_len + out_remainder + 1)
    print("Going to generate {} pairs for today's meeting...".format(number_of_pairings))
    # Get the nCr of meetings and try not to repeat a pairing
    nCr = (names_len * (names_len - 1)) // size
    previous_pairings = [set(pair) for p in store['history'][-nCr:] for pair in p['attendees']] if 'history' in store else []

    # == Handle Random Pairs ==
    attempts = 1
    max_attempts = 25
    while number_of_pairings:
        local_size = size + out_remainder
        local_names = names[:]
        pairing = []

        while local_size:
            name = random.choice(local_names)
            local_names.remove(name)
            pairing.append(name)
            local_size -= 1

        if not any_pair:
            pairing = frozenset(pairing)
            if pairing in previous_pairings and attempts <= max_attempts:
                print('Generated already existing pair, going to try again ({} attempt(s) so far)'.format(attempts))
                attempts += 1
                continue
            elif attempts > max_attempts:
                print('Max randomizing attempts reached, Got to start over again!!!!')
                return False

        # Store difference of names (remaining people to pair)
        names = [n for n in names if n in local_names]
        # Remainder should be dealt with at this point, set to zero
        out_remainder = 0
        todays_meeting['attendees'].append(pairing)
        number_of_pairings -= 1

    # == Print Pairs ==
    print('\n== Pairings for {:%m/%d/%Y} ==\n'.format(todays_meeting['date']))
    pretty_attendees = '\n'.join(format_attendees(pair, max_pair_size) for pair in todays_meeting['attendees'])
    print(pretty_attendees)
    pretty_whos_out = format_attendees([o[0] + '.' + o[1:] for o in whos_out], at=False)
    print("(Who's out: {})".format(pretty_whos_out))
    if names:
        # names should be empty if everyone is paired.
        sys.exit("\n ERROR: These people were not paired: {}".format(', '.join(names)))

    # == Generate meeting and Save ==
    while True:
        if force_create:
            answer = 'yes'
        else:
            answer = input('\nAccept and write to shelf storage? (y/n) ').lower()

        if answer in YES:
            if found_upcoming:
                del store['upcoming']
<commit_msg>Remainder updates to only put one extra person per pair<commit_after>#!/usr/bin/env python
from __future__ import print_function

import random
import sys
from datetime import date
from uuid import uuid4

from six.moves import input

from config import GOOGLE_HANGOUT_URL, PAIRING_SIZE, SLACK_CHANNEL
from utils import YES, NO, initialize, nostdout


def get_google_hangout_url():
    return "{}{}?authuser=0".format(GOOGLE_HANGOUT_URL, uuid4())


def create_meetings(store, sc, size, whos_out, pairs, force_create=False, any_pair=False):
    """Randomly generates sets of pairs for (usually) 1 on 1 meetings for a Slack team.

    Given the `size`, list of all users and who is out today, it generates a randomized set of people
    to per group to meet and chat. It tries not to redo any groups from the past nCr weeks (experimental).

    Args:
        store (instance): A persistent, dictionary-like object used to keep information about past/future meetings
        sc (SlackClient): An instance of SlackClient
        size (int): Pair size - defaulted to PAIRING_SIZE in config.py
        whos_out (list): List of slack users who aren't available for the meeting
        pairs (list): List of slack users explictly pair up (elements of list are in the form of 'username+username')
        force_create (Optional[bool]): If True, generate the meeting and write it to storage without asking if it should.
        any_pair (Optional[bool]): If True, generate any pairing - regardless if it's happened in the past or not

    Returns:
        bool: True if successful, False otherwise.
    """
    todays_meeting = {'date': date.today(), 'attendees': []}
    found_upcoming = False
    if store.get('upcoming') and store['upcoming']['date'] == todays_meeting['date']:
        print("Found upcoming meeting, using appending whoever is listed as out from it.")
        whos_out = whos_out + store['upcoming']['out']
        found_upcoming = True

    names = [n for n in store['everyone'] if n not in whos_out]
    max_pair_size = size

    # == Handle Explicit Pairs ==
    for index, explicit_pair in enumerate(pairs):
        local_names = names[:]
        pairing = []

        try:
            members = explicit_pair.split('+')
            for member in members:
                local_names.remove(member)
                pairing.append(member)
        except:
            sys.exit("ERROR: The following explicit pair was either malformed, contained invalid user names, or has members listed as being out: {}".format(explicit_pair))

        # Store difference of names (remaining people to pair)
        max_pair_size = max(max_pair_size, len(pairing))
        names = [n for n in names if n in local_names]
        todays_meeting['attendees'].append(frozenset(pairing))

    # == Set up Random Pairing Numbers ==
    names_len = len(names)
    if names_len < 2:
        if found_upcoming:
            del store['upcoming']
        sc.api_call("chat.postMessage", channel=SLACK_CHANNEL, as_user=True,
                    text="Today's :coffee: and :bagel: has been canceled - not enough people are available!")
        sys.exit("ERROR: Not enough people to have a meeting, canceling request.")

    number_of_pairings = names_len // size
    out_remainder = names_len % size
    max_pair_size = max(max_pair_size, names_len + out_remainder + 1)
    print("Going to generate {} pairs for today's meeting...".format(number_of_pairings))
    # Get the nCr of meetings and try not to repeat a pairing
    nCr = (names_len * (names_len - 1)) // size
    previous_pairings = [set(pair) for p in store['history'][-nCr:] for pair in p['attendees']] if 'history' in store else []

    # == Handle Random Pairs ==
    attempts = 1
    max_attempts = 25
    while number_of_pairings:
        # Only add an extra person per group unless at the last pair,
        # then clear out all remaining people into the last group.
        if out_remainder > 0 and number_of_pairings:
            if number_of_pairings > 1:
                remainder = 1
                out_remainder -= 1
            else:
                remainder = out_remainder
                out_remainder = 0
        else:
            remainder = 0

        local_size = size + remainder
        local_names = names[:]
        pairing = []

        while local_size:
            name = random.choice(local_names)
            local_names.remove(name)
            pairing.append(name)
            local_size -= 1

        if not any_pair:
            pairing = frozenset(pairing)
            if pairing in previous_pairings and attempts <= max_attempts:
                print('Generated already existing pair, going to try again ({} attempt(s) so far)'.format(attempts))
                attempts += 1
                continue
            elif attempts > max_attempts:
                print('Max randomizing attempts reached, Got to start over again!!!!')
                return False

        # Store difference of names (remaining people to pair)
        names = [n for n in names if n in local_names]
        todays_meeting['attendees'].append(pairing)
        number_of_pairings -= 1

    # == Print Pairs ==
    print('\n== Pairings for {:%m/%d/%Y} ==\n'.format(todays_meeting['date']))
    pretty_attendees = '\n'.join(format_attendees(pair, max_pair_size) for pair in todays_meeting['attendees'])
    print(pretty_attendees)
    pretty_whos_out = format_attendees([o[0] + '.' + o[1:] for o in whos_out], at=False)
    print("(Who's out: {})".format(pretty_whos_out))
    if names:
        # names should be empty if everyone is paired.
        sys.exit("\n ERROR: These people were not paired: {}".format(', '.join(names)))

    # == Generate meeting and Save ==
    while True:
        if force_create:
            answer = 'yes'
        else:
            answer = input('\nAccept and write to shelf storage? (y/n) ').lower()

        if answer in YES:
            if found_upcoming:
                del store['upcoming']

{'fraction_non_alphanumeric': 0.09457711871713079, 'fraction_numerical': 0.003362358824036555, 'mean_word_length': 2.672048116492561, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '724418', 'n_tokens_mistral': 3429, 'n_tokens_neox': 3284, 'n_words': 1166}