├── .cit ├── README.md └── cit /.cit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yassinemaaroufi/cit/HEAD/.cit -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cit 2 | === 3 | 4 | Command line Issue Tracker 5 | 6 | 7 | Author: Yassine Maaroufi - 8 | 9 | Distributed under GPLv3 - http://www.gnu.org/copyleft/gpl.html 10 | 11 | ### Description: 12 | Cit is a small issue tracker that can be included in your project's base directory. It can thus be versioned with the rest of your code using tools like git or bzr. Cit is written in python and resides in a sqlite database. 13 | 14 | ### Getting started: 15 | 16 | cit init # To initialize the issue tracker 17 | 18 | cit add "First issue" # To add an issue 19 | 20 | cit + 0 # To upvote issue no. 0 -------------------------------------------------------------------------------- /cit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ''' 4 | cit - Console Issue Tracker 5 | Copyright (C) 2011 Yassine Maaroufi - 6 | Distributed under GPLv3 - http://www.gnu.org/copyleft/gpl.html 7 | ''' 8 | 9 | import os, os.path, sqlite3, time, tempfile 10 | from sys import argv 11 | 12 | APPNAME = 'cit' 13 | DBNAME = '.' + APPNAME 14 | 15 | # Position in DB 16 | IDPOSITION = 0 17 | PTSPOSITION = 5 18 | DESCPOSITION = 6 19 | 20 | # Text editor 21 | EDITOR = 'vim' 22 | 23 | def connectSqlite(): 24 | connection = sqlite3.connect(DBNAME) 25 | cursor = connection.cursor() 26 | return [connection, cursor] 27 | 28 | def initTracker(): 29 | [connection, cursor] = connectSqlite() 30 | try: 31 | cursor.execute('create table issues (id, date_create, date_state, \ 32 | last_mod, state, pts, title, details)') 33 | print('Tracker up') 34 | connection.commit() 35 | except: print('Tracker already exists') 36 | 37 | # (project name, description, tag) 38 | def newIssue(description): 39 | [connection, cursor] = connectSqlite() 40 | cursor.execute('select max(id) from issues') 41 | i = cursor.fetchone()[0] 42 | if i == None: i = 0 43 | else: i += 1 44 | t = int(time.time()) 45 | cursor.execute('insert into issues values (?,?,?,?,?,?,?,?)', \ 46 | (i,t,t,t,1,0,description,'')) 47 | print('Created Issue No. ' + str(i)) 48 | connection.commit() 49 | 50 | def deleteIssue(x): 51 | [connection, cursor] = connectSqlite() 52 | cursor.execute('delete from issues where id='+x) 53 | print('Issue Deleted') 54 | connection.commit() 55 | 56 | def listIssues(): 57 | cursor = connectSqlite()[1] 58 | cursor.execute('select * from issues where state=1 order by pts, id') 59 | k = 0 60 | for i in cursor: 61 | se = '' if k % 2 == 0 else "\033[90m" 62 | ss = '' if k % 2 == 0 else "\033[0m" 63 | print(se + ' '.join([str(i[IDPOSITION]).rjust(4), \ 64 | str(i[PTSPOSITION]).rjust(3), i[DESCPOSITION]]) + ss) 65 | k += 1 66 | print(' '.join(['-'*4, '-'*3, '-'*17])) 67 | print(' '.join(['id'.center(4), 'pts', 'description'])) 68 | 69 | def voteIssue(direction, issue): 70 | [connection, cursor] = connectSqlite() 71 | cursor.execute('update issues set last_mod=? where id=' + issue, \ 72 | (int(time.time()),)) 73 | cursor.execute('select pts from issues where id = ' + issue) 74 | r = [int(i[IDPOSITION]) for i in cursor][0] 75 | r += 1 if direction == '+' else -1 76 | cursor.execute('update issues set pts=? where id=' + issue, (r,)) 77 | print('Issue Voted ' + direction + '1') 78 | connection.commit() 79 | 80 | def upVoteIssue(issue): 81 | voteIssue('+', issue) 82 | 83 | def downVoteIssue(issue): 84 | voteIssue('-', issue) 85 | 86 | def editIssue(issue): 87 | [connection, cursor] = connectSqlite() 88 | cursor.execute('select * from issues where id = ' + issue) 89 | i = cursor.fetchone() 90 | f = tempfile.NamedTemporaryFile() 91 | open(f.name, 'w').write(i[DESCPOSITION]) 92 | os.system(EDITOR + ' ' + f.name) 93 | ff = open(f.name) 94 | g = ff.read().rstrip('\n') 95 | cursor.execute('update issues set last_mod=? where id=' + issue, \ 96 | (int(time.time()),)) 97 | cursor.execute('update issues set title=? where id=' + issue, (g,)) 98 | connection.commit() 99 | 100 | def closeIssue(issue): 101 | [connection, cursor] = connectSqlite() 102 | cursor.execute('update issues set state=?, date_state=?, last_mod=? where \ 103 | id=' + issue, (0, int(time.time()), int(time.time()))) 104 | connection.commit() 105 | print('Issue No. ' + issue + ' Closed') 106 | 107 | # Command line arguments 108 | ARGS1 = { 109 | 'ls': [listIssues, 'List all open issues'], 110 | 'init': [initTracker, 'Initialize the issue tracker'] 111 | } 112 | ARGS2 = { 113 | 'add': [newIssue, 'Add new issues', 'issue-text'], 114 | 'rm': [deleteIssue, 'Delete issue', 'issue-id'], 115 | 'edit': [editIssue, 'Edit issue', 'issue-id'], 116 | 'close': [closeIssue, 'Close issue', 'issue-id'], 117 | '+': [upVoteIssue, 'Up vote issue', 'issue-id'], 118 | '-': [downVoteIssue, 'Down vote issue', 'issue-id'] 119 | } 120 | 121 | def printHelp(): 122 | print('\nUsage: ' + APPNAME + ' option argument') 123 | print('Options list:') 124 | for i in ARGS1: print(' '*4 + APPNAME + ' ' + i.ljust(20) + ARGS1[i][1]) 125 | print('') 126 | for i in ARGS2: print(' '*4 + APPNAME + ' ' + (i + ' ' + ARGS2[i][2]).ljust(20) \ 127 | + ARGS2[i][1]) 128 | exit() 129 | 130 | # Command line arguments handling 131 | if len(argv) == 2 and argv[1] in ARGS1: ARGS1[argv[1]][0]() 132 | elif len(argv) == 3 and argv[1] in ARGS2: ARGS2[argv[1]][0](argv[2]) 133 | else: printHelp() 134 | 135 | --------------------------------------------------------------------------------