Ejemplo n.º 1
0
def get_user_form(tourney, settings, div_rounds):
    div_group_size = dict()
    div_init_max_rematches = dict()
    div_init_max_win_diff = dict()

    prev_settings = settings.get_previous_settings()
    for key in prev_settings:
        if key not in settings and key != "submit":
            settings[key] = prev_settings[key]

    rounds = tourney.get_rounds();

    num_divisions = tourney.get_num_divisions()

    max_time = int_or_none(settings.get("maxtime", None))
    ignore_rematches_before_round = int_or_none(settings.get("ignorerematchesbefore", None))

    div_ready = []
    for div in range(num_divisions):
        if div in div_rounds:
            div_ready.append(False)
        else:
            div_ready.append(True)

    default_group_size = int_or_none(settings.get("groupsize", None))

    for div_index in sorted(div_rounds):
        group_size = int_or_none(settings.get("d%d_groupsize" % (div_index), None))
        if group_size is None or group_size == 0:
            group_size = default_group_size

        init_max_rematches = int_or_none(settings.get("d%d_initmaxrematches" % (div_index), "0"))

        init_max_win_diff = int_or_none(settings.get("d%d_initmaxwindiff" % (div_index), 0))

        games = tourney.get_games(game_type='P', division=div_index);
        players = [x for x in tourney.get_active_players() if x.division == div_index];

        if max_time is not None and max_time > 0 and group_size in valid_group_sizes and (group_size == -5 or len(players) % group_size == 0):
            div_ready[div_index] = True
