模拟通过看板的工作流程
kanban-simulator的Python项目详细描述
运行看板系统模拟的助手。
目前没有gui,但在jupyter/ipython笔记本上运行良好,比如 (需要安装ipython[notebook],pandas,numpy, matplotlib和openpyxel):
import random import kanban_simulator.board as kb # For rendering HTML output in an iPython notebook: from IPython.display import display, HTML %matplotlib inline # For data analysis and plan view: import pandas as pd import numpy as np def to_plan(board, start_date, finished_day, freq='W-MON'): """Use Pandas to print a week-by-week plan-like view showing what state each card was in each week. """ grid = pd.DataFrame( index=[c.name for c in board.donelog.cards], columns=pd.date_range(start_date, freq='D', periods=finished_day) ) for card in board.donelog.cards: for col, data in card.history.items(): for day in data['dates']: grid.ix[card.name, day-1] = col.name return grid.resample(freq, label='left', axis=1).first().fillna("") # Build a backlog with some epics. # Stipulate that when the epic enters the "Build" sublane-column, it will # split into a number of stories. backlog = kb.Backlog(cards=[ kb.Epic("Epic one", splits={'Build': random.randint(5, 10)}), kb.Epic("Epic two", splits={'Build': random.randint(10, 20)}), kb.Epic("Epic three", splits={'Build': 30}), kb.Epic("Epic four", splits={'Build': 50}), kb.Epic("Epic five", splits={'Build': 50}), kb.Epic("Epic six", splits={'Build': 50}), kb.Epic("Epic seven", splits={'Build': 50}), ]) # Create a lane and clone it so that we have two lanes with the same columns # It has a lane-wide WIP limit (optional), and a series of columns # operating on epics. The "Build" column has a sub-lane (or rather, # might have one or more depending on the number of epics in this column, # subject to WIP limits), which operates on stories. The epic itself splits # into stories and becomes a backlog for these stories, as per the number of # stories above. lane_template = kb.Lane( name="<lane name>", wip_limit=3, columns=[ kb.Column( name="Discovery", touch=lambda: random.randint(5, 10), wip_limit=1, card_type=kb.Epic ), kb.QueueColumn( name="Ready for Build", wip_limit=1, card_type=kb.Epic ), kb.SublaneColumn( name="Build", lane_template=kb.Lane( name="Build", columns=[ kb.Column( name="Analysis", touch=lambda: random.randint(1, 3), wip_limit=3, card_type=kb.Story ), kb.Column( name="Development", touch=lambda: random.randint(1, 4), wip_limit=3, card_type=kb.Story ), kb.Column( name="Test", touch=lambda: random.randint(1, 2), wip_limit=3, card_type=kb.Story ), ], ), wip_limit=1, card_type=kb.Epic ), kb.Column( name="Final testing", touch=lambda: random.randint(1, 5), wip_limit=1, card_type=kb.Epic ), ] ) lanes = [ lane_template.clone(name="Team 1"), lane_template.clone(name="Team 2"), ] # Create the board board = kb.Board( name="Test simulation", lanes=lanes, backlog=backlog ) # Show the Kanban board day by day. The board is a state machine, # so when we iterate through it, the state changes. We use `clone()` to # get a new copy so we can use the same `board` later. for day, board_state in board.clone(): print "Day", day board_html = board_state.to_html() # iPython notebook specific magic to print HTML display(HTML(board_html)) # If we only want the end state, we can just do: days, board_state = board.clone().run_simulation() print "It took", days, "days" # The cards are in the `board_state.donelog.cards` list. They have # attributes like `age` (total number of days), `dates` (dates the card # was active), `touch` (number of days actually working on a card, as # opposed to waiting), and `history` (a breakdown of `age`, `dates` and # `touch`) by column name. # We can also run a Monte Carlo simulation: mc_results = board.run_monte_carlo_simulation(trials=100) # We can do some data analysis on the finish dates of each finishes = pd.Series([r[0] for r in mc_results]) print "Monte Carlo, after", len(mc_results), "loops. Quantiles:" print finishes.quantile([0.5, 0.85, 0.95]) # Histogram of finishes finishes.plot.hist() # Board at the 85th percentile, output as a grid plan day85, board85 = mc_results[int(len(mc_results) * 0.85)] plan = to_plan(board85, '2016-06-01', day85) display(HTML(plan.to_html())) # Save to Excel (requires openpyxl) plan.to_excel("simulation.xlsx", "Simulation")
更改日志
- 0.3-2016年6月3日
- 中断:如果touch或splits值是一个函数,则将使用 作为论据的卡片。
- 卡拆分现在可以是可调用的或数字(类似于 触摸)
- 新的列类型sharedwipcolumn(),它可以将多个列分组,以便 他们有一个共同的总WIP限制。
- 修正了第一个车道在制品限制可以忽略的问题 列
- 0.2-2016年5月24日
- cardhistory现在是ordereddict
- 一个backlog现在可以通过card-source拥有一个链式的“父”backlog
- 0.1-2016年5月24日
- 初始版本