├── .gitignore
├── LICENSE
├── README.md
├── RELEASE_NOTES
├── app
├── __init__.py
├── administration
│ ├── __init__.py
│ └── statemachine.py
└── common
│ ├── __init__.py
│ ├── command.py
│ ├── task.py
│ └── taskwarrior.py
├── kanban-warrior.py
└── tests.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.pyo
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | /*
2 | * -----------------------------------------------------------------------------------------------
3 | * "THE APPRECIATION LICENSE" (Revision 0xFF):
4 | * Copyright (c) 2013 M. Joosten
5 | * You can do anything with this program and its code, even use it to run a nuclear reactor (why should you)
6 | But I won't be responsible for possible damage and mishap, because i never tested it on a nuclear reactor (why should I..)
7 | If you think this program/code is absolutely great and supercalifragilisticexpialidocious (or just plain useful), just
8 | let me know by sending me a nice email or postcard from your country of origin and leave this header in the code
9 | See my blog (http://keigezellig.blog.com), for contact info
10 | * ---------------------------------------------------------------------------------------------------
11 | */
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | kanbanwarrior
2 | =============
3 |
4 | A python script supporting my kanban workflow for Task Warrior.
5 | For more info about the workflow itself see http://blog.joosten-industries.nl/2013/03/07/kanban-warrior/
6 |
7 | Installation
8 | ------------
9 | - Make sure Python 2.7.x is installed. (see http://wiki.python.org/moin/BeginnersGuide/Download)
10 | - Make sure Taskwarrior 2.1.2 or higher is installed. (see http://taskwarrior.org/projects/taskwarrior/wiki/Download)
11 | - Download the scripts by cloning this repository (if you have a git client) or click the link above to download it as an archive.
12 | - Unzip or copy the downloaded files to a directory of your choice (preferably on your path)
13 | - (optional) Set execute bit of the main kanban-warrior.py script by executing: chmod kanban-warrior.py u+x, if you don't want to type 'python' everytime when you
14 | execute a command.
15 | - Setting up TaskWarrior:
16 | task config journal.time=on
17 | task config dateformat.annotation=d-m-Y H:N
18 | task config dateformat=d-m-Y H:N
19 | task config dateformat.report=d-m-Y H:N
20 | task config dateformat.edit=d-m-Y H:N
21 | task config dateformat.info=d-m-Y H:N
22 | task config xterm.title=on
23 | Of course you can use another date format if you want by replacing the d-m-Y. See the Task Warrior website for more information about this
24 |
25 |
26 |
27 | Short usage summary
28 | -------------------
29 |
30 | Commands
31 | --------
32 | - Add task to backlog: kanban-warrior addtobacklog [projectname.storyname] [taskdescription] [priority]
33 | - Add task to In Progress: kanban-warrior addtowip [taskid]
34 | - Start task: kanban-warrior start [taskid]
35 | - Stop task: kanban-warrior stop [taskid]
36 | - Set task on hold: kanban-warrior hold [taskid] [reason]
37 | - Finish a task: kanban-warrior finish [taskid]
38 |
39 | Reports
40 | --------
41 | - List backlog: kanban-warrior list backlog [projectname]
42 | - List work in progress: kanban-warriot list wip [projectname]
43 | - List finished work: kanban-warrior list done [projectname]
44 | - List work in progress: kanban-warrior list onhold [projectname]
45 |
46 |
47 |
--------------------------------------------------------------------------------
/RELEASE_NOTES:
--------------------------------------------------------------------------------
1 | v1.0
2 | ----
3 | - It's possible to administer tasks and projects.
4 | - Assigning a priority with the addbacklog command doesn't work yet
5 | - Only works under Linux/Unix
6 | - Path to the TaskWarrior binary is hardcoded in the application to /usr/bin/
--------------------------------------------------------------------------------
/app/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keigezellig/kanbanwarrior/0bad4ba4c65783e7f1de24933c48da9533eaab9c/app/__init__.py
--------------------------------------------------------------------------------
/app/administration/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keigezellig/kanbanwarrior/0bad4ba4c65783e7f1de24933c48da9533eaab9c/app/administration/__init__.py
--------------------------------------------------------------------------------
/app/administration/statemachine.py:
--------------------------------------------------------------------------------
1 | from app.common.task import *
2 | from app.common.taskwarrior import *
3 |
4 | __version__ = '1.0'
5 |
6 | class TransitionError(Exception):
7 | """Raised when an operation attempts a state transition that's not
8 | allowed.
9 |
10 | Attributes:
11 | prev -- state at beginning of transition
12 | next -- attempted new state
13 | msg -- explanation of why the specific transition is not allowed
14 | """
15 |
16 | def __init__(self, prev, next, msg):
17 | self.prev = prev
18 | self.next = next
19 | self.msg = msg
20 |
21 | def __str__(self):
22 | return self.msg
23 |
24 | class StateMachine:
25 | pathToTaskWarrior = None
26 |
27 | def __init__(self, pathToTW):
28 | self.pathToTaskWarrior = pathToTW;
29 |
30 | def addToWip(self, task):
31 | if (task.state != States.BACKLOG) and (task.state != States.ONHOLD):
32 | raise TransitionError(task.state, States.INPROGRESS_INACTIVE, "Task must be in backlog or on hold")
33 |
34 | self.__taskwarriorAddToWip(task)
35 |
36 |
37 |
38 | def __taskwarriorAddToWip(self, task):
39 | # Command line is: task modify +inprogress -backlog|-onhold
40 | verb = "-backlog"
41 |
42 | if (task.state == States.ONHOLD):
43 | verb = "-onhold"
44 |
45 | subprocess.call([self.pathToTaskWarrior, task.taskid.__str__(), 'modify', '+inprogress', verb ])
46 |
47 |
48 | def start(self, task):
49 | if (task.state != States.BACKLOG) and (task.state != States.ONHOLD) and (task.state != States.INPROGRESS_INACTIVE):
50 | raise TransitionError(task.state, States.INPROGRESS_ACTIVE, "Task must be in backlog or on hold or inactive")
51 |
52 | if (task.state == States.BACKLOG) | (task.state == States.ONHOLD) :
53 | self.addToWip(task)
54 |
55 | self.__taskwarriorStartTask(task)
56 |
57 |
58 | def __taskwarriorStartTask(self, task):
59 | # Command line is: task start
60 | subprocess.call([self.pathToTaskWarrior, task.taskid.__str__(), 'start' ])
61 |
62 | def stop(self, task):
63 | if (task.state != States.INPROGRESS_ACTIVE):
64 | raise TransitionError(task.state, States.INPROGRESS_INACTIVE, "Task must be active")
65 |
66 | self.__taskwarriorStopTask(task)
67 |
68 |
69 | def __taskwarriorStopTask(self, task):
70 | # Command line is: task stop
71 | subprocess.call([self.pathToTaskWarrior, task.taskid.__str__(), 'stop' ])
72 |
73 | def hold(self, task, reason):
74 | if (task.state != States.INPROGRESS_ACTIVE) and (task.state != States.INPROGRESS_INACTIVE):
75 | raise TransitionError(task.state, States.ONHOLD, "Task must be in progress")
76 |
77 | if (task.state == States.INPROGRESS_ACTIVE):
78 | self.stop(task)
79 |
80 | self.__taskwarriorHoldTask(task, reason)
81 |
82 |
83 | def __taskwarriorHoldTask(self, task, reason):
84 | # Command line is: task modify -inprogress +onhold
85 | # task annotate
86 | subprocess.call([self.pathToTaskWarrior, task.taskid.__str__(), 'modify', '+onhold', '-inprogress' ])
87 | subprocess.call([self.pathToTaskWarrior, task.taskid.__str__(), 'annotate', 'PUT ON HOLD: '+reason ])
88 |
89 | def finish(self, task):
90 | if (task.state != States.INPROGRESS_ACTIVE) and (task.state != States.INPROGRESS_INACTIVE):
91 | raise TransitionError(task.state, States.INPROGRESS_INACTIVE, "Task must be in progress")
92 |
93 | self.__taskwarriorFinishTask(task)
94 |
95 |
96 | def __taskwarriorFinishTask(self, task):
97 | # Command line is: task modify -inprogress
98 | # task done
99 | subprocess.call([self.pathToTaskWarrior, task.taskid.__str__(), 'modify', '-inprogress' ])
100 | subprocess.call([self.pathToTaskWarrior, task.taskid.__str__(),'done' ])
101 |
--------------------------------------------------------------------------------
/app/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keigezellig/kanbanwarrior/0bad4ba4c65783e7f1de24933c48da9533eaab9c/app/common/__init__.py
--------------------------------------------------------------------------------
/app/common/command.py:
--------------------------------------------------------------------------------
1 | #/*
2 | # * -----------------------------------------------------------------------------------------------
3 | # * "THE APPRECIATION LICENSE" (Revision 0xFF):
4 | # * Copyright (c) 2013 M. Joosten
5 | # * You can do anything with this program and its code, even use it to run a nuclear reactor (why should you)
6 | # But I won't be responsible for possible damage and mishap, because i never tested it on a nuclear reactor (why should I..)
7 | # If you think this program/code is absolutely great and supercalifragilisticexpialidocious (or just plain useful), just
8 | # let me know by sending me a nice email or postcard from your country of origin and leave this header in the code
9 | # See my blog (http://keigezellig.blog.com), for contact info
10 | # * ---------------------------------------------------------------------------------------------------
11 | # */
12 |
13 | import argparse
14 | __version__ = '1.0'
15 |
16 | def constructArgParser():
17 | parser = argparse.ArgumentParser()
18 |
19 | subparsers = parser.add_subparsers(title='subcommands', description='Valid subcommands are:', dest="command")
20 | addtobacklog_parser = subparsers.add_parser('addtobacklog', help='Adds a new task to the backlog')
21 | addtowip_parser = subparsers.add_parser('addtowip', help='Adds an existing task from the backlog to the in progress queue')
22 | start_parser = subparsers.add_parser('start', help='Starts a task from the in progress queue')
23 | stop_parser = subparsers.add_parser('stop', help='Stops a task from the in progress queue')
24 | finish_parser = subparsers.add_parser('finish', help='Finishes a task')
25 | hold_parser = subparsers.add_parser('hold', help='Puts a task on hold')
26 |
27 | report_parser = subparsers.add_parser('list', help='Reports')
28 | reportsubparsers = report_parser.add_subparsers(title='report types', description='Valid reports are:', dest='report' )
29 | backlog_reportparser = reportsubparsers.add_parser('backlog', help='Displays the contents of the backlog')
30 | wip_reportparser = reportsubparsers.add_parser('wip', help='Displays the contents of the in progress queue')
31 | done_reportparser = reportsubparsers.add_parser('done', help='Displays finished tasks')
32 | onhold_reportparser = reportsubparsers.add_parser('onhold', help='Displays tasks on hold')
33 |
34 |
35 | addtobacklog_parser.add_argument('projectname', help='The name of project for which the task is added. May be in the form project.subproject')
36 | addtobacklog_parser.add_argument('taskname', help='Task description')
37 | addtobacklog_parser.add_argument('--priority', choices=['H', 'M', 'L', ''], default='', help='Optional task priority. H=High, M=Medium, L=Low. When not specified, no priority is assigned')
38 | addtowip_parser.add_argument('taskid', type=int, help='Id of the task to be added to the In Progress queue (can be obtained by executing ''task list'' )')
39 | start_parser.add_argument('taskid', type=int, help='Id of the task to be started (can be obtained by executing ''task list'' )')
40 | stop_parser.add_argument('taskid', type=int, help='Id of the task to be stopped (can be obtained by executing ''task list'' )')
41 | finish_parser.add_argument('taskid', type=int, help='Id of the task to be finished (can be obtained by executing ''task list'' )')
42 | hold_parser.add_argument('taskid', type=int, help='Id of the task to be put on hold (can be obtained by executing ''task list'' )')
43 | hold_parser.add_argument('reason', help='Reason of putting task on hold')
44 |
45 | backlog_reportparser.add_argument('projectname', help='The project for which the report is generated. May be in the form project.subproject')
46 | wip_reportparser.add_argument('projectname', help='The project for which the report is generated. May be in the form project.subproject')
47 | done_reportparser.add_argument('projectname', help='The project for which the report is generated. May be in the form project.subproject')
48 | onhold_reportparser.add_argument('projectname', help='The project for which the report is generated. May be in the form project.subproject')
49 |
50 | return parser
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/common/task.py:
--------------------------------------------------------------------------------
1 | #/*
2 | # * -----------------------------------------------------------------------------------------------
3 | # * "THE APPRECIATION LICENSE" (Revision 0xFF):
4 | # * Copyright (c) 2013 M. Joosten
5 | # * You can do anything with this program and its code, even use it to run a nuclear reactor (why should you)
6 | # But I won't be responsible for possible damage and mishap, because i never tested it on a nuclear reactor (why should I..)
7 | # If you think this program/code is absolutely great and supercalifragilisticexpialidocious (or just plain useful), just
8 | # let me know by sending me a nice email or postcard from your country of origin and leave this header in the code
9 | # See my blog (http://keigezellig.blog.com), for contact info
10 | # * ---------------------------------------------------------------------------------------------------
11 | # */
12 | import json
13 | import subprocess
14 |
15 | __version__ = '1.0'
16 |
17 | class States:
18 | NONE = 0
19 | BACKLOG = 1
20 | INPROGRESS_INACTIVE = 2
21 | INPROGRESS_ACTIVE = 3
22 | DONE = 4
23 | ONHOLD = 5
24 |
25 | class Task:
26 | taskid = 0
27 | state = States.NONE
28 |
29 | def __init__(self, taskid, state):
30 | self.taskid = taskid
31 | self.state = state
32 |
33 | class Tasklist:
34 | __list = []
35 |
36 | def __init__(self, pathToTW):
37 | self.pathToTaskWarrior = pathToTW;
38 | if (self.pathToTaskWarrior == None):
39 | raise IOException("Cannot find Task Warrior program. Please install it")
40 | self.__list = self.__getTaskList()
41 |
42 |
43 | def getTaskById(self, id):
44 | taskfilter = filter(lambda x: x.taskid == id, self.__list)
45 | if taskfilter == []:
46 | raise LookupError('Unknown task specified.')
47 | else:
48 | return taskfilter[0]
49 |
50 | def __mapToTask(self, taskitem):
51 | if ('tags' in taskitem) and (len(taskitem['tags']) == 1) and ('backlog' in taskitem['tags']):
52 | return Task(taskitem['id'], States.BACKLOG)
53 |
54 | if ('tags' in taskitem) and (len(taskitem['tags']) == 1) and ('inprogress' in taskitem['tags']):
55 | if ('start' in taskitem):
56 | return Task(taskitem['id'], States.INPROGRESS_ACTIVE)
57 | else:
58 | return Task(taskitem['id'], States.INPROGRESS_INACTIVE)
59 |
60 | if ('tags' in taskitem) and (len(taskitem['tags']) == 1) and ('onhold' in taskitem['tags']):
61 | return Task(taskitem['id'], States.ONHOLD)
62 |
63 |
64 | def __filterIllegalTasks(self, listitem):
65 | return listitem != None and listitem.taskid != 0
66 |
67 |
68 | def __getTaskList(self):
69 | # excecute and get output from task export command
70 | exportOutput = subprocess.check_output([self.pathToTaskWarrior,'export'])
71 |
72 | # strip funky header (goes until first \n)
73 | jsonstring = exportOutput[exportOutput.index('\n')+1:]
74 | # add array brackets for json deserialization
75 | jsonstring = '[' + jsonstring
76 | jsonstring = jsonstring + ']'
77 |
78 | tasklist = json.loads(jsonstring)
79 |
80 | # map it to a list of task objects
81 | tasklist = map(self.__mapToTask, tasklist)
82 | # filter out the illegal ones
83 | tasklist = filter(self.__filterIllegalTasks, tasklist)
84 |
85 | return tasklist
86 |
--------------------------------------------------------------------------------
/app/common/taskwarrior.py:
--------------------------------------------------------------------------------
1 | #/*
2 | # * -----------------------------------------------------------------------------------------------
3 | # * "THE APPRECIATION LICENSE" (Revision 0xFF):
4 | # * Copyright (c) 2013 M. Joosten
5 | # * You can do anything with this program and its code, even use it to run a nuclear reactor (why should you)
6 | # But I won't be responsible for possible damage and mishap, because i never tested it on a nuclear reactor (why should I..)
7 | # If you think this program/code is absolutely great and supercalifragilisticexpialidocious (or just plain useful), just
8 | # let me know by sending me a nice email or postcard from your country of origin and leave this header in the code
9 | # See my blog (http://keigezellig.blog.com), for contact info
10 | # * ---------------------------------------------------------------------------------------------------
11 | # */
12 |
13 | import os
14 |
15 | __version__ = '1.0'
16 |
17 | def findTaskWarrior():
18 | #Default path for now, maybe in the future configurable
19 | filename='task'
20 | path='/usr/bin'
21 | for root, dirs, names in os.walk(path):
22 | if filename in names:
23 | return os.path.join(root, filename)
24 | else:
25 | raise IOError('Cannot find TaskWarrior binary. It should be located in the /usr/bin folder')
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/kanban-warrior.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | #/*
4 | # * -----------------------------------------------------------------------------------------------
5 | # * "THE APPRECIATION LICENSE" (Revision 0xFF):
6 | # * Copyright (c) 2013 M. Joosten
7 | # * You can do anything with this program and its code, even use it to run a nuclear reactor (why should you)
8 | # But I won't be responsible for possible damage and mishap, because i never tested it on a nuclear reactor (why should I..)
9 | # If you think this program/code is absolutely great and supercalifragilisticexpialidocious (or just plain useful), just
10 | # let me know by sending me a nice email or postcard from your country of origin and leave this header in the code
11 | # See my blog (http://keigezellig.blog.com), for contact info
12 | # * ---------------------------------------------------------------------------------------------------
13 | # */
14 |
15 | from app.administration.statemachine import *
16 | from app.common.task import *
17 | from app.common.taskwarrior import *
18 | from app.common.command import *
19 |
20 | import subprocess
21 |
22 | import sys
23 |
24 | __version__ = '1.0'
25 |
26 | if __name__=="__main__":
27 |
28 | try:
29 | argParser = constructArgParser()
30 | args = argParser.parse_args()
31 |
32 | pathToTaskWarrior = findTaskWarrior()
33 |
34 |
35 | if args.command=='addtobacklog':
36 | subprocess.check_call([pathToTaskWarrior, 'add', args.taskname, 'project:'+args.projectname, 'priority:'+args.priority, '+backlog' ])
37 | sys.exit(0)
38 |
39 |
40 | if args.command != 'list':
41 | print "Loading task list.."
42 | tasklist = Tasklist(pathToTaskWarrior)
43 | sm = StateMachine(pathToTaskWarrior)
44 | task = tasklist.getTaskById(args.taskid)
45 | if task == None:
46 | sys.exit('Unknown task specified. Aborting program')
47 |
48 | if args.command == 'addtowip':
49 | sm.addToWip(task)
50 | elif args.command == 'start':
51 | sm.start(task)
52 | elif args.command == 'stop':
53 | sm.stop(task)
54 | elif args.command == 'finish':
55 | sm.finish(task)
56 | elif args.command == 'hold':
57 | sm.hold(task, args.reason)
58 |
59 | else:
60 | if args.report == 'backlog':
61 | subprocess.call([pathToTaskWarrior, 'long' , 'project:'+args.projectname, '+backlog' ])
62 | elif args.report == 'wip':
63 | subprocess.call([pathToTaskWarrior, 'long' , 'project:'+args.projectname, '+inprogress' ])
64 | elif args.report == 'done':
65 | subprocess.call([pathToTaskWarrior, 'completed' ])
66 | elif args.report == 'onhold':
67 | subprocess.call([pathToTaskWarrior, 'long' , 'project:'+args.projectname, '+onhold' ])
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | except (IOError, LookupError, TransitionError) as e:
76 | sys.exit('Error: '+e.__str__())
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/tests.py:
--------------------------------------------------------------------------------
1 |
2 | from app.administration.statemachine import *
3 |
4 |
5 | def AddCorrectTaskToWipTest():
6 | print "***** AddCorrectTaskToWipTest ********"
7 | task1 = Task(1,States.BACKLOG)
8 | task2 = Task(1,States.ONHOLD)
9 | sm = StateMachine()
10 | sm.AddToWip(task1)
11 | sm.AddToWip(task2)
12 | print task1.state
13 | print task2.state
14 |
15 |
16 | def AddWrongTaskToWipTest():
17 | print "***** AddWrongTaskToWipTest ********"
18 | try:
19 | task = Task(1,States.INPROGRESS_INACTIVE)
20 | sm = StateMachine()
21 | sm.AddToWip(task)
22 | print task.state
23 | except TransitionError as e:
24 | print e.msg, e.prev, e.next
25 |
26 | try:
27 | task = Task(1,States.INPROGRESS_ACTIVE)
28 | sm = StateMachine()
29 | sm.AddToWip(task)
30 | print task.state
31 | except TransitionError as e:
32 | print e.msg, e.prev, e.next
33 |
34 | try:
35 | task = Task(1,States.DONE)
36 | sm = StateMachine()
37 | sm.AddToWip(task)
38 | print task.state
39 | except TransitionError as e:
40 | print e.msg, e.prev, e.next
41 |
42 | def StartCorrectTaskTest():
43 | print "***** StartCorrectTaskTest ********"
44 | task1 = Task(1,States.BACKLOG)
45 | task2 = Task(1,States.ONHOLD)
46 | task3 = Task(1,States.INPROGRESS_INACTIVE)
47 | sm = StateMachine()
48 | sm.Start(task1)
49 | sm.Start(task2)
50 | sm.Start(task3)
51 | print task1.state
52 | print task2.state
53 | print task3.state
54 |
55 |
56 | def StartWrongTaskTest():
57 | print "***** StartWrongTaskTest ********"
58 | try:
59 | task = Task(1,States.INPROGRESS_ACTIVE)
60 | sm = StateMachine()
61 | sm.Start(task)
62 | print task.state
63 | except TransitionError as e:
64 | print e.msg, e.prev, e.next
65 |
66 | try:
67 | task = Task(1,States.DONE)
68 | sm = StateMachine()
69 | sm.Start(task)
70 | print task.state
71 | except TransitionError as e:
72 | print e.msg, e.prev, e.next
73 |
74 | def StopCorrectTaskTest():
75 | print "***** StopCorrectTaskTest ********"
76 | task1 = Task(1,States.INPROGRESS_ACTIVE)
77 | sm = StateMachine()
78 | sm.Stop(task1)
79 | print task1.state
80 |
81 |
82 | def StopWrongTaskTest():
83 | print "***** StopWrongTaskTest ********"
84 | try:
85 | task = Task(1,States.INPROGRESS_INACTIVE)
86 | sm = StateMachine()
87 | sm.Stop(task)
88 | print task.state
89 | except TransitionError as e:
90 | print e.msg, e.prev, e.next
91 |
92 | try:
93 | task = Task(1,States.DONE)
94 | sm = StateMachine()
95 | sm.Stop(task)
96 | print task.state
97 | except TransitionError as e:
98 | print e.msg, e.prev, e.next
99 |
100 | try:
101 | task = Task(1,States.BACKLOG)
102 | sm = StateMachine()
103 | sm.Stop(task)
104 | print task.state
105 | except TransitionError as e:
106 | print e.msg, e.prev, e.next
107 |
108 | try:
109 | task = Task(1,States.ONHOLD)
110 | sm = StateMachine()
111 | sm.Stop(task)
112 | print task.state
113 | except TransitionError as e:
114 | print e.msg, e.prev, e.next
115 |
116 |
117 | def HoldCorrectTaskTest():
118 | print "***** HoldCorrectTaskTest ********"
119 | task1 = Task(1,States.INPROGRESS_ACTIVE)
120 | task2 = Task(2,States.INPROGRESS_INACTIVE)
121 | sm = StateMachine()
122 | sm.Hold(task1, "Oops")
123 | sm.Hold(task2, "Oops")
124 | print task1.state, task2.state
125 |
126 |
127 | def HoldWrongTaskTest():
128 | print "***** HoldWrongTaskTest ********"
129 | try:
130 | task = Task(1,States.DONE)
131 | sm = StateMachine()
132 | sm.Hold(task, "Oops")
133 | print task.state
134 | except TransitionError as e:
135 | print e.msg, e.prev, e.next
136 |
137 | try:
138 | task = Task(1,States.BACKLOG)
139 | sm = StateMachine()
140 | sm.Hold(task, "Oops")
141 | print task.state
142 | except TransitionError as e:
143 | print e.msg, e.prev, e.next
144 |
145 | try:
146 | task = Task(1,States.ONHOLD)
147 | sm = StateMachine()
148 | sm.Hold(task, "Oops")
149 | print task.state
150 | except TransitionError as e:
151 | print e.msg, e.prev, e.next
152 |
153 | def DoneCorrectTaskTest():
154 | print "***** DoneCorrectTaskTest ********"
155 | task1 = Task(1,States.INPROGRESS_ACTIVE)
156 | task2 = Task(2,States.INPROGRESS_INACTIVE)
157 | sm = StateMachine()
158 | sm.Finish(task1)
159 | sm.Finish(task2)
160 | print task1.state, task2.state
161 |
162 |
163 | def DoneWrongTaskTest():
164 | print "***** DoneWrongTaskTest ********"
165 | try:
166 | task = Task(1,States.DONE)
167 | sm = StateMachine()
168 | sm.Finish(task)
169 | print task.state
170 | except TransitionError as e:
171 | print e.msg, e.prev, e.next
172 |
173 | try:
174 | task = Task(1,States.BACKLOG)
175 | sm = StateMachine()
176 | sm.Finish(task)
177 | print task.state
178 | except TransitionError as e:
179 | print e.msg, e.prev, e.next
180 |
181 | try:
182 | task = Task(1,States.ONHOLD)
183 | sm = StateMachine()
184 | sm.Finish(task)
185 | print task.state
186 | except TransitionError as e:
187 | print e.msg, e.prev, e.next
188 |
189 | if __name__=="__main__":
190 | AddCorrectTaskToWipTest()
191 | AddWrongTaskToWipTest()
192 | StartCorrectTaskTest()
193 | StartWrongTaskTest()
194 | StopCorrectTaskTest()
195 | StopWrongTaskTest()
196 | HoldCorrectTaskTest()
197 | HoldWrongTaskTest()
198 | DoneCorrectTaskTest()
199 | DoneWrongTaskTest()
200 |
--------------------------------------------------------------------------------