#        else:
#            if max_time is None or max_time == 0:
#                max_time = 30;
#            if group_size is None or group_size not in valid_group_sizes:
#                if len(players) % 3 == 0:
#                    group_size = 3
#                elif len(players) % 2 == 0:
#                    group_size = 2
#                elif len(players) % 5 == 0:
#                    group_size = 5
#                elif len(players) >= 8:
#                    group_size = -5
#                elif len(players) % 4 == 0:
#                    group_size = 4
#                else:
#                    group_size = None

        div_group_size[div_index] = group_size
        div_init_max_rematches[div_index] = init_max_rematches
        div_init_max_win_diff[div_index] = init_max_win_diff

    if False not in div_ready and settings.get("submit") is not None:
        return None

    elements = [];
    javascript = """
<script type="text/javascript">
var click_time = 0;
var limit_seconds = 0;
var noticed_results_overdue = false;

var gerunds = ["Reticulating", "Exaggerating", "Refrigerating",
            "Bisecting", "Reordering", "Unseeding", "Reconstituting",
            "Inverting", "Convolving", "Reinventing", "Overpopulating",
            "Unwedging", "Tenderising", "Refactoring", "Frobnicating",
            "Normalising", "Factorising", "Transforming", "Relaying",
            "Decoupling", "Randomising", "Ignoring", "Disposing of",
            "Translating", "Restarting", "Entertaining", "Checking",
            "Verifying", "Flushing", "Contextualising", "Deconstructing",
            "Justifying", "Hacking", "Redrawing", "Reimagining",
            "Reinterpreting", "Reasoning with", "Impersonating",
            "Abbreviating", "Underestimating", "Misappropriating",
            "Constructing", "Preparing", "Redelivering", "Arguing over",
            "Grilling", "Baking", "Poaching", "Washing", "Stealing",
            "Emulsifying", "Discombobulating", "Correcting", "Extracting",
            "Unspooling", "Descaling", "Duplicating", "Overwriting" ];

var nouns = ["seeding list", "rule book", "hypergrid",
            "network services", "timestamps", "multidimensional array",
            "decision tree", "player list", "weighting matrix",
            "instrument panel", "database", "videprinter",
            "standings table", "preclusion rules", "event handlers",
            "dynamic modules", "hypertext", "fixture generator",
            "linked lists", "hash tables", "system clock", "file descriptors",
            "syntax tree", "binary tree", "dictionary", "homework",
            "breakfast", "contextualiser", "splines", "supercluster",
            "record books", "sandwiches", "grouping strategy", "reality",
            "spatula", "Eyebergine", "scripts", "blockchain", "phone charger",
            "fixtures", "associative arrays", "browser window", "subfolders"
            ];

var endings = [
    "Bribing officials", "Talking bollocks", "Feeding cat",
    "Rewinding tape", "Invading privacy", "Falling off cliff",
    "Kicking tyres", "Tapping barometer", "Serving hot",
    "Deploying parachute", "Cleaning up mess", "Straightening tie",
    "Seasoning to taste", "Stealing towels", "Reversing polarity",
    "Untangling headphones", "Compounding misery"
];

function spam_progress_label() {
    var progress = "";
    var pc = 0;
    var ms_elapsed = 0;

    if (limit_seconds != NaN) {
        current_time = new Date();
        ms_elapsed = current_time.getTime() - click_time.getTime();
        pc = Math.floor(ms_elapsed * 100 / (limit_seconds * 1000));
        if (pc > 100) {
            pc = 100;
        }
        progress = pc.toString() + "%";
    }

    if (ms_elapsed < 500) {
        document.getElementById('progresslabel').innerHTML = "Generating fixtures...";
    }
    else if (pc < 100) {
        if (Math.random() < 0.4) {
            var gerund = "";
            var noun = "";

            gerund = gerunds[Math.floor(Math.random() * gerunds.length)];
            noun = nouns[Math.floor(Math.random() * nouns.length)];

            document.getElementById('progresslabel').innerHTML = progress + " " + gerund + " " + noun + "...";
        }
    }
    else if (ms_elapsed < limit_seconds * 1000 + 3000) {
        if (!noticed_results_overdue) {
            var ending = endings[Math.floor(Math.random() * endings.length)];
            document.getElementById('progresslabel').innerHTML = "100% " + ending + "...";
            noticed_results_overdue = true;
        }
    }
    else {
        document.getElementById('progresslabel').innerHTML = "We ought to have finished by now.";
    }
}
function generate_fixtures_clicked() {
    click_time = new Date();
    noticed_results_overdue = false;
    limit_seconds = parseInt(document.getElementById('maxtime').value) * parseInt(document.getElementById('numdivisions').value);
    // document.getElementById('generatefixtures').disabled = true;
    spam_progress_label();
    setInterval(function() { spam_progress_label(); }, 300);
}
</script>""";
    elements.append(htmlform.HTMLFragment(javascript));

    elements.append(htmlform.HTMLFragment("<h2>Overall settings</h2>"))

    div_valid_table_sizes = []
    for div_index in sorted(div_rounds):
        div_players = [x for x in tourney.get_active_players() if x.get_division() == div_index]
        sizes = get_valid_group_sizes(len(div_players), len(rounds))
        div_valid_table_sizes.append(sizes)

    table_sizes_valid_for_all_divs = []
    for size in valid_group_sizes:
        for div_sizes in div_valid_table_sizes:
            if size not in div_sizes:
                break
        else:
            table_sizes_valid_for_all_divs.append(size)
    for size in (3, 2, 5, -5, 4):
        if size in table_sizes_valid_for_all_divs:
            default_default_group_size = size
            break
    else:
        default_default_group_size = None

    
    if num_divisions > 1 and len(table_sizes_valid_for_all_divs) > 0:
        elements.append(htmlform.HTMLFragment("<p>"))
        group_size_choices = [ htmlform.HTMLFormChoice(str(gs),
            "5&3" if gs == -5 else str(gs),
            int_or_none(settings.get("groupsize", default_default_group_size)) == gs) for gs in table_sizes_valid_for_all_divs ]
        elements.append(htmlform.HTMLFormRadioButton("groupsize", "Default players per table", group_size_choices))
        elements.append(htmlform.HTMLFragment("</p>"))

    elements.append(htmlform.HTMLFragment("<p>\n"))
    elements.append(htmlform.HTMLFormTextInput("Fixture generator time limit %s(seconds)" % ("per division " if num_divisions > 1 else ""),
        "maxtime", settings.get("maxtime", "30"),
        other_attrs={"size": "3", "id" : "maxtime"}));
    elements.append(htmlform.HTMLFragment("</p>\n<p>\n"))
    elements.append(htmlform.HTMLFormTextInput("For the purpose of avoiding rematches, disregard games before round ", "ignorerematchesbefore", str(ignore_rematches_before_round) if ignore_rematches_before_round is not None else "", other_attrs={"size": "3"}));
    elements.append(htmlform.HTMLFragment(" (leave blank to count all rematches)"))
    elements.append(htmlform.HTMLFormHiddenInput("numdivisions", str(len(div_rounds)), other_attrs={"id" : "numdivisions"}))
    elements.append(htmlform.HTMLFragment("</p>\n"))
    elements.append(htmlform.HTMLFragment("<hr />\n"))

    for div_index in sorted(div_rounds):
        group_size = div_group_size[div_index]
        init_max_rematches = div_init_max_rematches[div_index]
        init_max_win_diff = div_init_max_win_diff[div_index]
        players = [x for x in tourney.get_active_players() if x.division == div_index];
        div_prefix = "d%d_" % (div_index)

        if num_divisions > 1:
            elements.append(htmlform.HTMLFragment("<h2>%s (%d active players)</h2>" % (cgicommon.escape(tourney.get_division_name(div_index)), len(players))))
        else:
            elements.append(htmlform.HTMLFragment("<h2>Fixture generation (%d active players)</h2>" % (len(players))))

        elements.append(htmlform.HTMLFragment("<p>"))

        div_valid_sizes = get_valid_group_sizes(len(players), len(rounds))
        ticked_group_size = int_or_none(settings.get(div_prefix + "groupsize"))
        if ticked_group_size is None:
            if len(table_sizes_valid_for_all_divs) > 0 and num_divisions > 1:
                # There is a "default table size" option
                ticked_group_size = 0
            else:
                ticked_group_size = get_default_group_size(len(players), len(rounds))
        group_size_choices = [ htmlform.HTMLFormChoice(str(gs), "5&3" if gs == -5 else str(gs), gs == ticked_group_size) for gs in div_valid_sizes ]
        if num_divisions > 1 and len(table_sizes_valid_for_all_divs) > 0:
            group_size_choices = [ htmlform.HTMLFormChoice("0", "Round default (above)", ticked_group_size == 0) ] + group_size_choices

        elements.append(htmlform.HTMLFormRadioButton(div_prefix + "groupsize", "Players per table", group_size_choices))
        elements.append(htmlform.HTMLFragment("</p>\n"))
        elements.append(htmlform.HTMLFragment("<p>Increase the following values if the fixture generator has trouble finding a grouping within the time limit.</p>\n"));
        
        elements.append(htmlform.HTMLFragment("<blockquote>"))
        elements.append(htmlform.HTMLFormTextInput("Initial maximum rematches between players", div_prefix + "initmaxrematches", str(init_max_rematches), other_attrs={"size" : "3"}))
        elements.append(htmlform.HTMLFragment("</blockquote>\n<blockquote>"))
        elements.append(htmlform.HTMLFormTextInput("Initial maximum win count difference between players", div_prefix + "initmaxwindiff", str(init_max_win_diff), other_attrs={"size" : "3"}))
        elements.append(htmlform.HTMLFragment("</blockquote>\n"))
        if num_divisions > 1:
            elements.append(htmlform.HTMLFragment("<hr />\n"))

    elements.append(htmlform.HTMLFormSubmitButton("submit", "Generate Fixtures", other_attrs={"onclick": "generate_fixtures_clicked();", "id": "generatefixtures", "class" : "bigbutton"}));
    elements.append(htmlform.HTMLFragment("<p id=\"progresslabel\">For large numbers of players or unusual formats, fixture generation is not immediate - it can take up to the specified number of seconds, or longer if no permissible configurations are found in that time.</p><hr /><p></p>"));
    elements.append(htmlform.HTMLFragment("<noscript>Your browser doesn't have Javascript enabled, which means you miss out on progress updates while fixtures are being generated.</noscript>"));

    form = htmlform.HTMLForm("POST", "/cgi-bin/fixturegen.py", elements);
    return form;
