Guided Tour of the Code

Typically, this is where the API documentation would go, but this isn’t a library. However, this is a nice overview for someone wanting to get into the code.

Basic Structure

The project is primarily structured as a python package. The following items in the root of the project are common to many python package distributions (For more info, check out this:

docs/:
Documentation for the project
tests/:
Unit tests for the project
thekraf/:
The main python package of the project that is installed by setuptools
MANIFEST.in:
setup.py is configured to search for python code to include when installing the package into site-packages. To include non-python files, one option is to reference those files with rules defined in this file.
README.rst:
Introductory information about the project
requirements.txt:
Python package dependencies for the project. Although dependencies can be defined in setup.py, this file is more capable, especially when not all requirements are hosted on PyPI (such as with this project).
setup.cfg:
Site-specific setup config (More info here). In this case, it is being used to configure bumpversion.
setup.py:
Primary configuration file for package distribution.

Core Logic

thekraf.diceutils

Utilities for use with dice

class thekraf.diceutils.DiceUtils[source]

Bases: object

Utilities for use with/on dice

VALS = (1, 2, 3, 4, 5, 6)
static as_string(dice)[source]

Convert dice to string representation

Dice are sorted before conversion so this also the key for the caches

Parameters:dice (tuple[int]) – Dice to convert
Returns:Corresponding string
Return type:str
static concat(dice, other)[source]

Concatenate dice

Parameters:
  • dice (tuple[int]) – Starting dice
  • other (tuple[int]) – Dice to add on
Returns:

Concatenated dice

Return type:

tuple[int]

classmethod create_dice(dice_repr)[source]

Create dice values from various representations

Representation can be:

  • string

    • ‘123456’
    • ‘1 2 3 4 5 6’
    • ‘1,2,3,4,5,6’
  • sequence of strings

    • [‘1’, ‘1’, ‘2’, ‘2’]
  • sequence of int (basically passthrough)

    • (3, 4, 5)
Parameters:

dice_repr (str) – String to parse

Returns:

Corresponding values

Return type:

tuple[int]

Raises:
  • TypeError – If dice_repr can’t be converted to dice
  • ValueError – If dice values are not in VALS
static diff(minuend, subtrahend)[source]

Return dice in minuend less the dice in the subtrahend

Parameters:
  • minuend (tuple[int]) – Starting dice
  • subtrahend (tuple[int]) – The dice to remove from minuend
Returns:

The difference

Return type:

tuple[int]

static indices_of(*args, **kwargs)[source]
static is_dice_inst(obj)[source]

True if obj is an instance of dice

Parameters:obj (object) – Object to test
Returns:
Return type:bool
static is_dice_repr(obj)[source]

True if obj appears to be a valid dice representation

Parameters:obj (object) – Object to test
Returns:
Return type:bool
static roll(n)[source]
thekraf.diceutils.norm_dice(arg_index=0)[source]

Normalize any dice representations as true dice

Parameters:arg_index (int) – Index of the target arg in the parameter list. Usually this would be the first arg, but it would be the second if used on a class or instance method.
Returns:Configured decorator

thekraf.game

The game controller logic

class thekraf.game.Game(**kwargs)[source]

Bases: thekraf.game.GameTurn, thekraf.db.models.BaseModel

A game of thekraf

A game consists of a series of Cycles or rounds in which each Player takes a Turn.

created
cycles
do_action(action, params)[source]

Perform a game action

Parameters:
  • action (str) – Name of the action to perform
  • params (dict) – Keyword args to pass on the the action method
game_info()[source]

Format data about the game so it can easily be displayed

This is mostly for debugging purposes

Returns:Formatted data
Return type:tuple[tuple[str]]
goal
id
is_anonymous
min_bank
min_first_bank
mode
modified
new_cycle()[source]

Start a new cycle of the game

new_turn()[source]

Start a new turn of the game

opts
query = <sqlalchemy.orm.query.Query object>
score_table_info()[source]

Format data about the game so it is easily displayable in a table

Returns:Formatted data
Return type:tuple[tuple[str]]
class thekraf.game.GameTurn(**kwargs)[source]

Bases: thekraf.gamemodels.Game

Game logic specific to Turns

bank(dice=())[source]

Bank scored dice, thereby completing a turn

Parameters:dice (tuple(int) – Additional dice to include before banking
bankable(*args, **kwargs)
dice_to_roll_num()[source]

Calculate how many dice should be rolled

Returns:The number to roll
Return type:int
roll_dice()[source]

Roll any unscored dice or all of them if all have been scored

Don’t check for busted immediately after roll. That way, the user can be informed of the bust. Rolling at that point will act as an acknowledgement by the user so the turn can be marked complete.

select_and_score(*args, **kwargs)
select_and_score_min()[source]

Select the best scoring min number of scorable dice from rolled

Frequently, a player may choose to re-roll scoring dice for the chance at a higher score. However, at least one set of scoring dice must be set aside before rolling what it remaining. This method determines the smallest subcombo of the rolled dice that is scorable. If there are multiple combos of the same size, the one with the highest score is returned.

turn_info()[source]

Format data about the turn so it can easily be displayed

This is mostly for debugging purposes

Returns:Formatted data
Return type:tuple[tuple[str]]
turn_table_info()[source]

Format data about the turn so it is easily displayable in a table

Returns:Formatted data
Return type:tuple[tuple[str]]
unscore(*args, **kwargs)
thekraf.game.displayable_info(objs, spec)[source]

Semi-automatic formatting of attrs for display

Spec format:

Iterable of triplets (name, title, value) where:

name
Name of attr on one of the objs
title
Display name for the attr. If None, it will become title-case of name.
value
Attr value. If None, objs will be searched for attr. If attr is not found, it will remain None.
Parameters:
  • objs (collections.Iterable[object]) – Objects to search for attrs
  • spec (collections.Iterable[collections.Iterable]) – Dpecification as explained above
Returns:

Pairs of title str and value

Return type:

collections.Generator[tuple[str, object]]

thekraf.gamemodels

Models for use by the game controller

class thekraf.gamemodels.Cycle(**kwargs)[source]

Bases: object

A cycle or round of the game

Each player gets one turn per cycle

pids

tuple[int] – IDs of players

turns

list[Turn] – Turns in the cycle

complete

True if all players have completed their turn for this round

turn

The current Turn

class thekraf.gamemodels.Game(**kwargs)[source]

Bases: object

Model for a game

mode

str – Game mode. Possible modes are:

  • ‘rounds’ – The most points after a certain number of rounds
  • ‘points’ – First player to reach a certain number of points
goal

int – Value associated withe the game mode to determine the end of the game

min_first_bank

int – The minimum running total of a turn required to bank the first time

min_bank

int – The minimum running total of a turn required to bank after already being on the board

pids

tuple[int] – IDs of players in the game

cycles

list[Cycle] – Rounds of the game

MODES = {'rounds': {'goal': 10, 'min_first_bank': 300, 'min_bank': 50}, 'points': {'goal': 10000, 'min_first_bank': 500, 'min_bank': 50}}
complete

bool – True if the goal of the game has been reached

cycle

Cycle – The current cycle

description
pids
player
player_names
players
score_per_turns

dict[int, int] – Player ID -> total for each turn

totals

dict[int, int] – Player ID -> running total for game

turn

Turn – The current turn

Returns:
Return type:thekraf.gamemodels.Turn
turns

collections.Generator[Turn] – All turns of all rounds

class thekraf.gamemodels.Player(**kwargs)[source]

Bases: object

A player of a game

id

int – Unique player ID

name

str – Name of player

class thekraf.gamemodels.Turn(**kwargs)[source]

Bases: object

Series of rolls and scores until bank or bust

Each player will have one Turn per Round. A turn consists of a series of rolls and scores. After scoring, the player may choose to continue rolling the remaining dice (or all dice if all dice have been scored), or bank the running total for the turn.

After a roll, the player must score with at least one die. If the player is unable to score with any of the dice, the turn is busted. A busted turn results in the turned being scored as 0.

Rolls and scores will usually be balanced; The dice are rolled, and then a portion of the rolled dice a scored. The exception is a busted turn. Since there is no way to score, the final roll, the number of rolls will be one more than the number of scores.

complete

bool – True of the turn is finished (either banked or busted)

pid

int – Indicates the player playing the turn

dice_score_pairs

list[tuple[tuple[int], int]] – Items in this list are tuples of the dice that were scored after each roll and the corresponding score

roll_hist

list[tuple[int]] – History of rolls. Scored dice must be a subset of rolled dice.

min_bank

int – Minimum running total for a turn required to bank

FIRST_DICE_COUNT = 6

int – Number of dice to roll at the beginning of a turn

busted

bool – True if there is no way to score with the last roll

min_scorable

tuple[tuple[int], int] – Best score with the least number of dice

rolled

tuple[int] – The last roll of the dice

score

int – The score for the last scored dice

scored_dice

int – The score for the last scored dice

scores

int – The score for the last scored dice

subscores

dict[str, int] – Scores for sub-combos of rolled

total

int – Total of the scored dice, or 0 if busted

thekraf.score

Logic related to scoring dice and caching scores

class thekraf.score.BaseScore[source]

Bases: object

classmethod calc_of_a_kind(opts, n, val)[source]

Calculate score for n of a kind where n >= 3

Parameters:
  • opts (thekraf.config.ScoreOptions) – Scoring options
  • n (int) – Number of dice with val
  • val (int) – Value on the dice
Returns:

The score for n dice of value val

Return type:

int

classmethod gen_all_dice_strs()[source]

Generate all possible dice combos as strings

Yields:str – Dice represented by a string
classmethod score_all(*args, **kwargs)
class thekraf.score.Score[source]

Bases: thekraf.score.ScoreCache, thekraf.score.BaseScore

Logic related to scoring dice

classmethod calc_score(*args, **kwargs)
classmethod subscores(*args, **kwargs)
class thekraf.score.ScoreCache[source]

Bases: thekraf.score.BaseScore

Logic for creating and saving the score caches

classmethod create_score_any_by_num_cache(score_any_cache)[source]

Organize the score any cache by number of dice

Examples

>>> d = cfg.load_config_item(                'score_options', select=lambda x: x['name'] == 'default')[0]
>>> opts = mdls.ScoreOptions(**d)
>>> cls = ScoreCache
>>> cls.precache_scores(opts)
>>> cache = opts.score_any_by_num_cache
>>> sum(map(len, cache.values())) == len(opts.score_any_cache)
True
Parameters:score_any_cache (dict[str, dict[str, int]]) – The score any cache
Returns:Organized cache
Return type:dict[int, dict[str, dict[str, int]]]
classmethod create_score_any_cache(score_cache)[source]

Create cache of possible ways to score with sub-combos of dice

Sub-combo also includes all of the dice. If none of the sub-combos are in _score_cache, it will not be included (see examples).

Returns:
Dict from string rep of dice to sub-dict
of _score_cache
Return type:dict[str, dict[str, int]
Raises:AssertionError – If _score_cache hasn’t been initialized

Examples

>>> d = [a for a in cfg.load_config_item('score_options')                      if a['name'] == 'default'][0]
>>> opts = mdls.ScoreOptions(**d)
>>> score_cache = ScoreCache.create_score_cache(opts)
>>> cls = ScoreCache
>>> cache = cls.create_score_any_cache(score_cache)
>>> '1' in cache['12']
True
>>> '2' in cache['12']
False
>>> '12' in cache['12']
False
>>> '11' in cache['11']
True
>>> '1' in cache['1']
True
classmethod create_score_cache(opts)[source]

Create cache of possible scores in which all dice score

If any of the dice are non-scoring, the score would be zero, so it isn’t in the cache (see examples).

Parameters:opts (thekraf.config.ScoreOptions) – Scoring options
Returns:Dict from string rep of dice to score
Return type:dict[str, int]

Examples

>>> d = [a for a in cfg.load_config_item('score_options')                      if a['name'] == 'default'][0]
>>> opts = mdls.ScoreOptions(**d)
>>> cache = ScoreCache.create_score_cache(opts)
>>> cache['1']
100
>>> '2' in cache
False

Since all dice have to be scoring...

>>> '12' in cache
False
classmethod precache_scores(opts)[source]

Pre-calculate all possible scores

This speeds up subsequent scoring and simplifies some logic

Parameters:opts (thekraf.config.ScoreOptions) – Scoring options
classmethod save_score_caches(savepath)[source]

Save the score caches to disk as json

Parameters:savepath (str) – The save path (should have an extension of ‘json’

thekraf.utils

Miscellaneous utilities

class thekraf.utils.MixedHelpFormatter(prog, indent_increment=2, max_help_position=24, width=None)[source]

Bases: argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter

Combo of arg default and raw description help formatters

class thekraf.utils.MutableList[source]

Bases: sqlalchemy.ext.mutable.Mutable, list

List type for SQLAlchemy with changed state for the session

classmethod coerce(key, value)[source]
thekraf.utils.get_subclasses(cls)[source]

Get all subclasses of cls, recursively

If A is a subclass of cls and B is a subclass of A, both A and B will be include

Note

Does not include cls itself

Parameters:cls (type) –
Returns:
Return type:tuple[type]
thekraf.utils.recreate_field(unbound_field)[source]

Make a copy of an unbound field that bumps the creation counter

WTForms determines the order of the fields of form with a creation counter that is increment during the unbound field init. The issue is that the fields of subclasses of a form are created after their super classes so the subclass fields are always appended. That is, unless you override a field, but that can be a lot of work.

If a subclass overrides a parent field by sending it through this function, a copy of the unbound field will be created it an updated creation count so the subclass can dictate the order of the fields.

Parameters:unbound_field (wtforms.UnboundField) – The field to copy
Returns:Copy of the field
Return type:wtforms.UnboundField

Database

thekraf.db

Database models, ORM, and other configuration

Overview

thekraf.db.model defines models that are mostly pure Python. Then, thekraf.db.mapper configures those models to be accessible via the sqlalchemy ORM. Separating these roles, allows for the possibility of implementation of additional ORMs in the future.

thekraf.db.listeners

A collection of SQLAlchemy event listeners to be applied to mapped models

Note

SQLAlchemy will ignore fields that don’t exist on the target

thekraf.db.listeners.created_modified(mapper, connection, target)[source]

Sets the created/modified fields

thekraf.db.listeners.modified(mapper, connection, target)[source]

Sets the modified field

thekraf.db.mapper

Object-relational mapping of models for SQLAlchemy

thekraf.db.mapper.config_metadata()[source]

Configure the metadata for the ORM

This metadata may then be used to configure the database manager

Returns:The configured metadata
Return type:MetaData
thekraf.db.mapper.config_sqla_models(models, dbm)[source]

Configure basic models for SQLAlchemy

Note

This can only be called after the metadata and the database manager have been configured.

Parameters:
  • models (tuple[BaseModel]) – Models to config
  • dbm (DatabaseManager) – The database manager providing the session factory

thekraf.db.models

Models for the database

class thekraf.db.models.BaseModel[source]

Bases: object

query = None
class thekraf.db.models.Role(**kwargs)[source]

Bases: thekraf.db.models.BaseModel, flask_security.core.RoleMixin

created
description
id
modified
name
query = <sqlalchemy.orm.query.Query object>
users
class thekraf.db.models.ScoreOptions(**kwargs)[source]

Bases: thekraf.db.models.BaseModel

Configurable scoring options

name

str – Name of these options

description

str – Description of these options

single1

int – A single die of value 1

single5

int – A single die of value 5

triple1

int – A triplet of dice of value 1 (usually 300 or 1000)

fourplusscheme

str – Scoring scheme for 4-6 of a kind

kind4

int – Score for 4 of a kind when using the ‘set’ scheme

kind5

int – Score for 5 of a kind when using the ‘set’ scheme

kind6

int – Score for 6 of a kind when using the ‘set’ scheme

straight

int – Score for a straight (123456)

threepair

int – Score for three pairs (e.g. 112233)

twotriplets

int – Score of two triplets (e.g. 111222) (instead of scoring each triplet on its own)

fullhousebonus

int – Score a triplet and pair together as the triplet score plus this bonus (instead of scoring the pair on its own)

kind4bonus

int – Score a 4 of a kind and a pairs together as the 4 of a kind plus this bonus (instead of scoring the pair on its own)

points

int – Goal in points-based mode

score_cache

dict[str, int] – dice_str -> score, if all dice are scoring

score_any_cache

dict[str, dict[str, int]] – dice_str -> sub-dict of score_cache. The sub-keys will be sub-combos of dice_str in score_cache (meaning the sub-combo comprises all scoring dice.

score_any_by_num_cache

dict[int, dict[str, dict[str, int]]] – The score_any_cache organized by the number of dice.

ev_cache

dict[tuple[int, int], Decimal] – (pts, n) -> related ev

ADD_SCHEME = 'add'

str – Add triplet value for each die more than 3

Example

3 of a kind -> 1000, 4 -> 2000, 5 -> 3000, 6 -> 4000

DEFAULTS = {'name': '(unnamed)', 'straight': 2500, 'triple1': 1000, 'kind4': 2000, 'single1': 100, 'kind6': 6000, 'points': 10000, 'kind5': 4000, 'threepair': 1500, 'description': '', 'single5': 50, 'fourplusscheme': 'add'}
DOUBLE_SCHEME = 'double'

str – Double triplet value for each die more than 3

Example

3 of a kind -> 1000, 4 -> 2000, 5 -> 4000, 6 -> 8000

SET_SCHEME = 'set'

str – Use set values (kind4, kind5, kind6) regardless of die value

created
description
ev_cache
fourplusscheme
fullhousebonus
id
kind4
kind4bonus
kind5
kind6
modified
name
points
query = <sqlalchemy.orm.query.Query object>
score_any_by_num_cache
score_any_cache
score_cache
single1
single5
straight
threepair
triple1
twotriplets
class thekraf.db.models.User(**kwargs)[source]

Bases: thekraf.db.models.BaseModel, flask_security.core.UserMixin

active
confirmed_at
created
email
first
games
id
last
modified
name
nickname
password
query = <sqlalchemy.orm.query.Query object>
roles
username

Web

Init

thekraf.flaskapp

Flask web backend app

class thekraf.flaskapp.FlaskWithHamlish(import_name, static_path=None, static_url_path=None, static_folder='static', template_folder='templates', instance_path=None, instance_relative_config=False)[source]

Bases: flask.app.Flask

Adds support for Hamlish-Jinja to Flask

jinja_options = ImmutableDict({'extensions': ['jinja2.ext.autoescape', 'jinja2.ext.with_', 'hamlish_jinja.HamlishExtension']})
class thekraf.flaskapp.JSONEncoderWrapper[source]

Bases: object

Add support for more types to the json encoder

if default and for_json are used, then if an object has a for_json attribute, it will be used. Otherwise, it will fallback on default

flask uses a TaggedJSONSerializer on sessions which will be try
converting various types before giving default a try.
static default_override(o)[source]
thekraf.flaskapp.create_app(config_obj=<class 'thekraf.config.FlaskAppConfig'>)[source]

Flask app factory

Returns:An app configured for various extensions
Return type:flask.app

Views

thekraf.flaskapp.baseview

The base for all flaskapp views

class thekraf.flaskapp.baseview.ViewBase[source]

Bases: flask.views.MethodView

MethodView with contexts for backend template and frontend

  • Subclasses should subclass TemplateContext to add additional items to the context.
  • Update the frontend dict with ctx.update_frontend(d). This method is equivalent to dict.update().
  • ctx.frontend will become the json string, and this is done automatically by the base view. There is no reason to access this attr from a subclass.

The frontend data is collected in a python dict. One of the final stages of rendering is to dump this dict as a string of json and save it to ctx.frontend. Then, the base template creates a javascript script in which ctx is set to the json data. Then, the frontend can access this data via the ctx namespace.

ctx

ViewBase.TemplateContext – Context for project-specific data to be available from the backend template

class TemplateContext[source]

Bases: object

prep_frontend()[source]

Prepare frontend data by converting dict to json

update_frontend(other=None, **kwargs)[source]

Update the frontend dict

ViewBase.before_render()[source]

This method runs before any other logic in the render method

classmethod ViewBase.ctx_processor()[source]

Context processor for use with Flask-Security

When customizing the Flask-Security views, this context processor must be added so the template context includes data available to all other views.

Returns:Dict of keyword args included with flask.render
Return type:dict
ViewBase.get()[source]

Override with specific code for GET action

Override should end by returning self.render()

ViewBase.methods = ['GET', 'POST']
ViewBase.post()[source]

Override with specific code for POST action

Override should end by returning self.render() or self.redirect_to_get()

classmethod ViewBase.redirect_to_get(endpoint=None)[source]

Redirect a POST action to the equivalent GET

After a POST action, reloading the page results in the “Are you sure you want to send a form again?” dialog. One way to avoid this often annoying occurrence is to redirect to the equivalent GET.

Notes

The POST data is lost upon redirect, so this only works if the POST data has been processed before the redirect.

ViewBase.render()[source]

Format data for the backend template and render the template

View Helpers

thekraf.flaskapp.fields

Fields used by thekraf.flaskapp.forms

class thekraf.flaskapp.fields.BooleanFieldList(unbound_field, label=None, validators=None, min_entries=0, max_entries=None, default=(), **kwargs)[source]

Bases: wtforms.fields.core.FieldList

widget = <thekraf.flaskapp.widgets.BootstrapButtonGroupWidget object>
class thekraf.flaskapp.fields.CheckboxField(label=None, validators=None, **kwargs)[source]

Bases: wtforms.fields.core.Field

Checkbox input with checked state separate from value

Represents an <input type="checkbox">. If the default value is a tuple, the first item will become value, and the second item is a boolean indicating the initial checked state. When form data is parsed, only checked fields will be included (that is how HTML is designed).

process_data(value)[source]
process_formdata(valuelist)[source]
widget = <thekraf.flaskapp.widgets.CheckboxValueInput object>

thekraf.flaskapp.formatters

Functions to format model data for the backend templates

thekraf.flaskapp.formatters.score_opts_def_pairs(opts)[source]

Attr name / value pairs of specific opts

Parameters:opts (ScoreOptions) – Scoring options
Returns:Attr name / value pairs
Return type:tuple[tuple[str, str]]

thekraf.flaskapp.forms

Flask-WTF forms used by the flask app

class thekraf.flaskapp.forms.Form(formdata=<class 'flask_wtf.form._Auto'>, obj=None, prefix='', csrf_context=None, secret_key=None, csrf_enabled=None, *args, **kwargs)[source]

Bases: flask_wtf.form.Form

ACTION_SUFFIX = '_button'
action_buttons
class thekraf.flaskapp.forms.GameForm(formdata=<class 'flask_wtf.form._Auto'>, obj=None, prefix='', csrf_context=None, secret_key=None, csrf_enabled=None, *args, **kwargs)[source]

Bases: thekraf.flaskapp.forms.Form

mode_select = <UnboundField(SelectField, ('Mode',), {'choices': [('rounds', 'Rounds'), ('points', 'Points')]})>
opts_select = <UnboundField(SelectField, ('Scoring Config',), {'choices': []})>
player_name_input = <UnboundField(StringField, ('Player Names',), {'render_kw': {'placeholder': 'Player 1, Player 2, ...'}})>
reset_button = <UnboundField(SubmitField, ('Create',), {'render_kw': {'class_': 'btn btn-default'}})>
user_select = <UnboundField(BooleanFieldList, (<UnboundField(CheckboxField, (), {'render_kw': {'autocomplete': 'off'}})>,), {'label': 'Players', 'render_kw': {'_btn_styles': ('default',), '_btn_group_styles': ('vertical',)}})>
class thekraf.flaskapp.forms.LoginUserForm(*args, **kwargs)[source]

Bases: flask_security.forms.LoginForm

email = <UnboundField(StringField, ('E-mail/Username',), {'validators': (<wtforms.validators.DataRequired object at 0x7f6fb55bbb00>,)})>
password = <UnboundField(PasswordField, ('Password',), {'validators': (<wtforms.validators.DataRequired object at 0x7f6fb55bbc18>,)})>
remember = <UnboundField(BooleanField, ('Remember Me',), {})>
submit = <UnboundField(SubmitField, ('Login',), {})>
class thekraf.flaskapp.forms.OptsForm(formdata=<class 'flask_wtf.form._Auto'>, obj=None, prefix='', csrf_context=None, secret_key=None, csrf_enabled=None, *args, **kwargs)[source]

Bases: thekraf.flaskapp.forms.Form

opts_select = <UnboundField(SelectField, ('Scoring Config',), {'choices': []})>
submit = <UnboundField(SubmitField, ('Load',), {})>
class thekraf.flaskapp.forms.RegisterUserForm(*args, **kwargs)[source]

Bases: flask_security.forms.RegisterForm

email = <UnboundField(StringField, ('Email Address',), {'validators': [<flask_security.forms.Required object at 0x7f6fb5e870f0>, <flask_security.forms.Email object at 0x7f6fb5e87160>, <function unique_user_email at 0x7f6fb5ddbae8>]})>
first = <UnboundField(StringField, ('First Name *',), {'validators': (<wtforms.validators.DataRequired object at 0x7f6fb55c1080>,)})>
last = <UnboundField(StringField, ('Last Name *',), {'validators': (<wtforms.validators.DataRequired object at 0x7f6fb55c1160>,)})>
nickname = <UnboundField(StringField, ('Nickname',), {'validators': (<wtforms.validators.Optional object at 0x7f6fb55c1240>,)})>
password = <UnboundField(PasswordField, ('Password',), {'validators': [<flask_security.forms.Required object at 0x7f6fb5e87438>, <flask_security.forms.Length object at 0x7f6fb5e87470>]})>
password_confirm = <UnboundField(PasswordField, ('Retype Password',), {'validators': [<flask_security.forms.EqualTo object at 0x7f6fb5e51e80>]})>
submit = <UnboundField(SubmitField, ('Register',), {})>
to_dict()[source]
username = <UnboundField(StringField, ('Username',), {'validators': (<wtforms.validators.Optional object at 0x7f6fb55bbe80>,)})>
class thekraf.flaskapp.forms.ScoreForm(formdata=<class 'flask_wtf.form._Auto'>, obj=None, prefix='', csrf_context=None, secret_key=None, csrf_enabled=None, *args, **kwargs)[source]

Bases: thekraf.flaskapp.forms.Form

dice_input = <UnboundField(StringField, ('Dice Repr',), {})>
submit = <UnboundField(SubmitField, ('Score',), {})>
validate_dice_input(field)[source]
class thekraf.flaskapp.forms.TurnForm(formdata=<class 'flask_wtf.form._Auto'>, obj=None, prefix='', csrf_context=None, secret_key=None, csrf_enabled=None, *args, **kwargs)[source]

Bases: thekraf.flaskapp.forms.Form

bank_button = <UnboundField(SubmitField, ('Bank',), {'render_kw': {'disabled': True}})>
dice_select = <UnboundField(BooleanFieldList, (<UnboundField(CheckboxField, (), {'render_kw': {'autocomplete': 'off'}})>,), {'label': 'Select Dice', 'render_kw': {'_btn_styles': ('primary btn-dice',), '_btn_group_styles': ('lg',)}})>
roll_button = <UnboundField(SubmitField, ('Roll',), {'render_kw': {'disabled': True}})>
score_button = <UnboundField(SubmitField, ('Score',), {})>
score_min_button = <UnboundField(SubmitField, ('Score Min',), {})>
scored_select = <UnboundField(BooleanFieldList, (<UnboundField(CheckboxField, (), {'render_kw': {'autocomplete': 'off'}})>,), {'label': 'Scored Dice', 'render_kw': {'_btn_styles': ('success',), '_btn_group_styles': ('lg',)}})>
unscore_button = <UnboundField(SubmitField, ('Unscore',), {'render_kw': {'disabled': True}})>

thekraf.flaskapp.nav

Configuration of the navigation bar elements

thekraf.flaskapp.nav.top_nav_elems()[source]

Configure the top navbar elements for the template

Returns:Info for each element
Return type:tuple[dict[str, str]]

thekraf.flaskapp.widgets

Widgets used by thekraf.flaskapp.fields

class thekraf.flaskapp.widgets.BootstrapButtonGroupWidget[source]

Bases: object

Renders a Bootstrap 3 checkbox/radio button group

class thekraf.flaskapp.widgets.CheckboxValueInput(input_type=None)[source]

Bases: wtforms.widgets.core.Input

Render a checkbox with checked state separate from value

The checked HTML attribute is set if field.checked is a non-false value. The value of the input doesn’t change. This allows using the value in labeling the field.

Note

HTML will only return checkbox inputs that are checked upon submit.

input_type = 'checkbox'

App

thekraf.flaskapp.app

Create and configure the global app instance

Production servers usually need to access the global app instance.

thekraf.flaskapp.app.app = <FlaskWithHamlish 'thekraf.flaskapp'>

flask.Flask – Global flask app instance

thekraf.flaskapp.app.create_and_config_app()[source]

Create and configure the main app

Returns:Main app instance
Return type:flask.Flask
thekraf.flaskapp.app.create_and_config_hello_app()[source]

Create and configure a simple app, helpful for some unit testing

Returns:Simple hello app
Return type:flask.Flask

Paths

static/:
Static resources available to the client
static/frontend/:
Frontend code
templates/:
Backend templates
package.json:
This defines the nodejs dependencies to install when node install is run
package.yaml:
YAML version of package.json. Advantageous since it allows comments. However, it must be converted to package.json before it can be used.
webpack.config.js:
Configuration file that is interrogated when webpack is run. The result is static/bundle.js.