{ "info": { "author": "Martin Aspeli", "author_email": "optilude@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "Kanban Simulator\n================\n\nHelpers for running simulations of Kanban systems.\n\nCurrently no GUI, but works well in a Jupyter/iPython Notebook, like\n(requires installation of `ipython[notebook]`, `pandas`, `numpy`,\n`matplotlib` and `openpyxel`)::\n\n import random\n import kanban_simulator.board as kb\n\n # For rendering HTML output in an iPython notebook:\n from IPython.display import display, HTML\n %matplotlib inline\n\n # For data analysis and plan view:\n import pandas as pd\n import numpy as np\n\n def to_plan(board, start_date, finished_day, freq='W-MON'):\n \"\"\"Use Pandas to print a week-by-week plan-like view showing\n what state each card was in each week.\n \"\"\"\n\n grid = pd.DataFrame(\n index=[c.name for c in board.donelog.cards],\n columns=pd.date_range(start_date, freq='D', periods=finished_day)\n )\n\n for card in board.donelog.cards:\n for col, data in card.history.items():\n for day in data['dates']:\n grid.ix[card.name, day-1] = col.name\n\n return grid.resample(freq, label='left', axis=1).first().fillna(\"\")\n\n # Build a backlog with some epics.\n # Stipulate that when the epic enters the \"Build\" sublane-column, it will\n # split into a number of stories.\n\n backlog = kb.Backlog(cards=[\n kb.Epic(\"Epic one\", splits={'Build': random.randint(5, 10)}),\n kb.Epic(\"Epic two\", splits={'Build': random.randint(10, 20)}),\n kb.Epic(\"Epic three\", splits={'Build': 30}),\n kb.Epic(\"Epic four\", splits={'Build': 50}),\n kb.Epic(\"Epic five\", splits={'Build': 50}),\n kb.Epic(\"Epic six\", splits={'Build': 50}),\n kb.Epic(\"Epic seven\", splits={'Build': 50}),\n ])\n\n # Create a lane and clone it so that we have two lanes with the same columns\n # It has a lane-wide WIP limit (optional), and a series of columns\n # operating on epics. The \"Build\" column has a sub-lane (or rather,\n # might have one or more depending on the number of epics in this column,\n # subject to WIP limits), which operates on stories. The epic itself splits\n # into stories and becomes a backlog for these stories, as per the number of\n # stories above.\n\n lane_template = kb.Lane(\n name=\"\",\n wip_limit=3,\n columns=[\n kb.Column(\n name=\"Discovery\",\n touch=lambda: random.randint(5, 10),\n wip_limit=1,\n card_type=kb.Epic\n ),\n kb.QueueColumn(\n name=\"Ready for Build\",\n wip_limit=1,\n card_type=kb.Epic\n ),\n kb.SublaneColumn(\n name=\"Build\",\n lane_template=kb.Lane(\n name=\"Build\",\n columns=[\n kb.Column(\n name=\"Analysis\",\n touch=lambda: random.randint(1, 3),\n wip_limit=3,\n card_type=kb.Story\n ),\n kb.Column(\n name=\"Development\",\n touch=lambda: random.randint(1, 4),\n wip_limit=3,\n card_type=kb.Story\n ),\n kb.Column(\n name=\"Test\",\n touch=lambda: random.randint(1, 2),\n wip_limit=3,\n card_type=kb.Story\n ),\n ],\n ),\n wip_limit=1,\n card_type=kb.Epic\n ),\n kb.Column(\n name=\"Final testing\",\n touch=lambda: random.randint(1, 5),\n wip_limit=1,\n card_type=kb.Epic\n ),\n ]\n )\n\n lanes = [\n lane_template.clone(name=\"Team 1\"),\n lane_template.clone(name=\"Team 2\"),\n ]\n\n # Create the board\n board = kb.Board(\n name=\"Test simulation\",\n lanes=lanes,\n backlog=backlog\n )\n\n # Show the Kanban board day by day. The board is a state machine,\n # so when we iterate through it, the state changes. We use `clone()` to\n # get a new copy so we can use the same `board` later.\n\n for day, board_state in board.clone():\n print \"Day\", day\n board_html = board_state.to_html()\n\n # iPython notebook specific magic to print HTML\n display(HTML(board_html))\n\n # If we only want the end state, we can just do:\n days, board_state = board.clone().run_simulation()\n print \"It took\", days, \"days\"\n\n # The cards are in the `board_state.donelog.cards` list. They have\n # attributes like `age` (total number of days), `dates` (dates the card\n # was active), `touch` (number of days actually working on a card, as\n # opposed to waiting), and `history` (a breakdown of `age`, `dates` and\n # `touch`) by column name.\n\n # We can also run a Monte Carlo simulation:\n mc_results = board.run_monte_carlo_simulation(trials=100)\n\n # We can do some data analysis on the finish dates of each\n finishes = pd.Series([r[0] for r in mc_results])\n\n print \"Monte Carlo, after\", len(mc_results), \"loops. Quantiles:\"\n print finishes.quantile([0.5, 0.85, 0.95])\n\n # Histogram of finishes\n finishes.plot.hist()\n\n # Board at the 85th percentile, output as a grid plan\n day85, board85 = mc_results[int(len(mc_results) * 0.85)]\n\n plan = to_plan(board85, '2016-06-01', day85)\n display(HTML(plan.to_html()))\n\n # Save to Excel (requires openpyxl)\n plan.to_excel(\"simulation.xlsx\", \"Simulation\")\n\n\nChangelog\n---------\n\n0.3 - 03 June 2016\n * BREAKING: If `touch` or a `splits` value is a function, it will be called with the\n card as an argument.\n * Card splits can now be either a callable or a number (analogous to\n `touch`)\n * New column type, SharedWIPColumn(), which can group multiple columns so\n that they have a shared overall WIP limit.\n * Fixed problem whereby lane WIP limits could be ignored in the first\n column\n\n0.2 - 24 May 2016\n * Card `history` is now an OrderedDict\n * A backlog can now have a chained \"parent\" backlog via `card_source`\n\n0.1 - 24 May 2016\n * Initial release", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/optilude/kanban-simulator", "keywords": "agile kanban analytics", "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "kanban-simulator", "package_url": "https://pypi.org/project/kanban-simulator/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/kanban-simulator/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/optilude/kanban-simulator" }, "release_url": "https://pypi.org/project/kanban-simulator/0.3/", "requires_dist": null, "requires_python": null, "summary": "Simulate work flowing through a Kanban board", "version": "0.3" }, "last_serial": 2148014, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "7ca74adf2661fdf7b06d435e744827d2", "sha256": "248cf991e9ef10a176ec085dbadac91447f0fcf8ba33a5ca81958969fcaf3ae7" }, "downloads": -1, "filename": "kanban-simulator-0.1.tar.gz", "has_sig": false, "md5_digest": "7ca74adf2661fdf7b06d435e744827d2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7920, "upload_time": "2016-05-24T19:12:47", "url": "https://files.pythonhosted.org/packages/c0/c9/2029ca64aec2e0fe06fdd901f20baa0cf8dca0684d09aeb562b5f90eb23d/kanban-simulator-0.1.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "8d0a4a407cb4825829a1340e1b736999", "sha256": "ff9991e7916a0fb6270fce96cd7a7a694f38e43fea67a8beb40ee95f0537eaa9" }, "downloads": -1, "filename": "kanban-simulator-0.2.tar.gz", "has_sig": false, "md5_digest": "8d0a4a407cb4825829a1340e1b736999", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8014, "upload_time": "2016-05-24T22:08:41", "url": "https://files.pythonhosted.org/packages/23/7d/d381e45ecefa159e1cddccaf5916633f5dab57fe2cfac9b270e8e200cb79/kanban-simulator-0.2.tar.gz" } ], "0.3": [ { "comment_text": "", "digests": { "md5": "6fcff51179b28465f4b7abc78c353478", "sha256": "3d15694364c29e561c860df1f253ee0156e94b31cdf042a95f5385ea738a9508" }, "downloads": -1, "filename": "kanban-simulator-0.3.tar.gz", "has_sig": false, "md5_digest": "6fcff51179b28465f4b7abc78c353478", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8731, "upload_time": "2016-06-02T23:22:03", "url": "https://files.pythonhosted.org/packages/d6/e0/704fbc90b76bae284ce1239baad1a0a0e6bbc1da8d0b9a2f7090357d12cd/kanban-simulator-0.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "6fcff51179b28465f4b7abc78c353478", "sha256": "3d15694364c29e561c860df1f253ee0156e94b31cdf042a95f5385ea738a9508" }, "downloads": -1, "filename": "kanban-simulator-0.3.tar.gz", "has_sig": false, "md5_digest": "6fcff51179b28465f4b7abc78c353478", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8731, "upload_time": "2016-06-02T23:22:03", "url": "https://files.pythonhosted.org/packages/d6/e0/704fbc90b76bae284ce1239baad1a0a0e6bbc1da8d0b9a2f7090357d12cd/kanban-simulator-0.3.tar.gz" } ] }