Ejemplo n.º 2
0
def get_user_form(tourney, settings, div_rounds):
    num_divisions = tourney.get_num_divisions()
    div_num_games = dict()
    div_game_types = dict()

    players = sorted(tourney.get_active_players(), key=lambda x: x.get_name())

    latest_round_no = tourney.get_latest_round_no()
    if latest_round_no is None:
        latest_round_no = 0

    prev_settings = settings.get_previous_settings()
    for key in prev_settings:
        if key not in settings and re.match("^d[0-9]*_groupsize$", key):
            settings[key] = prev_settings[key]
    if settings.get("submitrestore", None):
        for key in prev_settings:
            if key not in ["submit", "submitrestore", "submitplayers"]:
                settings[key] = prev_settings[key]

    elements = []
    elements.append(htmlform.HTMLFormHiddenInput("numgamessubmit", "1"))
    elements.append(
        htmlform.HTMLFormHiddenInput("roundno", str(latest_round_no + 1)))

    # If there's a previously-saved form for this round, offer to load it
    prev_settings = settings.get_previous_settings()
    round_no = int_or_none(prev_settings.get("roundno", None))
    if round_no is not None and round_no == latest_round_no + 1:
        elements.append(
            htmlform.HTMLFragment("<div class=\"infoboxcontainer\">"))
        elements.append(htmlform.HTMLFragment("<div class=\"infoboximage\">"))
        elements.append(
            htmlform.HTMLFragment(
                "<img src=\"/images/info.png\" alt=\"Info\" />"))
        elements.append(htmlform.HTMLFragment("</div>"))
        elements.append(
            htmlform.HTMLFragment("<div class=\"infoboxmessagecontainer\">"))
        elements.append(
            htmlform.HTMLFragment("<div class=\"infoboxmessage\">"))
        elements.append(htmlform.HTMLFragment("<p>"))
        elements.append(
            htmlform.HTMLFragment(
                "There is an incomplete fixtures form saved. Do you want to carry on from where you left off?"
            ))
        elements.append(htmlform.HTMLFragment("</p>"))
        elements.append(htmlform.HTMLFragment("<p>"))
        elements.append(
            htmlform.HTMLFormSubmitButton("submitrestore",
                                          "Restore previously-saved form"))
        elements.append(htmlform.HTMLFragment("</p>"))
        elements.append(htmlform.HTMLFragment("</div></div></div>"))

    # When we pass these settings to fixgen_manual, we don't want it asking
    # awkward questions about the number of players in a group when we're
    # fixing at it two, so tell it that's already been submitted.
    settings["tablesizesubmit"] = "1"

    for div_index in div_rounds:
        num_games_name = "d%d_num_groups" % (div_index)
        game_type_name = "d%d_game_type" % (div_index)

        # For fully-manual, number of players per group is always 2, and
        # we're allowed to put a player on more than one table
        settings["d%d_groupsize" % (div_index)] = "2"
        settings["d%d_allow_player_repetition" % (div_index)] = "1"

        # Also we want fixgen_manual to show the standings table for each
        # division.
        settings["d%d_show_standings" % (div_index)] = "1"

        if settings.get(num_games_name, None) is not None:
            try:
                div_num_games[div_index] = int(settings.get(num_games_name))
                if div_num_games[div_index] < 0:
                    div_num_games[div_index] = 0
            except ValueError:
                div_num_games[div_index] = 0
        else:
            div_num_games[div_index] = 0

        if settings.get(game_type_name, None) is not None:
            try:
                div_game_types[div_index] = settings.get(game_type_name)
                if div_game_types[div_index] in special_round_names:
                    settings["d%d_round_name" %
                             (div_index)] = special_round_names[
                                 div_game_types[div_index]]
            except ValueError:
                div_game_types[div_index] = None

        if num_divisions > 1:
            elements.append(
                htmlform.HTMLFragment(
                    "<h2>%s</h2>" %
                    (cgicommon.escape(tourney.get_division_name(div_index)))))

        elements.append(htmlform.HTMLFragment("<div class=\"fixgenoption\">"))
        num_games_element = htmlform.HTMLFormTextInput(
            "Number of games to create", num_games_name, "")
        elements.append(num_games_element)
        elements.append(htmlform.HTMLFragment("</div>"))

        elements.append(htmlform.HTMLFragment("<div class=\"fixgenoption\">"))
        elements.append(htmlform.HTMLFragment("Create games of this type: "))

        game_type_options = [
            htmlform.HTMLFormDropDownOption(x["code"],
                                            x["name"] + " (" + x["code"] + ")")
            for x in countdowntourney.get_game_types()
        ]
        type_element = htmlform.HTMLFormDropDownBox(
            "d%d_game_type" % (div_index), game_type_options)

        current_setting = settings.get("d%d_game_type" % (div_index))
        if current_setting:
            type_element.set_value(current_setting)
        elements.append(type_element)
        elements.append(htmlform.HTMLFragment("</div>"))

    num_games_total = sum([div_num_games[x] for x in div_num_games])

    if num_games_total == 0 or not (settings.get("numgamessubmit", "")):
        elements.append(htmlform.HTMLFragment("<div class=\"fixgenoption\">"))
        elements.append(htmlform.HTMLFormSubmitButton("submit", "Continue"))
        elements.append(htmlform.HTMLFragment("</div>"))
        return htmlform.HTMLForm(
            "POST", "/cgi-bin/fixturegen.py?tourney=%s" %
            (urllib.parse.quote_plus(tourney.name)), elements)
    else:
        return fixgen_manual.get_user_form(tourney, settings, div_rounds)
Ejemplo n.º 3
0
def get_user_form(tourney, settings, div_rounds):
    num_divisions = tourney.get_num_divisions()
    div_table_sizes = dict()
    players = sorted(tourney.get_active_players(), key=lambda x: x.get_name())

    latest_round_no = tourney.get_latest_round_no()
    if latest_round_no is None:
        latest_round_no = 0

    prev_settings = settings.get_previous_settings()
    for key in prev_settings:
        if key not in settings and re.match("^d[0-9]*_groupsize$", key):
            settings[key] = prev_settings[key]
    if settings.get("submitrestore", None):
        for key in prev_settings:
            if key not in ["submit", "submitrestore", "submitplayers"]:
                settings[key] = prev_settings[key]

    elements = []
    elements.append(htmlform.HTMLFormHiddenInput("tablesizesubmit", "1"))
    elements.append(
        htmlform.HTMLFormHiddenInput("roundno", str(latest_round_no + 1)))

    # If there's a previously-saved form for this round, offer to load it
    prev_settings = settings.get_previous_settings()
    round_no = int_or_none(prev_settings.get("roundno", None))
    if round_no is not None and round_no == latest_round_no + 1:
        elements.append(
            htmlform.HTMLFragment("<div class=\"infoboxcontainer\">"))
        elements.append(htmlform.HTMLFragment("<div class=\"infoboximage\">"))
        elements.append(
            htmlform.HTMLFragment(
                "<img src=\"/images/info.png\" alt=\"Info\" />"))
        elements.append(htmlform.HTMLFragment("</div>"))
        elements.append(
            htmlform.HTMLFragment("<div class=\"infoboxmessagecontainer\">"))
        elements.append(
            htmlform.HTMLFragment("<div class=\"infoboxmessage\">"))
        elements.append(htmlform.HTMLFragment("<p>"))
        elements.append(
            htmlform.HTMLFragment(
                "There is an incomplete fixtures form saved. Do you want to carry on from where you left off?"
            ))
        elements.append(htmlform.HTMLFragment("</p>"))
        elements.append(htmlform.HTMLFragment("<p>"))
        elements.append(
            htmlform.HTMLFormSubmitButton("submitrestore",
                                          "Restore previously-saved form"))
        elements.append(htmlform.HTMLFragment("</p>"))
        elements.append(htmlform.HTMLFragment("</div></div></div>"))

    for div_index in div_rounds:
        div_players = [x for x in players if x.get_division() == div_index]
        table_size = None
        table_size_name = "d%d_groupsize" % (div_index)
        if settings.get(table_size_name, None) is not None:
            try:
                div_table_sizes[div_index] = int(settings.get(table_size_name))
            except ValueError:
                div_table_sizes[div_index] = None
        else:
            div_table_sizes[div_index] = None
        choices = []

        # Number of groups may be specified by fully-manual generator.
        # If it isn't, then use all the players.
        num_groups = int_or_none(settings.get("d%d_num_groups" % (div_index)))
        if num_groups:
            if div_table_sizes[
                    div_index] is not None and div_table_sizes[div_index] <= 0:
                raise countdowntourney.FixtureGeneratorException(
                    "%s: invalid table size for fully-manual setup." %
                    (tourney.get_division_name(div_index)))
        else:
            for size in (2, 3, 4, 5):
                if num_groups or len(div_players) % size == 0:
                    choices.append(
                        htmlform.HTMLFormChoice(
                            str(size), str(size),
                            size == div_table_sizes[div_index]))
            if len(div_players) >= 8:
                choices.append(
                    htmlform.HTMLFormChoice("-5", "5&3",
                                            div_table_sizes[div_index] == -5))

            if not choices:
                raise countdowntourney.FixtureGeneratorException(
                    "%s: number of players (%d) is not compatible with any supported table size."
                    % (tourney.get_division_name(div_index), len(div_players)))

        if num_divisions > 1:
            elements.append(
                htmlform.HTMLFragment(
                    "<h2>%s</h2>" %
                    (cgicommon.escape(tourney.get_division_name(div_index)))))

        elements.append(htmlform.HTMLFragment("<p>"))
        elements.append(
            htmlform.HTMLFormRadioButton(table_size_name, "Players per table",
                                         choices))
        elements.append(htmlform.HTMLFragment("</p>"))

    all_table_sizes_given = True
    for div in div_table_sizes:
        if div_table_sizes.get(div) is None:
            all_table_sizes_given = False

    if not all_table_sizes_given or not (settings.get("tablesizesubmit", "")):
        elements.append(htmlform.HTMLFragment("<p>"))
        elements.append(
            htmlform.HTMLFormSubmitButton(
                "submit", "Submit table sizes and select players"))
        elements.append(htmlform.HTMLFragment("</p>"))
        return htmlform.HTMLForm(
            "POST", "/cgi-bin/fixturegen.py?tourney=%s" %
            (urllib.parse.quote_plus(tourney.name)), elements)

    show_already_assigned_players = bool(settings.get("showallplayers"))

    div_num_slots = dict()
    for div_index in div_rounds:
        num_groups = int_or_none(settings.get("d%d_num_groups" % (div_index)))
        div_players = [x for x in players if x.get_division() == div_index]
        table_size = div_table_sizes[div_index]

        if num_groups:
            # If num_groups is specified, then we can't use the 5&3 setup.
            # If it's any other table setup then the number of player slots is
            # the number of players per table times num_groups.
            if table_size <= 0:
                raise countdowntourney.FixtureGeneratorException(
                    "%s: invalid table size for fully-manual setup" %
                    (tourney.get_division_name(div_index)))
            else:
                num_slots = table_size * num_groups
        else:
            # If num_groups is not specified, then the number if slots is
            # simply the number of active players in this division.
            num_slots = len(div_players)

        div_num_slots[div_index] = num_slots

        if table_size > 0 and num_slots % table_size != 0:
            raise countdowntourney.FixtureGeneratorException(
                "%s: table size of %d is not allowed, as the number of player slots (%d) is not a multiple of it."
                %
                (tourney.get_division_name(div_index), table_size, num_slots))

        if table_size == -5 and num_slots < 8:
            raise countdowntourney.FixtureGeneratorException(
                "%s: can't use table sizes of five and three - you need at least 8 players and you have %d"
                % (tourney.get_division_name(div_index), num_slots))

        if table_size not in (2, 3, 4, 5, -5):
            raise countdowntourney.FixtureGeneratorException(
                "%s: invalid table size: %d" %
                (tourney.get_division_name(div_index), table_size))

    div_set_players = dict()
    div_duplicate_slots = dict()
    div_empty_slots = dict()
    div_invalid_slots = dict()
    div_count_in_standings = dict()
    div_set_text = dict()
    div_game_type = dict()
    all_filled = True
    for div_index in div_rounds:
        div_players = [x for x in players if x.get_division() == div_index]
        num_groups = int_or_none(settings.get("d%d_num_groups" % (div_index)))

        num_slots = div_num_slots[div_index]

        set_players = [None for i in range(0, num_slots)]
        set_text = ["" for i in range(0, num_slots)]

        game_type = settings.get("d%d_game_type" % (div_index))

        if not game_type:
            if not settings.get("submitplayers"):
                count_in_standings = True
            else:
                count_in_standings = settings.get("d%d_heats" % (div_index))
                if count_in_standings is None:
                    count_in_standings = False
                else:
                    count_in_standings = True
        else:
            count_in_standings = (game_type == "P")

        # Slot numbers which contain text that doesn't match any player name
        invalid_slots = []

        allow_player_repetition = int_or_none(
            settings.get("d%d_allow_player_repetition" % (div_index)))
        if allow_player_repetition is None:
            allow_player_repetition = False
        else:
            allow_player_repetition = bool(allow_player_repetition)

        # Ask the user to fill in N little drop-down boxes, where N is the
        # number of players, to decide who's going on what table.
        for player_index in range(0, num_slots):
            name = settings.get("d%d_player%d" % (div_index, player_index))
            if name is None:
                name = ""
            set_text[player_index] = name
            if name:
                set_players[player_index] = lookup_player(div_players, name)
                if set_players[player_index] is None:
                    invalid_slots.append(player_index)
            else:
                set_players[player_index] = None

        # Slot numbers which contain a player already contained in another slot
        duplicate_slots = []

        # Slot numbers which don't contain a player
        empty_slots = []

        player_index = 0
        for p in set_players:
            if player_index in invalid_slots:
                all_filled = False
            elif p is None:
                empty_slots.append(player_index)
                all_filled = False
            else:
                if not allow_player_repetition:
                    count = 0
                    for q in set_players:
                        if q is not None and q.get_name() == p.get_name():
                            count += 1
                    if count > 1:
                        duplicate_slots.append(player_index)
                        all_filled = False
            player_index += 1

        div_set_players[div_index] = set_players
        div_duplicate_slots[div_index] = duplicate_slots
        div_empty_slots[div_index] = empty_slots
        div_invalid_slots[div_index] = invalid_slots
        div_count_in_standings[div_index] = count_in_standings
        div_set_text[div_index] = set_text
        div_game_type[div_index] = game_type

    interface_type = int_or_none(
        settings.get("interfacetype", INTERFACE_AUTOCOMPLETE))

    if all_filled and settings.get("submitplayers"):
        # All slots filled, don't need to ask the user anything more
        return None

    elements = []
    elements.append(
        htmlform.HTMLFormHiddenInput("roundno", str(latest_round_no + 1)))
    elements.append(
        htmlform.HTMLFragment("""<style type=\"text/css\">
table.seltable {
    margin-top: 20px;
}
.seltable td {
    padding: 2px;
    border: 2px solid white;
}
td.tablenumber {
    font-family: "Cabin";
    background-color: blue;
    color: white;
    text-align: center;
    min-width: 1.5em;
}
.duplicateplayer {
    background-color: violet;
}
.emptyslot {
    /*background-color: #ffaa00;*/
}
.invalidslot {
    background-color: red;
}
.validslot {
    background-color: #00cc00;
}
</style>
"""))
    elements.append(
        htmlform.HTMLFragment("""<script>
function set_unsaved_data_warning() {
    if (window.onbeforeunload == null) {
        window.onbeforeunload = function() {
            return "You have modified entries on this page and not submitted them. If you navigate away from the page, these changes will be lost.";
        };
    }
}

function unset_unsaved_data_warning() {
    window.onbeforeunload = null;
}
</script>
"""))

    autocomplete_script = "<script>\n"
    autocomplete_script += "var divPlayerNames = "

    div_player_names = {}
    for div_index in div_rounds:
        name_list = [
            x.get_name() for x in players if x.get_division() == div_index
        ]
        div_player_names[div_index] = name_list
    autocomplete_script += json.dumps(div_player_names, indent=4) + ";\n"

    autocomplete_script += """
function setLastEditedBox(controlId) {
    var lastEdited = document.getElementById("lasteditedinput");
    if (lastEdited != null) {
        lastEdited.value = controlId;
    }
}

function editBoxEdit(divIndex, controlId) {
    var control = document.getElementById(controlId);
    if (control == null)
        return;

    setLastEditedBox(controlId);

    var value = control.value;
    //console.log("editBoxEdit() called, value " + value);
    var previousValue = control.getAttribute("previousvalue");

    /* If the change has made the value longer, then proceed. Otherwise don't
       do any autocompletion because that would interfere with the user's
       attempt to backspace out the text. */
    //console.log("editBoxEdit() called, value " + value + ", previousValue " + previousValue);

    control.setAttribute("previousvalue", value);

    if (previousValue != null && value.length <= previousValue.length) {
        return;
    }

    /* Take the portion of the control's value from the start of the string
       to the start of the selected part. If that string is the start of
       exactly one player's name, then:
       1. Set the control's value to the player's full name
       2. Highlight the added portion
       3. Leave the cursor where it was before.
    */
    var validNames = divPlayerNames[divIndex];
    if (validNames) {
        var lastMatch = null;
        var numMatches = 0;
        var selStart = control.selectionStart;

        // head is the part the user typed in, i.e. the bit not highlighted
        var head = value.toLowerCase().substring(0, selStart);
        for (var i = 0; i < validNames.length; ++i) {
            if (validNames[i].toLowerCase().startsWith(head)) {
                numMatches++;
                lastMatch = validNames[i];
            }
        }

        if (numMatches == 1) {
            control.focus();
            control.value = lastMatch;
            control.setSelectionRange(head.length, lastMatch.length);
        }
    }
}
"""

    autocomplete_script += "</script>\n"
    elements.append(htmlform.HTMLFragment(autocomplete_script))

    elements.append(
        htmlform.HTMLFragment(
            "<p>Enter player names below. Each horizontal row is one group, or table.</p>"
        ))

    choice_data = [("Auto-completing text boxes", INTERFACE_AUTOCOMPLETE),
                   ("Drop-down boxes", INTERFACE_DROP_DOWN),
                   ("Combo boxes (not supported on all browsers)",
                    INTERFACE_DATALIST)]
    choices = [
        htmlform.HTMLFormChoice(str(x[1]), x[0], interface_type == x[1])
        for x in choice_data
    ]
    interface_menu = htmlform.HTMLFormRadioButton(
        "interfacetype", "Player name selection interface", choices)
    elements.append(interface_menu)

    if interface_type == INTERFACE_DROP_DOWN:
        elements.append(htmlform.HTMLFragment("<div class=\"fixgenoption\">"))
        elements.append(
            htmlform.HTMLFormCheckBox(
                "showallplayers",
                "Show all players in drop-down boxes, even those already assigned a table",
                show_already_assigned_players))
        elements.append(htmlform.HTMLFragment("</div>"))

    (acc_tables, acc_default) = tourney.get_accessible_tables()

    table_no = 1
    for div_index in div_rounds:
        div_players = [x for x in players if x.get_division() == div_index]
        player_index = 0
        table_size = div_table_sizes[div_index]
        duplicate_slots = div_duplicate_slots[div_index]
        invalid_slots = div_invalid_slots[div_index]
        empty_slots = div_empty_slots[div_index]
        set_players = div_set_players[div_index]
        set_text = div_set_text[div_index]
        num_slots = div_num_slots[div_index]
        game_type = div_game_type[div_index]

        if num_divisions > 1:
            elements.append(
                htmlform.HTMLFragment(
                    "<h2>%s</h2>" %
                    (cgicommon.escape(tourney.get_division_name(div_index)))))

        if not game_type:
            # Ask the user if they want these games to count towards the
            # standings table (this is pretty much universally yes)
            elements.append(
                htmlform.HTMLFragment("<div class=\"fixgenoption\">"))
            elements.append(
                htmlform.HTMLFormCheckBox(
                    "d%d_heats" % (div_index),
                    "Count the results of these matches in the standings table",
                    div_count_in_standings[div_index]))
            elements.append(htmlform.HTMLFragment("</div>"))

        # Show the table of groups for the user to fill in
        elements.append(htmlform.HTMLFragment("<table class=\"seltable\">\n"))
        prev_table_no = None
        unselected_names = [x.get_name() for x in div_players]

        if table_size > 0:
            table_sizes = [
                table_size for i in range(0, num_slots // table_size)
            ]
        else:
            table_sizes = countdowntourney.get_5_3_table_sizes(num_slots)

        for p in set_players:
            if p and p.get_name() in unselected_names:
                unselected_names.remove(p.get_name())

        for table_size in table_sizes:
            elements.append(htmlform.HTMLFragment("<tr>\n"))
            elements.append(
                htmlform.HTMLFragment(
                    "<td>%s</td><td class=\"tablenumber\">%d</td>\n" %
                    (" &#9855;" if
                     (table_no in acc_tables) != acc_default else "",
                     table_no)))
            if game_type is not None:
                elements.append(
                    htmlform.HTMLFragment(
                        "<td class=\"fixturegametype\">%s</td>" %
                        (cgicommon.escape(game_type, True))))
            for i in range(table_size):
                p = set_players[player_index]
                td_style = ""
                value_is_valid = False
                if player_index in duplicate_slots:
                    td_style = "class=\"duplicateplayer\""
                elif player_index in empty_slots:
                    td_style = "class=\"emptyslot\""
                elif player_index in invalid_slots:
                    td_style = "class=\"invalidslot\""
                else:
                    td_style = "class=\"validslot\""
                    value_is_valid = True
                elements.append(htmlform.HTMLFragment("<td %s>" % td_style))

                # Make a drop down list with every unassigned player in it
                player_option_list = []

                if interface_type == INTERFACE_DROP_DOWN:
                    # Drop-down list needs an initial "nothing selected" option
                    player_option_list.append(
                        htmlform.HTMLFormDropDownOption("", " -- select --"))
                selected_name = ""

                if show_already_assigned_players:
                    name_list = [x.get_name() for x in div_players]
                else:
                    if p:
                        name_list = sorted(unselected_names + [p.get_name()])
                    else:
                        name_list = unselected_names

                for q in name_list:
                    if p is not None and q == p.get_name():
                        selected_name = p.get_name()
                    if interface_type == INTERFACE_DROP_DOWN:
                        player_option_list.append(
                            htmlform.HTMLFormDropDownOption(q, q))
                    else:
                        player_option_list.append(q)
                if interface_type != INTERFACE_DROP_DOWN and not selected_name:
                    selected_name = set_text[player_index]

                # Select the appropriate player
                control_name = "d%d_player%d" % (div_index, player_index)
                if interface_type == INTERFACE_DATALIST:
                    sel = htmlform.HTMLFormComboBox(
                        control_name,
                        player_option_list,
                        other_attrs={
                            "onchange": "set_unsaved_data_warning();"
                        })
                elif interface_type == INTERFACE_DROP_DOWN:
                    sel = htmlform.HTMLFormDropDownBox(
                        control_name,
                        player_option_list,
                        other_attrs={
                            "onchange": "set_unsaved_data_warning();"
                        })
                elif interface_type == INTERFACE_AUTOCOMPLETE:
                    sel = htmlform.HTMLFormTextInput(
                        "",
                        control_name,
                        selected_name,
                        other_attrs={
                            "oninput":
                            "editBoxEdit(%d, \"%s\");" %
                            (div_index, control_name),
                            "onclick":
                            "if (this.selectionStart == this.selectionEnd) { this.select(); }",
                            "id":
                            control_name,
                            "validvalue":
                            "1" if value_is_valid else "0",
                            "previousvalue":
                            selected_name,
                            "class":
                            "playerslot"
                        })
                else:
                    sel = None

                sel.set_value(selected_name)

                elements.append(sel)
                elements.append(htmlform.HTMLFragment("</td>"))
                player_index += 1
            table_no += 1
            elements.append(htmlform.HTMLFragment("</tr>\n"))

        elements.append(htmlform.HTMLFragment("</table>\n"))

        if len(acc_tables) > 0:
            # Warn the user that the table numbers displayed above might not
            # end up being the final table numbers.
            elements.append(
                htmlform.HTMLFragment(
                    "<p style=\"font-size: 10pt\">Note: You have designated accessible tables, so the table numbers above may be automatically reassigned to fulfil accessibility requirements.</p>"
                ))

        # Add the submit button
        elements.append(htmlform.HTMLFragment("<p>\n"))
        elements.append(htmlform.HTMLFormHiddenInput("submitplayers", "1"))
        elements.append(
            htmlform.HTMLFormHiddenInput("lasteditedinput",
                                         "",
                                         other_attrs={"id":
                                                      "lasteditedinput"}))
        elements.append(
            htmlform.HTMLFormSubmitButton("submit",
                                          "Submit",
                                          other_attrs={
                                              "onclick":
                                              "unset_unsaved_data_warning();",
                                              "class": "bigbutton"
                                          }))
        elements.append(htmlform.HTMLFragment("</p>\n"))

        if invalid_slots:
            elements.append(
                htmlform.HTMLFragment(
                    "<p>You have slots with unrecognised player names; these are highlighted in <span style=\"color: red; font-weight: bold;\">red</span>.</p>"
                ))
        if duplicate_slots:
            elements.append(
                htmlform.HTMLFragment(
                    "<p>You have players in multiple slots; these are highlighted in <span style=\"color: violet; font-weight: bold;\">violet</span>.</p>"
                ))

        if unselected_names:
            elements.append(
                htmlform.HTMLFragment(
                    "<p>Players still to be given a table:\n"))
            for i in range(len(unselected_names)):
                name = unselected_names[i]
                elements.append(
                    htmlform.HTMLFragment(
                        "%s%s" %
                        (cgicommon.escape(name, True),
                         "" if i == len(unselected_names) - 1 else ", ")))
            elements.append(htmlform.HTMLFragment("</p>\n"))

        elements.append(
            htmlform.HTMLFormHiddenInput("d%d_groupsize" % (div_index),
                                         str(div_table_sizes[div_index])))

        show_standings = int_or_none(
            settings.get("d%d_show_standings" % (div_index)))
        if show_standings:
            elements.append(
                htmlform.HTMLFormStandingsTable("d%d_standings" % (div_index),
                                                tourney, div_index))

    last_edited_input_name = settings.get("lasteditedinput", "")
    set_element_focus_script = """
<script>
var lastEditedElementName = %s;

var playerBoxes = document.getElementsByClassName("playerslot");
var playerBoxesBefore = [];
var playerBoxesAfter = [];
var foundElement = false;

for (var i = 0; i < playerBoxes.length; ++i) {
    if (playerBoxes[i].name == lastEditedElementName) {
        foundElement = true;
    }
    if (foundElement) {
        playerBoxesAfter.push(playerBoxes[i]);
    }
    else {
        playerBoxesBefore.push(playerBoxes[i]);
    }
}
//console.log("playerBoxesAfter " + playerBoxesAfter.length.toString() + ", playerBoxesBefore " + playerBoxesBefore.length.toString());

/* Give focus to the first text box equal to or after this one which
   does not have a valid value in it. If there are no such text boxes,
   search from the beginning of the document onwards. */
var playerBoxOrder = playerBoxesAfter.concat(playerBoxesBefore);

for (var i = 0; i < playerBoxOrder.length; ++i) {
    var box = playerBoxOrder[i];
    var validValue = box.getAttribute("validvalue");
    if (validValue == null || validValue == "0") {
        box.focus();
        box.select();
        break;
    }
}
</script>
""" % (json.dumps(last_edited_input_name))
    elements.append(htmlform.HTMLFragment(set_element_focus_script))

    form = htmlform.HTMLForm(
        "POST", "/cgi-bin/fixturegen.py?tourney=%s" %
        (urllib.parse.quote_plus(tourney.name)), elements)
    return form
Ejemplo n.º 4
0
def get_user_form(tourney, settings):
    elements = []

    num_players = settings.get("num_players")
    if num_players:
        try:
            num_players = int(num_players)
            num_players_in_tourney = len(tourney.get_active_players())
            if num_players < 2 or num_players > num_players_in_tourney:
                elements.append(
                    htmlform.HTMLFragment(
                        "<p><strong>%d is an invalid number of players: must be between 2 and %d.</strong></p>"
                        % (num_players, num_players_in_tourney)))
                num_players = None
        except ValueError:
            elements.append(
                htmlform.HTMLFragment(
                    "<p><strong>The number of players must be a number.</strong></p>"
                ))
            num_players = None

    player_selection_mode = settings.get("player_sel_mode")
    if player_selection_mode not in ("topntable", "topnrating", "random",
                                     "manual"):
        player_selection_mode = None

    if not num_players:
        # Page 1
        elements.append(
            htmlform.HTMLFormTextInput("How many players?",
                                       "num_players",
                                       "",
                                       other_attrs={"size": "4"}))
        elements.append(htmlform.HTMLFormSubmitButton("submit", "Submit"))
    elif not player_selection_mode:
        # Page 2
        elements.append(
            htmlform.HTMLFragment(
                "<p>How do you want to pick these %d players?</p>" %
                num_players))
        sel_options = []
        sel_options.append(
            htmlform.HTMLFormDropDownOption(
                "topntable", "Top %d players by table position" % num_players))
        sel_options.append(
            htmlform.HTMLFormDropDownOption(
                "topnrating", "Top %d players by rating" % num_players))
        sel_options.append(
            htmlform.HTMLFormDropDownOption(
                "random", "%d players in a random draw" % num_players))
        sel_options.append(
            htmlform.HTMLFormDropDownOption("manual", "Specify draw manually"))
        elements.append(htmlform.HTMLFragment("<p>"))
        elements.append(
            htmlform.HTMLFormDropDownBox("player_sel_mode", sel_options))
        elements.append(htmlform.HTMLFormHiddenInput("page2", "1"))
        elements.append(htmlform.HTMLFragment("</p><p>"))
        elements.append(htmlform.HTMLFormSubmitButton("submit",
                                                      "Pick Players"))
        elements.append(htmlform.HTMLFragment("</p>"))
    else:
        # Page 3

        players = tourney.get_active_players()
        standings = tourney.get_standings()

        # If we've just come from page 2, decide on initial player names in
        # "settings" for seed1 ... seedN.
        if settings.get("page2"):
            del settings["page2"]
            if player_selection_mode == "topntable":
                seed = 1
                for standing in standings[0:num_players]:
                    settings["seed%d" % seed] = standing[1]
                    seed += 1
            elif player_selection_mode == "topnrating":
                players_by_rating = sorted(players,
                                           key=lambda x: x.rating,
                                           reverse=True)
                seed = 1
                for p in players_by_rating[0:num_players]:
                    settings["seed%d" % seed] = p.name
                    seed += 1
            elif player_selection_mode == "random":
                random_player_order = players[:]
                random.shuffle(random_player_order)
                seed = 1
                for p in random_player_order[0:num_players]:
                    settings["seed%d" % seed] = p.name
                    seed += 1

        all_seeds_set = True
        found_dupes = False
        seed_players = [None for i in range(num_players)]

        elements.append(
            htmlform.HTMLFragment(
                "<p>Use the following %d players in the knockout series...</p>"
                % num_players))

        # Make N drop-down boxes, each containing the N players
        for seed_index in range(1, num_players + 1):
            keyname = "seed%d" % seed_index
            current_player_name = settings.get(keyname)

            if current_player_name:
                for p in players:
                    if current_player_name == p.get_name():
                        break
                else:
                    # Don't recognise this player
                    current_player_name = None
            else:
                current_player_name = None

            if not current_player_name:
                all_seeds_set = False

            options = []
            options.append(
                htmlform.HTMLFormDropDownOption("", "--- select player ---",
                                                current_player_name is None))
            for standing in standings:
                player = tourney.get_player_from_name(standing[1])
                player_string = "%d. %s (%d wins, %d draws, %d points)" % (
                    standing[0], player.get_name(), standing[3], standing[5],
                    standing[4])
                options.append(
                    htmlform.HTMLFormDropDownOption(
                        player.get_name(), player_string,
                        (player.get_name() == current_player_name)))
                if player.get_name() == current_player_name:
                    if player in seed_players:
                        # player is already in seed_players, so we have
                        # a duplicate
                        found_dupes = True
                        all_seeds_set = False
                    seed_players[seed_index - 1] = player

            elements.append(htmlform.HTMLFragment("#%d " % seed_index))
            elements.append(
                htmlform.HTMLFormDropDownBox("seed%d" % seed_index, options))
            elements.append(htmlform.HTMLFragment("<br />"))

        if found_dupes:
            elements.append(
                htmlform.HTMLFragment(
                    "<p><strong>Warning</strong>: one or more players appears more than once above. You need to fix this before generating fixtures.</p>"
                ))

        # Are any of the seeds involved in a tie?
        if player_selection_mode == "topntable":
            ties_mentioned = []
            for p in seed_players:
                if p:
                    player_standing = None
                    for s in standings:
                        if s[1] == p.name:
                            player_standing = s
                            break
                    for s in standings:
                        if (s[3] * 2 + s[5]
                                == player_standing[3] * 2 + player_standing[5]
                                and s[4] == player_standing[4]
                                and s[1] != player_standing[1] and
                            (s[1], player_standing[1]) not in ties_mentioned
                                and
                            (player_standing[1], s[1]) not in ties_mentioned):
                            elements.append(
                                htmlform.HTMLFragment(
                                    "<p><strong>Warning:</strong> %s and %s have the same number of wins and points and have been ordered arbitrarily.</p>"
                                    % (player_standing[1], s[1])))
                            ties_mentioned.append((s[1], player_standing[1]))

        elements.append(htmlform.HTMLFormSubmitButton("setseeds",
                                                      "Save Order"))

        if all_seeds_set:
            # All seed positions have a player in them and no player appears
            # more than once.

            # Work out fixtures and display them.
            (rounds, fixtures) = generate_knockout(tourney, seed_players)

            if settings.get("generate"):
                # The fixtures have already been okayed, so nothing more to do
                return None

            html = "<h2>Fixture list</h2>\n"
            html += "<p>The following rounds will be generated</p>\n"
            html += "<blockquote>\n"
            for r in rounds:
                html += "<li>%s</li>\n" % cgicommon.escape(r["name"])
            html += "</blockquote>\n"
            elements.append(htmlform.HTMLFragment(html))

            html = "<p>The following fixtures will be generated</p>\n"
            prev_round_no = None
            html += "<table>"
            for g in fixtures:
                if g.round_no != prev_round_no:
                    round_name = None
                    for r in rounds:
                        if r["round"] == g.round_no:
                            round_name = r["name"]
                            break
                    if not round_name:
                        round_name = "Round %d" % g.round_no
                    html += "<tr><th colspan=\"4\">%s</td></tr>\n" % round_name
                prev_round_no = g.round_no
                html += "<tr>"
                html += "<td>%d</td>" % g.seq
                html += "<td>%s</td>" % str(g.p1)
                html += "<td>v</td>"
                html += "<td>%s</td>" % str(g.p2)
                html += "</tr>"
            html += "</table>"
            elements.append(htmlform.HTMLFragment(html))

            elements.append(
                htmlform.HTMLFragment(
                    "<p>Click the button below to proceed. On the next screen you can review the fixtures and accept them.</p>"
                ))
            elements.append(
                htmlform.HTMLFormSubmitButton("generate",
                                              "Yep, looks good to me"))
        else:
            if "generate" in settings:
                del settings["generate"]

    return htmlform.HTMLForm(
        "POST", "/cgi-bin/fixturegen.py?tourney=%s" %
        urllib.parse.quote_plus(tourney.name), elements)
Ejemplo n.º 5
0
    elements.append(htmlform.HTMLFragment("<table class=\"fixdivselector\">"))
    elements.append(
        htmlform.HTMLFragment(
            "<tr><th>Division</th><th>Round number</th></tr>"))
    for div in range(num_divisions):
        elements.append(htmlform.HTMLFragment("<tr><td>"))
        elements.append(
            htmlform.HTMLFormCheckBox("_div%d" % (div),
                                      tourney.get_division_name(div), True))
        next_free_round_number = tourney.get_next_free_round_number_for_division(
            div)
        elements.append(htmlform.HTMLFragment("</td><td>"))
        elements.append(
            htmlform.HTMLFormTextInput("",
                                       "_div%dround" % (div),
                                       str(next_free_round_number),
                                       other_attrs={"class":
                                                    "fixdivroundsel"}))
        elements.append(htmlform.HTMLFragment("</td></tr>"))
    elements.append(htmlform.HTMLFragment("</table>"))
    elements.append(
        htmlform.HTMLFormSubmitButton("_divsubmit",
                                      "Next",
                                      other_attrs={"class": "bigbutton"}))
    settings_form = htmlform.HTMLForm(
        "POST", "/cgi-bin/fixturegen.py?tourney=%s&generator=%s" %
        (urllib.parse.quote_plus(tourney.get_name()),
         urllib.parse.quote_plus(generator_name)), elements)
    cgicommon.writeln(settings_form.html())

# If the user has selected which divisions they want to generate fixtures for,