edi_core.py (85124B)
1 #! /usr/bin/env python 2 # -*- coding: latin-1 -*- 3 ## @package edi_core 4 # Core module 5 # 6 # 7 # Copyright (C) 2014 Spartatek AB 8 # 9 # contact@spartatek.se 10 # http://spartatek.se 11 # 12 # EASYDONEIT CLI is free software: you can redistribute it and/or modify 13 # it under the terms of the GNU Genereric Public License as published by 14 # the Free Software Foundation, either version 3 of the License, or 15 # (at your option) any later version. 16 # 17 # EASYDONIT CLI is distributed in the hope that it will be usesul, 18 # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 # GNU Genereral Public License for more details. 21 # 22 # You should have received a copy of the GNU General Public License 23 # along with this program. If not, see 24 # GNU licences http://www.gnu.org/licenses 25 26 import sys 27 import re 28 import time 29 import shutil 30 import os 31 import ConfigParser 32 from string import ascii_lowercase 33 from string import ascii_uppercase 34 from string import digits 35 import random 36 import stat 37 import getpass 38 39 from edi_common import * 40 41 ## user_interface is initialized in start() and changes the behavior of some functions to allow a functional user interface design. The default is not used, start() is always called before using edi_core. 42 # Values: 'cli', 'web' 43 user_interface = 'cli' 44 45 data_location = '' 46 data_location_tasks = '' 47 data_location_groups = '' 48 data_location_tree = '' 49 50 ## Available databases 51 databases = [] 52 53 ## list selected databases 54 selected = [] 55 selected_path = [] 56 ## default database where tasks are created 57 default_add_in = '' 58 59 ## add new tasks in 'bottom' of group or 'top' of group 60 add_top_or_bottom = 'bottom' 61 62 ## Autlink groups (array of tids) 63 autolink = '' 64 65 ## list of groups for edi ls -L, -La and -Lx (array of tids) 66 list_of_groups = '' 67 68 ## characters for task ids 69 ID_BASE = sorted(list(digits)+list(ascii_lowercase)+list(ascii_uppercase)+['_',',']) 70 ID_BASE_STRING = ''.join(ID_BASE) 71 ## Length of task id 72 ID_LENGTH = 16 73 ## Length of order id in groups 74 ORDER_ID_LENGTH = 8 75 BASE = len(ID_BASE) 76 77 TASK_STATUS = [' Active', 78 ' Done', 79 ' Ongoing', 80 ' Pending', 81 'Inactive', 82 ' Void'] 83 TASK_STATUS_ACTIVE = 0 84 TASK_STATUS_DONE = 1 85 TASK_STATUS_ONGOING = 2 86 TASK_STATUS_PENDING = 3 87 TASK_STATUS_INACTIVE = 4 88 TASK_STATUS_VOID = 5 89 90 ## Sort order for sort_task_attributes function, ongoing, active, pending, done, inactive, void 91 SORT_TASK_ORDER = [2,0,3,1,4,5] 92 93 LIST_OPTIONS = ['tids', 94 'positions', 95 'html'] 96 list_option = 'tids' 97 98 STATUS_FILTER_STATES = ['enable','disable'] 99 100 ## enables all status filters 101 status_filters = [STATUS_FILTER_STATES[0] for i in range(len(TASK_STATUS))] 102 ## status filter dictionary, initialized after loading ini file 103 status_filters_d = {} 104 105 ## colors 106 no_color = (-1,-1,-1,255) 107 #status_fgColors = [(0,0,0,255) for i in range(len(TASK_STATUS))] 108 status_fgColors = [(0,0,0,255),(0,255,0,255),(255,128,0,255),(160,32,240,255),(192,192,192,255),(0,0,0,255)] 109 status_fgColors_d = {} 110 ## no background color by default 111 status_bgColors = [no_color for i in range(len(TASK_STATUS))] 112 status_fgColors_d = {} 113 114 ## text editor in terminal - easydoneit.ini [settings] EDITOR 115 editor = 'vi' 116 117 ## Agenda generated by edi.in_cli 118 agenda = [] 119 120 ## user name 121 user = '' 122 123 ## user email 124 email = '' 125 126 ## stats for statistics function 127 stats = {} 128 129 ## total number of tasks for statistics function 130 stats_total = 0 131 132 ## creation dates of tasks in statistics, used to find out oldest task 133 stats_creation_dates = [] 134 135 ## timely state changes and creation dates 136 # Type: dict of dict of integers 137 # keys are dates 138 # each dates is a dictionary with STATS_OVERTIME_KEYS keys 139 # each key is the amount for states and creation 140 stats_overtime = {} 141 142 ## timely state changes and creation dates 143 STATS_OVERTIME_KEYS = [TASK_STATUS[i] for i in range(len(TASK_STATUS))] + ['Creation'] 144 145 ## Group directory, cache result to speed up color functions 146 group_directory_file_list = [] 147 148 ## initializes path to data. 149 # creates directories<br> 150 # creates root group and task<br> 151 # @ingroup EDI_CORE 152 def init(): 153 # Set global variables 154 global data_location_tasks 155 global data_location_groups 156 global data_location_tree 157 global status_filters 158 global status_filters_d 159 global status_fgColors 160 global status_fgColors_d 161 global status_bgColors 162 global status_bgColors_d 163 164 status_filters_d = dict(zip(TASK_STATUS,status_filters)) 165 status_fgColors_d = dict(zip(TASK_STATUS,status_fgColors)) 166 status_bgColors_d = dict(zip(TASK_STATUS,status_bgColors)) 167 168 data_location_tasks = '%s/tasks'%data_location 169 data_location_groups = '%s/groups'%data_location 170 data_location_tree = '%s/tree'%data_location 171 172 if not os.path.exists(data_location_tasks): 173 if not os.path.exists(data_location): 174 os.mkdir(data_location) 175 os.mkdir(data_location_tasks) 176 os.mkdir(data_location_groups) 177 os.mkdir(data_location_tree) 178 os.mkdir('%s/root'%data_location_groups) 179 180 # Create root description in tasks 181 task_path = '%s/root'%data_location_tasks 182 os.mkdir(task_path) 183 # copy root description from script directory 184 shutil.copy('%s/root.txt'%os.path.abspath(os.path.dirname(sys.argv[0])),'%s/description.txt'%task_path) 185 186 # Create status 187 f = open('%s/status'%task_path,'w') 188 f.write(TASK_STATUS[TASK_STATUS_VOID]) 189 f.close() 190 191 ## log actions in database - data_location/log.txt 192 # @param[in] s string without user name and timestamp 193 # @ingroup EDI_CORE 194 def edi_log(s): 195 global user 196 global email 197 f = open('%s/log.txt'%data_location,'a') 198 f.write('%s - %s - %s\n'%(time.strftime("%Y-%m-%d %H:%M"), '%s <%s>'%(user,email),s.strip())) 199 f.close() 200 201 202 ## select location database 203 # @param[in] location database name 204 # @ingroup EDI_CORE 205 def select_database(location): 206 global data_location 207 global data_location_tasks 208 global data_location_groups 209 global data_location_tree 210 211 z = dict(zip(selected, selected_path)) 212 data_location = os.path.expanduser(z[location]) 213 data_location_tasks = '%s/tasks'%data_location 214 data_location_groups = '%s/groups'%data_location 215 data_location_tree = '%s/tree'%data_location 216 if not os.path.exists(data_location): 217 return '%s is unreachable.'%data_location 218 else: 219 return '' 220 221 ## Mix foreground colors from task, groups and default colors. 222 # @return color array 223 # @param[in] tid task id 224 # @ingroup EDI_CORE 225 # Collect all defined colors in link groups. 226 # For each group, the group gets the first color defined in the tree<br> 227 # compute average color 228 def mix_fgcolors(tid): 229 # mix colors 230 # collect all defined colors in groups to root 231 colors = [] 232 # root has no parent group, dont search parent group color 233 if tid != 'root': 234 # find all groups - link and parent group 235 if is_linked(tid): 236 groups = os.listdir('%s/groups/' % generate_task_path(tid)) 237 else: 238 groups = [find_group_containing_task(tid)] 239 240 # walk parent groups until a color or root is found 241 while groups: 242 status_color = 'search' 243 if os.path.isfile('%s/fgColor'%generate_task_path(groups[0])): 244 f = open('%s/fgColor'%generate_task_path(groups[0])) 245 c = tuple([int(i) for i in f.readline().split(',')]) 246 f.close() 247 # -1 means no_color, mix colors 248 if c[0] != -1: 249 colors.append(c) 250 status_color = 'found color' 251 if status_color == 'search': 252 parent_group = find_group_containing_task(groups[0]) 253 if parent_group != 'root': 254 groups.append(parent_group) 255 del groups[0] 256 257 # compute average color 258 if colors: 259 color = [0,0,0,0] 260 for c in colors: 261 for i in range(len(c)): 262 color[i] += c[i] 263 colorCount = len(colors) 264 color = tuple([i/colorCount for i in color]) 265 if not colors: 266 # no defined colors, use default color for status 267 task_status = get_status(tid) 268 color = status_fgColors_d[task_status] 269 return color 270 271 ## get color for task tid 272 # @return color array 273 # @param[in] tid task id 274 # @ingroup EDI_CORE 275 # when no color is set, mix colors from groups or use defaults 276 def get_forground_color(tid): 277 color = no_color 278 279 if os.path.isfile('%s/fgColor'%generate_task_path(tid)): 280 f = open('%s/fgColor'%generate_task_path(tid)) 281 color = tuple([int(i) for i in f.readline().split(',')]) 282 f.close() 283 # -1 means no_color, mix colors 284 if color[0] == -1: 285 color = mix_fgcolors(tid) 286 else: 287 color = mix_fgcolors(tid) 288 return color 289 290 ## set color for task tid 291 # @return color array 292 # @param[in] tid task id 293 # @param[in] color_s color array 294 # @ingroup EDI_CORE 295 def set_forground_color(tid,color_s): 296 f = open('%s/fgColor'%generate_task_path(tid),'w') 297 f.write(color_s) 298 f.close() 299 edi_log('set foreground color in %s to %s'%(tid,color_s)) 300 301 ## Mix background colors from task, groups and default colors. 302 # @return color array 303 # @param[in] tid task id 304 # @ingroup EDI_CORE 305 # Collect all defined colors in link groups. 306 # For each group, the group gets the first color defined in the tree<br> 307 # compute average color 308 def mix_bgcolors(tid): 309 # mix colors 310 # collect all defined colors in groups to root 311 colors = [] 312 # root has no parent group, dont search parent group color 313 if tid != 'root': 314 # find all groups - link and parent group 315 if is_linked(tid): 316 groups = os.listdir('%s/groups/' % generate_task_path(tid)) 317 else: 318 groups = [find_group_containing_task(tid)] 319 320 # walk parent groups until a color or root is found 321 while groups: 322 status_color = 'search' 323 if os.path.isfile('%s/bgColor'%generate_task_path(groups[0])): 324 f = open('%s/bgColor'%generate_task_path(groups[0])) 325 c = tuple([int(i) for i in f.readline().split(',')]) 326 f.close() 327 # -1 means no_color, mix colors 328 if c[0] != -1: 329 colors.append(c) 330 status_color = 'found color' 331 if status_color == 'search': 332 parent_group = find_group_containing_task(groups[0]) 333 if parent_group != 'root': 334 groups.append(parent_group) 335 del groups[0] 336 337 # compute average color 338 if colors: 339 color = [0,0,0,0] 340 for c in colors: 341 for i in range(len(c)): 342 color[i] += c[i] 343 colorCount = len(colors) 344 color = tuple([i/colorCount for i in color]) 345 if not colors: 346 # no defined colors, use default color for status 347 task_status = get_status(tid) 348 color = status_bgColors_d[task_status] 349 return color 350 351 ## get color for task tid 352 # @return color array 353 # @param[in] tid task id 354 # @ingroup EDI_CORE 355 # when no color is set, mix colors from groups or use defaults 356 def get_background_color(tid): 357 color = no_color 358 359 if os.path.isfile('%s/bgColor'%generate_task_path(tid)): 360 f = open('%s/bgColor'%generate_task_path(tid)) 361 color = tuple([int(i) for i in f.readline().split(',')]) 362 f.close() 363 # -1 means no_color, mix colors 364 if color[0] == -1: 365 color = mix_bgcolors(tid) 366 else: 367 color = mix_bgcolors(tid) 368 return color 369 370 ## set color for task tid 371 # @return color array 372 # @param[in] tid task id 373 # @param[in] color_s color array 374 # @ingroup EDI_CORE 375 def set_background_color(tid,color_s): 376 f = open('%s/bgColor'%generate_task_path(tid),'w') 377 f.write(color_s) 378 f.close() 379 edi_log('set background color in %s to %s'%(tid,color_s)) 380 381 ## converts color to hex format 382 # @return color hex string 383 # @param[in] color color array 384 # @ingroup EDI_CORE 385 def color_to_hex(color): 386 c = [] 387 for n in color: 388 if n == -1: 389 # -1 is no color, white 390 c.append(255) 391 else: 392 c.append(n) 393 color = tuple(c) 394 return '%02x%02x%02x' % (color[0],color[1],color[2]) 395 396 ## filesystem path for task in database tasks 397 # @return path 398 # @param[in] tid task id 399 # @ingroup EDI_CORE 400 def generate_task_path(tid): 401 return '%s/%s'%(data_location_tasks,tid) 402 403 ## filesystem path for group in database groups 404 # @return path 405 # @param[in] tid task id 406 # @ingroup EDI_CORE 407 def generate_group_path(tid): 408 return '%s/%s/'%(data_location_groups,tid) 409 410 ## get status for tid 411 # @return status string 412 # @param[in] tid task id 413 # @ingroup EDI_CORE 414 def get_status(tid): 415 # open status file 416 try: 417 f = open('%s/status'%generate_task_path(tid)) 418 status = f.readline() 419 f.close() 420 except: 421 print '%s is an invalid task.'%generate_task_path(tid) 422 status = TASK_STATUS[TASK_STATUS_VOID] 423 return status 424 425 ## creates a string with task id, status and string parameter 426 # @return list of strings 427 # @param[in] tid task id 428 # @param[in] s task title string 429 # @ingroup EDI_CORE 430 # Displays a task depending on edi_core.list_option 431 def generate_task_string_with_tid(tid,s): 432 status = get_status(tid) 433 if list_option == 'tids': 434 # %*s to dynamically adjust id length 435 r = '%*s - %s - %s'%(ID_LENGTH,tid,status,s) 436 if list_option == 'positions': 437 if is_this_task_a_group(tid): 438 # print tid for groups in the list 439 r = '%*s - %s - %s'%(ID_LENGTH,tid,status,s) 440 else: 441 tid = ' ' 442 r = '%*s %s - %s'%(ID_LENGTH,tid,status,s) 443 if list_option == 'md': 444 # print title and description 445 f = open('%s/description.txt'%generate_task_path(tid)) 446 # remove title line 447 description = ''.join(f.readlines()[1:]) 448 f.close() 449 450 tid = ' ' 451 # add <br> for long titles 452 r = '\n---\n#%s<br>\n%s'%(s,description) 453 if list_option == 'rst': 454 # print title and description 455 f = open('%s/description.txt'%generate_task_path(tid)) 456 # remove title line 457 description_l = f.readlines()[1:] 458 f.close() 459 460 # convert urls to link 461 NOBRACKET = r'[^\]\[]*' 462 BRK = ( r'\[(' 463 + (NOBRACKET + r'(\[')*6 464 + (NOBRACKET+ r'\])*')*6 465 + NOBRACKET + r')\]' ) 466 NOIMG = r'(?<!\!)' 467 LINK_RE = NOIMG + BRK + \ 468 r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12\s*)?\)''' 469 # [text](url) or [text](<url>) or [text](url "title") 470 471 compiled_re = re.compile("^(.*?)%s(.*?)$" % LINK_RE, re.DOTALL | re.UNICODE) 472 473 for line_index,l in enumerate(description_l): 474 if 'http' in l: 475 try: 476 match = compiled_re.match(l) 477 url = match.group(9) 478 text = match.group(2) 479 before_url = match.group(1) 480 #title = match.group(13) 481 after_url = match.group(14) 482 483 l = '%s`%s<%s>`_%s'% (before_url, text, url, after_url) 484 except: 485 # not used, information 486 link_status = 'WARNING: the link is not in format [title](url)' 487 description_l[line_index] = l 488 489 description = ''.join(description_l) 490 491 tid = ' ' 492 # remove position and type (GROUP, LINK) from title 493 title = s[11:].lstrip() 494 r = '%s\n'%title + '='*len(title) + '\n%s\n\n'%description 495 if list_option == 'html': 496 #<tr bgcolor="#FEFFCC"> 497 # <td class="subject"> <font color="#000000">t1</font></td> 498 # <td class="description"> <font color="#000000"> </font></td> 499 #</tr> 500 #:define read_description 501 # convert < to < and > > to ignore html tags in title line 502 s = s.replace('<','<').replace('>','>') 503 f = open('%s/description.txt'%generate_task_path(tid)) 504 # remove title line, convert < to < and > > to ignore html tags 505 description_l = ['%s<br>'%i.rstrip().replace('<','<').replace('>','>') for i in f.readlines()[1:]] 506 f.close() 507 508 # convert urls to link 509 NOBRACKET = r'[^\]\[]*' 510 BRK = ( r'\[(' 511 + (NOBRACKET + r'(\[')*6 512 + (NOBRACKET+ r'\])*')*6 513 + NOBRACKET + r')\]' ) 514 NOIMG = r'(?<!\!)' 515 LINK_RE = NOIMG + BRK + \ 516 r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12\s*)?\)''' 517 # [text](url) or [text](<url>) or [text](url "title") 518 519 compiled_re = re.compile("^(.*?)%s(.*?)$" % LINK_RE, re.DOTALL | re.UNICODE) 520 521 for line_index,l in enumerate(description_l): 522 if 'http' in l: 523 try: 524 match = compiled_re.match(l) 525 url = match.group(9) 526 text = match.group(2) 527 before_url = match.group(1) 528 #title = match.group(13) 529 after_url = match.group(14) 530 531 l = '%s<a href="%s">%s</a>%s'% (before_url, url, text, after_url) 532 except: 533 # not used, information 534 link_status = 'WARNING: the link is not in format [title](url)' 535 description_l[line_index] = l 536 537 description = '\n'.join(description_l) 538 #:end 539 540 if is_this_task_a_group(tid) and user_interface == 'web': 541 subject = '<a href="edi_web.py?tid=%s">%s - %s</a>'%(tid,status,s) 542 else: 543 subject = '%s - %s'%(status,s) 544 fg = color_to_hex(get_forground_color(tid)) 545 bg = color_to_hex(get_background_color(tid)) 546 r=' <tr bgcolor="#%s">\n <td class="subject"> <font color="#%s">%s</font></td>\n <td class="description"> <font color="#%s">%s</font></td>\n </tr>' % (bg, fg, subject, fg, description) 547 return r 548 549 ## creates a string with task id, status and string parameter 550 # @return list of strings 551 # @param[in] tid task id 552 # @param[in] s task title string 553 # @ingroup EDI_CORE 554 # Displays a group depending on edi_core.list_option 555 def generate_group_string_with_tid(tid,s): 556 global agenda 557 558 status = get_status(tid) 559 if (list_option == 'tids') or (list_option == 'positions'): 560 # %*s to dynamically adjust id length 561 r = '%*s - %s - %s'%(ID_LENGTH,tid,status,s) 562 if list_option == 'md': 563 tid = ' ' 564 # print group title only 565 r = '#%s'%s.replace(' 0 - GROUP','') 566 if agenda: 567 r += '\n---\n# Agenda\n%s' % '\n\n'.join(agenda) 568 if list_option == 'rst': 569 tid = ' ' 570 # print group title only 571 title = s.replace(' 0 - GROUP','') 572 r = '='*len(title) + '\n%s\n'%title + '='*len(title) + '\n\n' 573 if agenda: 574 r += 'Agenda\n======\n%s\n\n' % '\n'.join(agenda) 575 else: 576 r += '\n.. contents::\n\n' 577 if list_option == 'html': 578 #<tr bgcolor="#FEFFCC"> 579 # <td class="subject"> <font color="#000000">t1</font></td> 580 # <td class="description"> <font color="#000000"> </font></td> 581 #</tr> 582 # display description in html 583 #:read_description 584 # convert < to < and > > to ignore html tags in title line 585 s = s.replace('<','<').replace('>','>') 586 f = open('%s/description.txt'%generate_task_path(tid)) 587 # remove title line, convert < to < and > > to ignore html tags 588 description_l = ['%s<br>'%i.rstrip().replace('<','<').replace('>','>') for i in f.readlines()[1:]] 589 f.close() 590 591 # convert urls to link 592 NOBRACKET = r'[^\]\[]*' 593 BRK = ( r'\[(' 594 + (NOBRACKET + r'(\[')*6 595 + (NOBRACKET+ r'\])*')*6 596 + NOBRACKET + r')\]' ) 597 NOIMG = r'(?<!\!)' 598 LINK_RE = NOIMG + BRK + \ 599 r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12\s*)?\)''' 600 # [text](url) or [text](<url>) or [text](url "title") 601 602 compiled_re = re.compile("^(.*?)%s(.*?)$" % LINK_RE, re.DOTALL | re.UNICODE) 603 604 for line_index,l in enumerate(description_l): 605 if 'http' in l: 606 try: 607 match = compiled_re.match(l) 608 url = match.group(9) 609 text = match.group(2) 610 before_url = match.group(1) 611 #title = match.group(13) 612 after_url = match.group(14) 613 614 l = '%s<a href="%s">%s</a>%s'% (before_url, url, text, after_url) 615 except: 616 # not used, information 617 link_status = 'WARNING: the link is not in format [title](url)' 618 description_l[line_index] = l 619 620 description = '\n'.join(description_l) 621 622 subject = '%s - %s'%(status, s) 623 fg = color_to_hex(get_forground_color(tid)) 624 bg = color_to_hex(get_background_color(tid)) 625 r=' <tr bgcolor="#%s">\n <td class="subject"> <font color="#%s">%s</font></td>\n <td class="description"> <font color="#%s">%s</font></td>\n </tr>' % (bg, fg, subject, fg, description) 626 return r 627 628 ## determines if task is a group 629 # @return empty string or 'this task is a group' 630 # @param[in] tid task id 631 # @ingroup EDI_CORE 632 def is_this_task_a_group(tid): 633 # Identify if task is a group 634 is_a_group = '' 635 # list groups 636 groups = os.listdir(data_location_groups) 637 if tid in groups: 638 is_a_group = 'this task is a group' 639 return is_a_group 640 641 ## get task title 642 # @return first line of task description 643 # @param[in] tid task id 644 # @ingroup EDI_CORE 645 def get_task_title(tid): 646 task_path = generate_task_path(tid) 647 f = open('%s/description.txt'%task_path) 648 r = f.readline().strip() 649 f.close() 650 return r 651 652 ## get creation date 653 # @return array of date string and unix time 654 # @param[in] tid task id 655 # @ingroup EDI_CORE 656 def get_creation_date(tid): 657 r = [] 658 # Figure out creation time and modification times for description and status 659 task_path = generate_task_path(tid) 660 if not os.path.exists('%s/ctime.txt'%task_path): 661 task_ctime = 'Not available' 662 r.append(task_ctime) 663 r.append(0) 664 else: 665 f = open('%s/ctime.txt'%task_path) 666 task_ctime = f.readline() 667 f.close() 668 r.append(task_ctime) 669 r.append(time.mktime(time.strptime(task_ctime))) 670 671 return r 672 673 ## get media 674 # @return media file name 675 # @param[in] tid task id 676 # @ingroup EDI_CORE 677 def get_media(tid): 678 r = ['None'] 679 task_path = generate_task_path(tid) 680 if os.path.exists('%s/media'%task_path): 681 files = os.listdir('%s/media'%task_path) 682 r = [files[0].split('.')[0], '%s/media/%s'%(task_path,files[0])] 683 return r 684 685 ## set media, one media file per task 686 # @return media file name 687 # @param[in] tid task id 688 # @param[in] filename media file to copy to task 689 # @ingroup EDI_CORE 690 def set_media(tid,filename): 691 r = '%s is not a supported media file.' % filename 692 693 task_path = generate_task_path(tid) 694 if filename.strip()[-3:] == 'wav': 695 #:define create_media_folder 696 if not os.path.exists('%s/media'%task_path): 697 # create media folder 698 os.mkdir('%s/media'%task_path) 699 #:end 700 else: 701 # media exists, check type 702 files = os.listdir('%s/media'%task_path) 703 if not files[0][-3:] == 'wav': 704 r = '%s is not a sound.'%tid 705 return r 706 # sound file 707 shutil.copy(filename,'%s/media/sound.wav'%task_path) 708 r = 'sound.wav' 709 if filename.strip()[-3:] == 'jpg': 710 #:create_media_folder 711 if not os.path.exists('%s/media'%task_path): 712 # create media folder 713 os.mkdir('%s/media'%task_path) 714 else: 715 # media exists, check type 716 files = os.listdir('%s/media'%task_path) 717 if not files[0][-3:] == 'jpg': 718 r = '%s is not an image.'%tid 719 return r 720 # image file 721 shutil.copy(filename,'%s/media/image.jpg'%task_path) 722 r = 'image.jpg' 723 edi_log('added media %s in task %s'%(filename,tid)) 724 return r 725 726 ## get attachments 727 # @return attachment file names 728 # @param[in] tid task id 729 # @ingroup EDI_CORE 730 def get_attachments(tid): 731 r = ['None'] 732 task_path = generate_task_path(tid) 733 if os.path.exists('%s/attachments'%task_path): 734 files = os.listdir('%s/attachments'%task_path) 735 r = ['%s/attachments/%s'%(task_path,i) for i in files] 736 return r 737 738 ## set attachments 739 # @return attachment file names 740 # @param[in] tid task id 741 # @param[in] array of filenames 742 # @ingroup EDI_CORE 743 # With *, set_attachments copies multiple files at once 744 def set_attachments(tid,filenames): 745 task_path = generate_task_path(tid) 746 if not os.path.exists('%s/attachments'%task_path): 747 # create attachment folder 748 os.mkdir('%s/attachments'%task_path) 749 750 r = [] 751 for fn in filenames: 752 try: 753 shutil.copy(fn,'%s/attachments/'%task_path) 754 edi_log('added attachment %s in task %s'%(fn,tid)) 755 # Remove path from fn, keep only filename 756 r.append('%s/attachments/%s'%(task_path,fn.split(os.sep)[-1])) 757 except: 758 r.append('Failed to copy %s'%fn) 759 return r 760 761 ## sort task attributes (ls in edi cli) 762 # @return sorted task list by state 763 # @param[in] result from list_group 764 # @ingroup EDI_CORE 765 def sort_task_attributes(task_attributes): 766 r = [] 767 # Keep head group on top 768 if task_attributes[0]['head'] == 'head group': 769 r.append(task_attributes[0]) 770 del task_attributes[0] 771 for s in SORT_TASK_ORDER: 772 for t in task_attributes: 773 if t['status'] == TASK_STATUS[s]: 774 r.append(t) 775 return r 776 777 ## sort function for sorting an array of tasks by date from newest to oldest 778 # @return compare result 779 # @param[in] result from list_group 780 # @ingroup EDI_CORE 781 def sortdatefunc(x,y): 782 return cmp(y['ctime'],x['ctime']) 783 784 ## sort task attributes (ls in edi cli) 785 # @return sorted task list by date 786 # @param[in] result from list_group 787 # @ingroup EDI_CORE 788 def sort_task_attributes_by_date(task_attributes): 789 r = [] 790 # Keep head group on top 791 if task_attributes[0]['head'] == 'head group': 792 r.append(task_attributes[0]) 793 del task_attributes[0] 794 795 # -1 to exclude the empty line 796 a = task_attributes[:-1] 797 a.sort(sortdatefunc) 798 #print task_attributes 799 r += a 800 r.append(task_attributes[-1]) 801 return r 802 803 ## list id - status - group - first line from description 804 # @return list of tasks and groups title 805 # @param[in] tid task id 806 # @ingroup EDI_CORE 807 # items in groups have the format 'ORDER_ID''TASK_ID'<br> 808 # task 0 is group tittle<br> 809 #<br> 810 # result array elements:<br> 811 # {head :string, tid :string, position :value, group :string, title :string, status :string}<br> 812 # head tells if the element is a group title.<br> 813 #<br> 814 # example:<br> 815 #fTTB1KRWfDpoSR1_ - Active - GROUP Motivational Interviewing<br> 816 #62ZvFA_q0pCZFr0Y - Active - news<br> 817 def list_group(tid): 818 result = [] 819 # list visible groups only 820 task_status = get_status(tid) 821 if status_filters_d[task_status] == STATUS_FILTER_STATES[0]: 822 # List task titles in group 823 group_path = generate_group_path(tid) 824 825 # List groups to indicate when a task is a group 826 # Identify if task is a group 827 groups = os.listdir(data_location_groups) 828 # End Identify if task is a group 829 830 # print items in tid in order 831 for n,fn in enumerate(sorted(os.listdir(group_path))): 832 # print position in list 833 current_postion = baseconvert_to_dec(fn[:ORDER_ID_LENGTH]) 834 835 # Identify if task is a group 836 # Remove order_id, keep task id only 837 group = ' ' 838 if fn[ORDER_ID_LENGTH:] in groups: 839 group = 'GROUP' 840 if is_linked(fn[ORDER_ID_LENGTH:]): 841 group = ' LINK' 842 # End Identify if task is a group 843 # Get task title 844 # Remove order_id, keep task id only 845 task_path = generate_task_path(fn[ORDER_ID_LENGTH:]) 846 f = open('%s/description.txt'%task_path) 847 # Remove order_id, keep task id only 848 if (not n) and (not tid == 'root'): 849 # First task is group title 850 # filter status, keep task if status filter is enabled 851 task_status = get_status(fn[ORDER_ID_LENGTH:]) 852 if status_filters_d[task_status] == STATUS_FILTER_STATES[0]: 853 task_d = {} 854 task_d['head'] = 'head group' 855 task_d['tid'] = fn[ORDER_ID_LENGTH:] 856 task_d['position'] = current_postion 857 task_d['group'] = group 858 task_d['title'] = f.readline().strip() 859 task_d['status'] = get_status(task_d['tid']) 860 task_d['ctime'] = get_creation_date(task_d['tid'])[1] 861 result.append(task_d) 862 else: 863 # filter status, keep task if status filter is enabled 864 task_status = get_status(fn[ORDER_ID_LENGTH:]) 865 if status_filters_d[task_status] == STATUS_FILTER_STATES[0]: 866 task_d = {} 867 task_d['head'] = 'element' 868 task_d['tid'] = fn[ORDER_ID_LENGTH:] 869 task_d['position'] = current_postion 870 task_d['group'] = group 871 task_d['title'] = f.readline().strip() 872 task_d['status'] = get_status(task_d['tid']) 873 task_d['ctime'] = get_creation_date(task_d['tid'])[1] 874 result.append(task_d) 875 f.close() 876 task_d = {} 877 task_d['head'] = 'empty line' 878 task_d['tid'] = '' 879 task_d['position'] = 0 880 task_d['group'] = '' 881 task_d['title'] = '' 882 task_d['status'] = '' 883 task_d['ctime'] = 0 884 result.append(task_d) 885 return result 886 887 ## lists all items in the tid group 888 # @return list of tasks and groups title 889 # @param[in] tid task id 890 # @ingroup EDI_CORE 891 def list_tree(tid): 892 result = [] 893 # walk_group is the list of groups to visit. FIFO 894 walk_group = [tid] 895 # the while loop goes through all the group that are found 896 while walk_group: 897 # list visible groups only 898 task_status = get_status(walk_group[0]) 899 if status_filters_d[task_status] == STATUS_FILTER_STATES[0]: 900 # list items in first group 901 tasks = sorted(os.listdir(generate_group_path(walk_group[0]))) 902 result += list_group(walk_group[0]) 903 904 # add group found in first group 905 for t in tasks: 906 # Check tasks that are not title task in a group 907 if (t[ORDER_ID_LENGTH:] != walk_group[0]) and is_this_task_a_group(t[ORDER_ID_LENGTH:]): 908 walk_group.append(t[ORDER_ID_LENGTH:]) 909 910 # remove first group to list items in next group 911 del walk_group[0] 912 return result 913 914 ## generates a random task id 915 # @return new task id 916 # @ingroup EDI_CORE 917 def generate_id(): 918 return ''.join([random.choice(ID_BASE) for _ in range(ID_LENGTH)]) 919 920 ## converts decimal number to BASE (base 65) 921 # @return string representing a number in base BASE 922 # @param[in] n integer 923 # @ingroup EDI_CORE 924 def baseconvert(n): 925 s = "" 926 while 1: 927 r = n % BASE 928 s = ID_BASE[r] + s 929 n = n / BASE 930 if n == 0: 931 break 932 933 s = s.rjust(ORDER_ID_LENGTH, ',') 934 return s 935 936 ## converts BASE number to decimal 937 # @return n integer 938 # @param[in] n string representing a number in base BASE 939 # @ingroup EDI_CORE 940 def baseconvert_to_dec(n): 941 r = 0 942 power = 1 943 for digit in reversed(list(n)): 944 r += ID_BASE_STRING.find(digit) * power 945 power *= BASE 946 return r 947 948 ## add task to group folder in database groups 949 # @param[in] tid task id 950 # @param[in] group task id 951 # @ingroup EDI_CORE 952 # Tasks are added at the top or bottom of the list. 953 def add_task_to_group_folder(tid,group): 954 # Create an entry in group 955 tasks = sorted(os.listdir(generate_group_path(group))) 956 # Add +1 to last order_id to have the task last in the list 957 if tasks: 958 if (len(tasks) == 1) or (add_top_or_bottom == 'bottom'): 959 # add tasks in bottom 960 order_id = baseconvert(baseconvert_to_dec(tasks[-1][:ORDER_ID_LENGTH])+1) 961 else: 962 # add tasks on top 963 # temporary orderid # 964 orderid_and_tid = '#' * ORDER_ID_LENGTH + tid 965 if group == 'root': 966 to_pos = 0 967 else: 968 # add new tasks after group title 969 to_pos = 1 970 971 tasks = tasks[:to_pos] + [orderid_and_tid] + tasks[to_pos:] 972 973 # Move tasks 974 path = generate_group_path(group) 975 for n,t in enumerate(tasks): 976 if n == to_pos: 977 # set orderid on top 978 order_id = baseconvert(n) 979 else: 980 os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:])) 981 982 else: 983 # start at 0 when group is empty 984 order_id = baseconvert(0) 985 tid_in_groups_path = '%s/%s%s'%(generate_group_path(group),order_id,tid) 986 # remove double slash that can come up 987 tid_in_groups_path = tid_in_groups_path.replace('//','/') 988 f = open(tid_in_groups_path,'w') 989 f.close() 990 991 # return tid_in_groups_path to easily add new tasks to group_directory_file_list and save time 992 return tid_in_groups_path 993 994 ## creates a task and opens vi 995 # @param[in] group task id 996 # @ingroup EDI_CORE 997 def create_task(group): 998 # Open text editor 999 # create task in tasks folder 1000 #:define create_task_part1 1001 tid = generate_id() 1002 1003 # Save text in tasks 1004 task_path = generate_task_path(tid) 1005 os.mkdir(task_path) 1006 #:end 1007 os.system('%s %s/description.txt' % (editor,task_path)) 1008 if not os.path.isfile('%s/description.txt' % task_path): 1009 # file doesnt exist, abort task creation 1010 shutil.rmtree(task_path) 1011 return 'no new task, the description is empty' 1012 1013 # Save creation time 1014 #:define save_task_creation_time 1015 f = open('%s/ctime.txt'%task_path,'w') 1016 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/ctime.txt'%task_path) 1017 task_mtime = time.ctime(mtime) 1018 f.write(task_mtime) 1019 f.close() 1020 #:end 1021 1022 # create status, active by default 1023 #:define create_task_part2 1024 # Create status 1025 f = open('%s/status'%task_path,'w') 1026 f.write(TASK_STATUS[TASK_STATUS_ACTIVE]) 1027 f.close() 1028 1029 tid_in_groups_path = add_task_to_group_folder(tid, group) 1030 1031 global group_directory_file_list 1032 if group_directory_file_list: 1033 group_directory_file_list.append(tid_in_groups_path) 1034 1035 # autolink 1036 global autolink 1037 if autolink: 1038 for g in autolink: 1039 if is_this_task_a_group(g): 1040 # link to groups existing in current database 1041 add_task_reference_to_a_group(tid,g) 1042 #:end 1043 edi_log('created %s in group %s'%(tid,group)) 1044 return tid 1045 1046 ## create task and copy text file 1047 # @param[in] group task id 1048 # @param[in] text_file filename of text file to copy 1049 # @ingroup EDI_CORE 1050 def add_task(group,text_file): 1051 #:create_task_part1 1052 tid = generate_id() 1053 1054 # Save text in tasks 1055 task_path = generate_task_path(tid) 1056 os.mkdir(task_path) 1057 shutil.copy(text_file,'%s/description.txt'%task_path) 1058 #:save_task_creation_time 1059 f = open('%s/ctime.txt'%task_path,'w') 1060 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/ctime.txt'%task_path) 1061 task_mtime = time.ctime(mtime) 1062 f.write(task_mtime) 1063 f.close() 1064 #:create_task_part2 1065 # Create status 1066 f = open('%s/status'%task_path,'w') 1067 f.write(TASK_STATUS[TASK_STATUS_ACTIVE]) 1068 f.close() 1069 1070 tid_in_groups_path = add_task_to_group_folder(tid, group) 1071 1072 global group_directory_file_list 1073 if group_directory_file_list: 1074 group_directory_file_list.append(tid_in_groups_path) 1075 1076 # autolink 1077 global autolink 1078 if autolink: 1079 for g in autolink: 1080 if is_this_task_a_group(g): 1081 # link to groups existing in current database 1082 add_task_reference_to_a_group(tid,g) 1083 edi_log('created %s in group %s'%(tid,group)) 1084 return tid 1085 1086 ## create task with description text 1087 # @param[in] group task id 1088 # @param[in] text string 1089 # @ingroup EDI_CORE 1090 def add_text(group,text): 1091 #:create_task_part1 1092 tid = generate_id() 1093 1094 # Save text in tasks 1095 task_path = generate_task_path(tid) 1096 os.mkdir(task_path) 1097 f = open('%s/description.txt'%task_path,'w') 1098 f.write(text) 1099 f.close() 1100 #:save_task_creation_time 1101 f = open('%s/ctime.txt'%task_path,'w') 1102 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/ctime.txt'%task_path) 1103 task_mtime = time.ctime(mtime) 1104 f.write(task_mtime) 1105 f.close() 1106 #:create_task_part2 1107 # Create status 1108 f = open('%s/status'%task_path,'w') 1109 f.write(TASK_STATUS[TASK_STATUS_ACTIVE]) 1110 f.close() 1111 1112 tid_in_groups_path = add_task_to_group_folder(tid, group) 1113 1114 global group_directory_file_list 1115 if group_directory_file_list: 1116 group_directory_file_list.append(tid_in_groups_path) 1117 1118 # autolink 1119 global autolink 1120 if autolink: 1121 for g in autolink: 1122 if is_this_task_a_group(g): 1123 # link to groups existing in current database 1124 add_task_reference_to_a_group(tid,g) 1125 edi_log('created %s in group %s'%(tid,group)) 1126 return tid 1127 1128 ## create task and copy text file, filename is task title 1129 # @param[in] group task id 1130 # @param[in] text_file filename of text file to copy 1131 # @ingroup EDI_CORE 1132 def add_task_and_filename(group,text_file): 1133 tid = add_task(group,text_file) 1134 1135 # Add file name on first line 1136 f = open('%s/description.txt'%generate_task_path(tid)) 1137 des = f.readlines() 1138 f.close() 1139 f = open('%s/description.txt'%generate_task_path(tid),'w') 1140 # save text filename only 1141 f.write('%s\n'%text_file.split('/')[-1]) 1142 for l in des: 1143 f.write(l) 1144 f.close() 1145 1146 return tid 1147 1148 ## create many tasks from a text file 1149 # @param[in] group task id 1150 # @param[in] text_file filename of text file to copy 1151 # @ingroup EDI_CORE 1152 def add_many_tasks(group,text_file): 1153 r = '' 1154 1155 # Copy a task to text string 1156 # Remove '#' from first line, first character position 1157 # Add task to database 1158 f = open(text_file) 1159 status = 'start' 1160 text = '' 1161 number_of_tasks = 0 1162 for l in f.readlines(): 1163 if l.rstrip() == '---': 1164 if status != 'start': 1165 # add task to database 1166 add_text(group,text) 1167 number_of_tasks += 1 1168 text = '' 1169 status = 'start' 1170 else: 1171 if (status == 'start') and (l[0] == '#'): 1172 l = l[1:] 1173 text += l 1174 status = 'writing task' 1175 f.close() 1176 if status == 'writing task': 1177 # add task to database 1178 add_text(group,text) 1179 number_of_tasks += 1 1180 1181 edi_log('created %s tasks in group %s'%(number_of_tasks,group)) 1182 r = 'created %s tasks in group %s'%(number_of_tasks,group) 1183 return r 1184 1185 ## create many one line tasks from a text file 1186 # @param[in] group task id 1187 # @param[in] text_file filename of text file to copy 1188 # @ingroup EDI_CORE 1189 def add_many_one_line_tasks(group,text_file): 1190 r = [] 1191 1192 f = open(text_file) 1193 number_of_tasks = 0 1194 for l in f.readlines(): 1195 add_text(group,l.strip()) 1196 number_of_tasks += 1 1197 f.close() 1198 1199 edi_log('created %s tasks in group %s'%(number_of_tasks,group)) 1200 r = 'created %s tasks in group %s'%(number_of_tasks,group) 1201 return r 1202 1203 ## create group from text file 1204 # @param[in] group task id 1205 # @param[in] text_file with group structure 1206 # @ingroup EDI_CORE 1207 def add_many_groups_from_text(group,text_file): 1208 r = [] 1209 1210 f = open(text_file) 1211 number_of_tasks = 0 1212 group_stack = [group] 1213 for l in f.readlines(): 1214 # count space indents 1215 l_l = l.split(' ') 1216 indent = 0 1217 for s in l_l: 1218 if s: 1219 break 1220 indent += 1 1221 if indent < len(group_stack)-1: 1222 # remove groups from stack if same level or higher levels 1223 del group_stack[-(len(group_stack)-1 - indent):] 1224 l = l.strip() 1225 tid = add_text(group_stack[-1],l) 1226 create_group(tid) 1227 group_stack.append(tid) 1228 number_of_tasks += 1 1229 f.close() 1230 1231 edi_log('created %s groups in group %s'%(number_of_tasks,group)) 1232 r = 'created %s groups in group %s'%(number_of_tasks,group) 1233 return r 1234 1235 ## copy description to path using first line of description as filename 1236 # @return path and filname 1237 # @param[in] tid task id 1238 # @param[in] path destination directory for task description 1239 # @ingroup EDI_CORE 1240 def export_task_to_a_file(tid,path): 1241 f = open('%s/description.txt'%generate_task_path(tid)) 1242 fn = f.readline().strip() 1243 des = f.readlines() 1244 f.close() 1245 f = open('%s/%s'%(path,fn),'w') 1246 for l in des: 1247 f.write(l) 1248 f.close() 1249 r = '%s/%s'%(path,fn) 1250 # remove eventual double // 1251 return r.replace(os.sep*2,os.sep) 1252 1253 ## print description of task tid 1254 # @return list of strings 1255 # @param[in] tid task id 1256 # @ingroup EDI_CORE 1257 def display_task(tid): 1258 task_path = generate_task_path(tid) 1259 f = open('%s/description.txt'%task_path) 1260 description = f.readlines() 1261 f.close() 1262 1263 # print tid, status and first line 1264 description[0] = generate_task_string_with_tid(tid,description[0]) 1265 return description 1266 1267 ## find group containing task tid in groups folder 1268 # @return tid 1269 # @param[in] tid task id 1270 # @ingroup EDI_CORE 1271 def find_group_containing_task(tid): 1272 if tid == 'root': 1273 # root has not parent group, return root 1274 return tid 1275 #:define walk_groups 1276 global data_location_groups 1277 # reuse previously created group list, to save time 1278 global group_directory_file_list 1279 if not group_directory_file_list: 1280 group_directory_file_list = ffind(data_location_groups) 1281 groups_and_tasks = group_directory_file_list 1282 for t in groups_and_tasks: 1283 if (tid == t[-ID_LENGTH:]) and (tid != t.split('/')[-2]): 1284 group = t.split('/')[-2] 1285 #:end 1286 try: 1287 a = group 1288 except: 1289 print '\n\nEDI_ERROR: Database inconsistent - run: find %s|grep %s and remove reference.\n'%(data_location, tid) 1290 group = 'error' 1291 return group 1292 1293 ## find group containing task tid in groups folder and print 1294 # @return list of strings 1295 # @param[in] tid task id 1296 # @ingroup EDI_CORE 1297 # Shows path in tree and title for each group in path 1298 def show_group_for_task(tid): 1299 global data_location_tree 1300 r = [] 1301 1302 # set group string to be displayed on first line 1303 group_s = ' ' 1304 if is_this_task_a_group(tid): 1305 group_s = 'GROUP' 1306 if is_linked(tid): 1307 group_s = ' LINK' 1308 1309 #:walk_groups 1310 global data_location_groups 1311 # reuse previously created group list, to save time 1312 global group_directory_file_list 1313 if not group_directory_file_list: 1314 group_directory_file_list = ffind(data_location_groups) 1315 groups_and_tasks = group_directory_file_list 1316 for t in groups_and_tasks: 1317 if (tid == t[-ID_LENGTH:]) and (tid != t.split('/')[-2]): 1318 group = t.split('/')[-2] 1319 tree_path = find_group_in_tree(group) 1320 # p is data_location folder/tree 1321 p_l = data_location_tree.split('/')[-2:] 1322 p = '/'.join(p_l) 1323 # if empty then command is run in tree root 1324 if tree_path.split(p)[-1]: 1325 # print path of tids: tid/tid... 1326 r.append(tree_path.split(p)[-1][1:]) 1327 group_titles_in_path = [] 1328 for g in tree_path.split(p)[-1][1:].split('/'): 1329 group_titles_in_path.append(get_task_title(g)) 1330 # print title/title... 1331 r.append('/'.join(group_titles_in_path)) 1332 r.append(generate_group_string_with_tid(group,get_task_title(group))) 1333 r.append('') 1334 1335 # Print media and attachments 1336 # media type 1337 media = get_media(tid)[0] 1338 # attachment list 1339 attachments = get_attachments(tid) 1340 1341 # Print task, colors, group list 1342 color = get_forground_color(tid) 1343 fc = '%d,%d,%d,%d' % (color[0],color[1],color[2],color[3]) 1344 color = get_background_color(tid) 1345 bc = '%d,%d,%d,%d' % (color[0],color[1],color[2],color[3]) 1346 # Figure out creation time and modification times for description and status 1347 task_ctime = get_creation_date(tid)[0] 1348 1349 task_path = generate_task_path(tid) 1350 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/description.txt'%task_path) 1351 description_mtime = time.ctime(mtime) 1352 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/status'%task_path) 1353 status_mtime = time.ctime(mtime) 1354 r = ['Task:\n%s\n\n'%generate_task_string_with_tid( tid,'%s %s'%(group_s, get_task_title(tid)) )] + ['Media type: %s'%media] + ['\nAttachments:'] + attachments + ['\n\nforeground color: %s\nbackground color: %s\nCreation time: %s\nLast description change: %s\nLast status change: %s\n\nGroup list:\n' % (fc, bc, task_ctime, description_mtime, status_mtime)] + r 1355 return r 1356 1357 ## find group in tree folder 1358 # @return path_in_tree 1359 # @param[in] tid task id 1360 # @ingroup EDI_CORE 1361 def find_group_in_tree(group): 1362 if group == 'root': 1363 path_in_tree = data_location_tree 1364 else: 1365 path_in_tree = '' 1366 # list all group paths in tree 1367 f = os.popen('find %s'%data_location_tree) 1368 groups = [i.strip() for i in f.readlines()] 1369 f.close() 1370 # remove data_location_tree from paths 1371 del groups[0] 1372 # find the group in group paths 1373 for g in groups: 1374 if g.split('/')[-1] == group: 1375 path_in_tree = g 1376 return path_in_tree 1377 1378 ## Determines if a task has multiple references 1379 # @return integer 0 or 1 1380 # @param[in] tid task id 1381 # @ingroup EDI_CORE 1382 def is_linked(tid): 1383 status = 0 1384 task_linked_groups_path = '%s/groups/' % generate_task_path(tid) 1385 # check if tid/groups exists 1386 if os.path.exists(task_linked_groups_path): 1387 groups = os.listdir(task_linked_groups_path) 1388 # check if task is linked to more than 1 group 1389 if len(groups) > 1: 1390 status = 1 1391 return status 1392 1393 ## convert task to group 1394 # @return list of stings when there is an error 1395 # @param[in] tid task id 1396 # @ingroup EDI_CORE 1397 def create_group(tid): 1398 r = [] 1399 # convert task to group only when task is not linked 1400 if is_linked(tid): 1401 r.append('Converting linked task to group removes links. The task groups are:') 1402 r += show_group_for_task(tid) 1403 1404 # delete tid/groups because groups are not linked 1405 task_linked_groups_path = '%s/groups/' % generate_task_path(tid) 1406 groups = os.listdir(task_linked_groups_path) 1407 # remove all links except for the first group 1408 for g in groups[1:]: 1409 delete_linked_task(g,tid) 1410 if os.path.exists(task_linked_groups_path): 1411 shutil.rmtree(task_linked_groups_path) 1412 r.append('Created group %s in %s'%(tid,groups[0])) 1413 # create new group in groups folder 1414 os.mkdir('%s/%s'%(data_location_groups,tid)) 1415 # First task in group is group title task 1416 order_id = baseconvert(0) 1417 f = open('%s/%s%s'%(generate_group_path(tid),order_id,tid),'w') 1418 f.close() 1419 1420 # Add group in tree 1421 # update group list 1422 global group_directory_file_list 1423 group_directory_file_list = [] 1424 group = find_group_containing_task(tid) 1425 if group == 'root': 1426 os.mkdir('%s/%s'%(data_location_tree,tid)) 1427 else: 1428 os.mkdir('%s/%s'%(find_group_in_tree(group),tid)) 1429 1430 # Change status active to void by default 1431 # To avoid filtering groups 1432 if get_status(tid) == TASK_STATUS[TASK_STATUS_ACTIVE]: 1433 # set status to void 1434 set_status(tid,TASK_STATUS_VOID) 1435 edi_log('created group %s'%tid) 1436 return r 1437 1438 ## convert group to task 1439 # @param[in] group group to convert to task 1440 # @ingroup EDI_CORE 1441 def convert_group_to_task(group): 1442 r = [] 1443 1444 # list all tasks (data_location_groups/GROUP/'ORDER_ID''TASK_ID') in groups folder 1445 groups_and_tasks = ffind(data_location_groups) 1446 1447 convert_group_status = 'delete' 1448 for t2 in groups_and_tasks: 1449 # search a task in group that is not tid 1450 if ('%s/'%group in t2) and (group not in t2.split('/')[-1]): 1451 convert_group_status = 'There is another task in the group, keep group.' 1452 r.append(convert_group_status) 1453 if (convert_group_status == 'delete') and (not 'root' in group): 1454 # Delete group title and group folder 1455 os.remove('%s/%s%s'%(generate_group_path(group),baseconvert(0),group)) 1456 os.rmdir(generate_group_path(group)) 1457 # Delete group in tree 1458 if find_group_in_tree(group): 1459 shutil.rmtree(find_group_in_tree(group)) 1460 # change state from void to active 1461 set_status(group,TASK_STATUS_ACTIVE) 1462 edi_log('converted group %s to task'%group) 1463 return r 1464 1465 ## delete task tid in all groups 1466 # @return group id 1467 # @param[in] tid task id 1468 # @ingroup EDI_CORE 1469 def delete_task(tid): 1470 # save original tid parameter for log 1471 tid_param = tid 1472 # Delete task in tasks 1473 shutil.rmtree(generate_task_path(tid)) 1474 1475 # Delete task reference in groups 1476 # list all tasks (data_location_groups/GROUP/'ORDER_ID''TASK_ID') in groups folder 1477 groups_and_tasks = ffind(data_location_groups) 1478 1479 # Identify if task is a group 1480 is_a_group = is_this_task_a_group(tid) 1481 1482 # Return group task, it changes when first task is deleted 1483 group_id = '' 1484 # list groups to reorder at the end 1485 reorder_groups = [] 1486 for t in groups_and_tasks: 1487 # Delete reference in upper group, not the title task of the group 1488 if (tid == t[-ID_LENGTH:]) and (tid != t.split('/')[-2]): 1489 # group is the group for task tid 1490 group = t.split('/')[-2] 1491 group_id = group 1492 # Delete task reference in group 1493 os.remove(t) 1494 reorder_groups.append(group_id) 1495 if is_a_group: 1496 # First task becomes a group, add a reference in group group 1497 # list tasks in order in tid group 1498 group_tasks = sorted(os.listdir(generate_group_path(tid))) 1499 # Delete emtpy group or first task in group becomes the group title. 1500 if len(group_tasks) == 1: 1501 shutil.rmtree(generate_group_path(tid)) 1502 # Delete group in tree 1503 # when a group is deleted, subgroups are automatically deleted in the tree 1504 if find_group_in_tree(tid): 1505 shutil.rmtree(find_group_in_tree(tid)) 1506 else: 1507 # Remove order_id, keep task id only 1508 first_task = group_tasks[1][ORDER_ID_LENGTH:] 1509 group_id = first_task 1510 # Create an entry in group at the same position as tid had 1511 order_id = t.split('/')[-1][:ORDER_ID_LENGTH] 1512 f = open('%s/%s%s'%(generate_group_path(group),order_id,first_task),'w') 1513 f.close() 1514 1515 # Delete group task of group of more then 2 tasks, first task becomes a group 1516 # delete orderidtaskid 1517 os.remove('%s/%s'%(generate_group_path(tid),group_tasks[0])) 1518 os.rename('%s/%s'%(generate_group_path(tid),group_tasks[1]),'%s/%s%s'%(generate_group_path(tid),baseconvert(0),first_task)) 1519 # reorder tasks to remove gap between group title task and first task 1520 path = generate_group_path(tid) 1521 tasks = sorted(os.listdir(path)) 1522 for n,t in enumerate(tasks): 1523 os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:])) 1524 # rename group in groups folder 1525 os.rename(generate_group_path(tid),generate_group_path(first_task)) 1526 # rename group in tree folder 1527 group_tree_path = find_group_in_tree(tid) 1528 group_tree_path_l = group_tree_path.split('/') 1529 group_tree_path_l[-1] = first_task 1530 new_group_tree_path = '/'.join(group_tree_path_l) 1531 os.rename(group_tree_path,new_group_tree_path) 1532 1533 # reorder tasks to remove gaps in reorder_groups 1534 for tid in reorder_groups: 1535 path = generate_group_path(tid) 1536 tasks = sorted(os.listdir(path)) 1537 for n,t in enumerate(tasks): 1538 os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:])) 1539 1540 # Return group task 1541 edi_log('deleted %s in %s'%(tid_param,' '.join(reorder_groups))) 1542 return group_id 1543 1544 ## Delete task only if it is linked in one group 1545 # @return group id 1546 # @param[in] group task id 1547 # @param[in] tid task id 1548 # @ingroup EDI_CORE 1549 def delete_linked_task(group,tid): 1550 group_id = group 1551 if not is_linked(tid): 1552 group_id = delete_task(tid) 1553 else: 1554 # Delete task reference in group 1555 # find task in group: ORDER_IDTASK_ID 1556 tasks = os.listdir(generate_group_path(group)) 1557 for t in tasks: 1558 if t[ORDER_ID_LENGTH:] == tid: 1559 os.remove('%s%s' % (generate_group_path(group), t)) 1560 1561 # delete group in tid/groups 1562 os.remove('%s/groups/%s' % (generate_task_path(tid), group)) 1563 1564 # reorder tasks to remove gaps in group 1565 path = generate_group_path(group) 1566 tasks = os.listdir(path) 1567 for n,t in enumerate(tasks): 1568 os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:])) 1569 1570 edi_log('deleted %s in group %s'%(tid,group)) 1571 1572 return group_id 1573 1574 ## delete group tid 1575 # @return group id 1576 # @param[in] tid task id 1577 # @ingroup EDI_CORE 1578 def delete_group(tid): 1579 # Delete tasks in group 1580 group_tasks = sorted(os.listdir(generate_group_path(tid))) 1581 if (len(group_tasks) == 0) and (tid == 'root'): 1582 # the database is already empty 1583 return tid 1584 if tid != 'root': 1585 # root group doesnt have a title 1586 # Remove group title from the loop to delete tasks only and then group 1587 del group_tasks[0] 1588 1589 # Delete tasks and groups recursively 1590 for t in group_tasks: 1591 # Remove order_id, keep task id only 1592 oid = t[ORDER_ID_LENGTH:] 1593 if not is_this_task_a_group(oid): 1594 # delete tasks that are linked only once 1595 delete_linked_task(tid, oid) 1596 else: 1597 delete_group(oid) 1598 1599 if tid != 'root': 1600 # never delete root group 1601 return delete_task(tid) 1602 else: 1603 # return root and keep root task 1604 return tid 1605 1606 ## edit task with vi 1607 # @param[in] tid task id 1608 # @ingroup EDI_CORE 1609 def edit_task(tid): 1610 os.system('%s %s/description.txt' % (editor, generate_task_path(tid))) 1611 edi_log('edited %s'%tid) 1612 1613 ## move task from group at at_pos to to_pos and reorder 1614 # @return list of stings when there is an error 1615 # @param[in] group task id 1616 # @param[in] at_pos selected position 1617 # @param[in] to_pos insert position 1618 # @ingroup EDI_CORE 1619 def change_task_order(group,at_pos,to_pos): 1620 # save original group parameter for log 1621 group_param = group 1622 # List tasks in group 1623 r = [] 1624 path = generate_group_path(group) 1625 tasks = sorted(os.listdir(path)) 1626 1627 # Verify position 1628 # Get task 1629 try: 1630 orderid_and_tid = tasks[at_pos] 1631 except: 1632 r.append('%d is an invalid position.'%at_pos) 1633 return r 1634 if to_pos == 0: 1635 tid = orderid_and_tid[ORDER_ID_LENGTH:] 1636 # do not move linked tasks to position 0 1637 if is_linked(tid): 1638 r.append('Converting linked task to group removes links. The task groups are:') 1639 r += show_group_for_task(tid) 1640 1641 # delete tid/groups because groups are not linked 1642 task_linked_groups_path = '%s/groups/' % generate_task_path(tid) 1643 groups = os.listdir(task_linked_groups_path) 1644 # remove all links except for the first group 1645 for g in groups: 1646 if not g == group: 1647 delete_linked_task(g,tid) 1648 if os.path.exists(task_linked_groups_path): 1649 shutil.rmtree(task_linked_groups_path) 1650 parent_group = find_group_containing_task(group) 1651 r.append('Created group %s in %s'%(tid,parent_group)) 1652 # do not move groups to position 0 1653 if is_this_task_a_group(tid): 1654 r.append('Having a group in group title is not supported.') 1655 return r 1656 # Insert task at to_pos 1657 if to_pos > at_pos: 1658 # +1 because at_pos reference will be deleted and will shift to_pos reference 1659 to_pos += 1 1660 tasks = tasks[:to_pos] + [orderid_and_tid] + tasks[to_pos:] 1661 # Delete task at at_pos 1662 del tasks[at_pos] 1663 else: 1664 # rename group title, when to_pos in 0 1665 if (to_pos == 0) and (not group == 'root'): 1666 to_pos_tid = tasks[to_pos][ORDER_ID_LENGTH:] 1667 1668 tasks = tasks[:to_pos] + [orderid_and_tid] + tasks[to_pos:] 1669 # Delete task at at_pos+1 because the new position is before at_pos 1670 # at_pos was shifted when to_pos reference was added above 1671 del tasks[at_pos+1] 1672 1673 #:define reorder_tasks 1674 # Move tasks 1675 for n,t in enumerate(tasks): 1676 os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:])) 1677 #:end 1678 1679 # rename group title, when to_pos in 0 1680 if (to_pos == 0) and (not group == 'root'): 1681 # rename group in parent group 1682 # group is tid for parent group and to_pos_tid is equal to group input parameter because to_pos is 0 1683 group = find_group_containing_task(group) 1684 # to_pos_orderid_and_tid is group in parent group 1685 parent_group_tasks = os.listdir(generate_group_path(group)) 1686 for t in parent_group_tasks: 1687 if t[ORDER_ID_LENGTH:] == to_pos_tid: 1688 to_pos_orderid_and_tid = t 1689 # remove order_id, keep task id only for new group 1690 group_id = orderid_and_tid[ORDER_ID_LENGTH:] 1691 # create an entry in parent group at the same position as to_pos tid had 1692 order_id = to_pos_orderid_and_tid[:ORDER_ID_LENGTH] 1693 f = open('%s/%s%s'%(generate_group_path(group),order_id,group_id),'w') 1694 f.close() 1695 1696 # delete group task of group 1697 # delete orderidtaskid 1698 os.remove('%s/%s'%(generate_group_path(group),to_pos_orderid_and_tid)) 1699 1700 # rename group in groups folder 1701 os.rename(generate_group_path(to_pos_tid),generate_group_path(group_id)) 1702 # rename group in tree folder 1703 group_tree_path = find_group_in_tree(to_pos_tid) 1704 group_tree_path_l = group_tree_path.split('/') 1705 group_tree_path_l[-1] = group_id 1706 new_group_tree_path = '/'.join(group_tree_path_l) 1707 os.rename(group_tree_path,new_group_tree_path) 1708 1709 # rename group in linked task and former linked task with a 'groups' folder in tasks database folder 1710 group_tasks = os.listdir(generate_group_path(group_id)) 1711 for group_task in group_tasks: 1712 if os.path.exists('%s/groups' % generate_task_path(group_task[ORDER_ID_LENGTH:])): 1713 # search for to_pos_tid (group input parameter) in task path groups folder 1714 link_groups = os.listdir('%s/groups' % generate_task_path(group_task[ORDER_ID_LENGTH:])) 1715 for lg in link_groups: 1716 if to_pos_tid == lg: 1717 # rename group to new group id since the title changed 1718 os.rename('%s/groups/%s' % (generate_task_path(group_task[ORDER_ID_LENGTH:]),to_pos_tid), '%s/groups/%s' % (generate_task_path(group_task[ORDER_ID_LENGTH:]),group_id)) 1719 1720 edi_log('changed task order %s to %s in group %s'%(at_pos, to_pos, group_param)) 1721 return r 1722 1723 1724 ## move task to group 1725 # @return tid 1726 # @param[in] tgroup task id, select a task in this group 1727 # @param[in] tid task id 1728 # @param[in] group task id, destination group 1729 # @ingroup EDI_CORE 1730 def move_task_to_a_group(tgroup,tid,group): 1731 1732 # find task tid in tgroup and remove task 1733 path = generate_group_path(tgroup) 1734 tasks = sorted(os.listdir(path)) 1735 task_in_group = '' 1736 for n,t in enumerate(tasks): 1737 # remove task from tasks to reorder tgroup 1738 if t[ORDER_ID_LENGTH:] == tid: 1739 task_in_group = t 1740 del tasks[n] 1741 break 1742 if not task_in_group: 1743 return '%s not found in %s'%(tid,tgroup) 1744 1745 # Move group in tree 1746 if is_this_task_a_group(tid): 1747 if find_group_in_tree(tid)[:-ID_LENGTH-1] == find_group_in_tree(group): 1748 # prevent moving a group on itself, in tree and moving group title position to parent group 1749 return tid 1750 shutil.move(find_group_in_tree(tid),find_group_in_tree(group)) 1751 1752 # Remove task in source group 1753 os.remove('%s/%s'%(path,task_in_group)) 1754 1755 #:reorder_tasks 1756 # Move tasks 1757 for n,t in enumerate(tasks): 1758 os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:])) 1759 # check that tid is not already linked in destination group 1760 if is_linked(tid): 1761 link_groups = os.listdir('%s/groups/' % generate_task_path(tid)) 1762 if group in link_groups: 1763 # removed task reference from tgroup. Remove tgroup reference in task. There is already a tid reference in group, nothing more to do 1764 os.remove('%s/groups/%s' % (generate_task_path(tid), tgroup)) 1765 return tid 1766 1767 # Create reference in destination group 1768 add_task_to_group_folder(tid, group) 1769 1770 # linked tasks, remove source group and add destination group 1771 if is_linked(tid): 1772 # remove source tgroup from tid/groups 1773 os.remove('%s/groups/%s' % (generate_task_path(tid), tgroup)) 1774 f = open('%s/groups/%s'%(generate_task_path(tid), group),'w') 1775 f.close() 1776 1777 edi_log('moved %s in group %s to group %s'%(tid,tgroup,group)) 1778 return tid 1779 1780 ## copy task to group with new tid 1781 # @return new tid 1782 # @param[in] tid task id 1783 # @param[in] group task id, destination group 1784 # @ingroup EDI_CORE 1785 def copy_task_to_a_group(tid,group): 1786 # Generate new tid 1787 newtid = generate_id() 1788 # Copy task in tasks 1789 shutil.copytree(generate_task_path(tid),generate_task_path(newtid)) 1790 # delete tid/groups because new task is not linked 1791 task_linked_groups_path = '%s/groups/' % generate_task_path(newtid) 1792 if os.path.exists(task_linked_groups_path): 1793 shutil.rmtree(task_linked_groups_path) 1794 # Copy group in groups and in tree 1795 if is_this_task_a_group(tid): 1796 # create new group 1797 shutil.copytree(generate_group_path(tid),generate_group_path(newtid)) 1798 # Change group title task to newtid 1799 shutil.move('%s/%s%s'%(generate_group_path(newtid),baseconvert(0),tid),'%s/%s%s'%(generate_group_path(newtid),baseconvert(0),newtid)) 1800 1801 # delete tasks to be recreated with new tid 1802 tasks = sorted(os.listdir(generate_group_path(newtid))) 1803 del tasks[0] 1804 for t in tasks: 1805 os.remove('%s/%s'%(generate_group_path(newtid),t)) 1806 1807 # Add group in tree 1808 if group == 'root': 1809 os.mkdir('%s/%s'%(data_location_tree,newtid)) 1810 else: 1811 os.mkdir('%s/%s'%(find_group_in_tree(group),newtid)) 1812 1813 1814 # Add reference in group 1815 tmp_tid = tid 1816 tid = newtid 1817 # Create reference in destination group 1818 add_task_to_group_folder(tid, group) 1819 tid = tmp_tid 1820 1821 if is_this_task_a_group(tid): 1822 # walk in group 1823 # list items in group 1824 tasks = sorted(os.listdir(generate_group_path(tid))) 1825 1826 # add group found in first group 1827 for t in tasks: 1828 # Check tasks that are not title task in a group 1829 if t[ORDER_ID_LENGTH:] != tid: 1830 # copy_task_to_a_group recursively 1831 copy_task_to_a_group(t[ORDER_ID_LENGTH:],newtid) 1832 edi_log('copied %s to group %s, created %s'%(tid,group,newtid)) 1833 return newtid 1834 1835 ## create task path in database using database name 1836 # @return path to task in tasks 1837 # @param[in] tid task id 1838 # @param[in] location database name in data section of easydoneit.ini 1839 # @ingroup EDI_CORE 1840 def generate_task_path_in_database(tid,location): 1841 global selected 1842 global selected_path 1843 1844 z = dict(zip(selected, selected_path)) 1845 location_tasks = '%s/tasks'%z[location] 1846 return '%s/%s'%(location_tasks,tid) 1847 1848 ## create group path in database using database name 1849 # @return path to group in groups 1850 # @param[in] tid task id 1851 # @param[in] location database name in data section of easydoneit.ini 1852 # @ingroup EDI_CORE 1853 def generate_group_path_in_database(tid,location): 1854 global selected 1855 global selected_path 1856 1857 z = dict(zip(selected, selected_path)) 1858 location_groups = '%s/groups'%z[location] 1859 return '%s/%s/'%(location_groups,tid) 1860 1861 ## find group in tree folder in database 1862 # @return path to group in tree 1863 # @param[in] group task id 1864 # @param[in] location database name in data section of easydoneit.ini 1865 # @ingroup EDI_CORE 1866 def find_group_in_tree_in_database(group,location): 1867 global selected 1868 global selected_path 1869 1870 z = dict(zip(selected, selected_path)) 1871 location_tree = '%s/tree'%z[location] 1872 1873 if group == 'root': 1874 path_in_tree = location_tree 1875 else: 1876 path_in_tree = '' 1877 # list all group paths in tree 1878 f = os.popen('find %s'%location_tree) 1879 groups = [i.strip() for i in f.readlines()] 1880 f.close() 1881 # remove data_location_tree from paths 1882 del groups[0] 1883 # find the group in group paths 1884 for g in groups: 1885 if g.split('/')[-1] == group: 1886 path_in_tree = g 1887 return path_in_tree 1888 1889 ## add task with new tid in selected database 1890 # @param[in] tid task id 1891 # @param[in] group task id 1892 # @param[in] location database name in data section of easydoneit.ini 1893 # @ingroup EDI_CORE 1894 def add_task_to_group_folder_in_database(tid,group,location): 1895 # Create an entry in group 1896 tasks = sorted(os.listdir(generate_group_path_in_database(group,location))) 1897 # Add +1 to last order_id to have the task last in the list 1898 if tasks: 1899 order_id = baseconvert(baseconvert_to_dec(tasks[-1][:ORDER_ID_LENGTH])+1) 1900 else: 1901 # start at 0 when group is empty 1902 order_id = baseconvert(0) 1903 f = open('%s/%s%s'%(generate_group_path_in_database(group,location),order_id,tid),'w') 1904 f.close() 1905 1906 ## copy task to group in selected database with new tid 1907 # @return new tid 1908 # @param[in] tid task id 1909 # @param[in] group task id 1910 # @param[in] location database name in data section of easydoneit.ini 1911 # @ingroup EDI_CORE 1912 def copy_task_to_database(tid,location,group): 1913 # Generate new tid 1914 newtid = generate_id() 1915 # Copy task in tasks 1916 shutil.copytree(generate_task_path(tid),generate_task_path_in_database(newtid,location)) 1917 # delete tid/groups because new task is not linked 1918 task_linked_groups_path = '%s/groups/' % generate_task_path_in_database(newtid,location) 1919 if os.path.exists(task_linked_groups_path): 1920 shutil.rmtree(task_linked_groups_path) 1921 # Copy group in groups and in tree 1922 if is_this_task_a_group(tid): 1923 # create new group 1924 shutil.copytree(generate_group_path(tid),generate_group_path_in_database(newtid,location)) 1925 # Change group title task to newtid 1926 shutil.move('%s/%s%s'%(generate_group_path_in_database(newtid,location),baseconvert(0),tid),'%s/%s%s'%(generate_group_path_in_database(newtid,location),baseconvert(0),newtid)) 1927 1928 # delete tasks to be recreated with new tid 1929 tasks = sorted(os.listdir(generate_group_path_in_database(newtid,location))) 1930 del tasks[0] 1931 for t in tasks: 1932 os.remove('%s/%s'%(generate_group_path_in_database(newtid,location),t)) 1933 1934 # Add group in tree 1935 global selected 1936 global selected_path 1937 1938 z = dict(zip(selected, selected_path)) 1939 location_tree = '%s/tree'%z[location] 1940 if group == 'root': 1941 os.mkdir('%s/%s'%(location_tree,newtid)) 1942 else: 1943 os.mkdir('%s/%s'%(find_group_in_tree_in_database(group,location),newtid)) 1944 1945 1946 # Add reference in group 1947 # Create reference in destination group 1948 add_task_to_group_folder_in_database(newtid, group, location) 1949 1950 if is_this_task_a_group(tid): 1951 # walk in group 1952 # list items in group 1953 tasks = sorted(os.listdir(generate_group_path(tid))) 1954 1955 # add group found in first group 1956 for t in tasks: 1957 # Check tasks that are not title task in a group 1958 if t[ORDER_ID_LENGTH:] != tid: 1959 # copy_task_to_database recursively 1960 copy_task_to_database(t[ORDER_ID_LENGTH:],location,newtid) 1961 return newtid 1962 1963 ## move task to database/group 1964 # @param[in] tgroup group id for task tid 1965 # @param[in] tid task id 1966 # @param[in] location database name in data section of easydoneit.ini 1967 # @param[in] group destination group id 1968 # @ingroup EDI_CORE 1969 # copy and delete 1970 def move_task_to_a_group_to_database(tgroup,tid,location,group): 1971 newtid = copy_task_to_database(tid,location,group) 1972 if is_this_task_a_group(tid): 1973 delete_group(tid) 1974 else: 1975 delete_linked_task(tgroup,tid) 1976 return newtid 1977 1978 ## add reference to tid in group 1979 # @param[in] tid task id 1980 # @param[in] group destination group id 1981 # @ingroup EDI_CORE 1982 # Used in edi ln to link tasks 1983 def add_task_reference_to_a_group(tid,group): 1984 prints = [] 1985 if is_this_task_a_group(tid): 1986 prints.append('Select a task to link instead of a group') 1987 else: 1988 # add group to task folder 1989 task_path = generate_task_path(tid) 1990 if not os.path.exists('%s/groups/' % task_path): 1991 os.mkdir('%s/groups/' % task_path) 1992 1993 # add first group when task is linked to tid/groups/ 1994 first_group = find_group_containing_task(tid) 1995 f = open('%s/groups/%s'%(task_path,first_group),'w') 1996 f.close() 1997 f = open('%s/groups/%s'%(task_path,group),'w') 1998 f.close() 1999 2000 # add task to group 2001 add_task_to_group_folder(tid, group) 2002 2003 edi_log('linked %s to group %s'%(tid,group)) 2004 return prints 2005 2006 ## set status for tid 2007 # @param[in] tid task id 2008 # @param[in] status_number index in edi_core.TASK_STATUS 2009 # @ingroup EDI_CORE 2010 def set_status(tid,status_number): 2011 # Change status 2012 f = open('%s/status'%generate_task_path(tid),'w') 2013 f.write(TASK_STATUS[status_number]) 2014 f.close() 2015 edi_log('set status for %s to %s'%(tid,TASK_STATUS[status_number].strip())) 2016 2017 ## set all tasks in group to active 2018 # @param[in] tid task id 2019 # @ingroup EDI_CORE 2020 def reset_group_status(tid): 2021 group_tasks = os.listdir(generate_group_path(tid)) 2022 for t in group_tasks: 2023 if (not is_this_task_a_group(t[ORDER_ID_LENGTH:])) or (is_this_task_a_group(t[ORDER_ID_LENGTH:]) and (get_status(t[ORDER_ID_LENGTH:]) != TASK_STATUS[TASK_STATUS_VOID])): 2024 set_status(t[ORDER_ID_LENGTH:],TASK_STATUS_ACTIVE) 2025 edi_log('reset group %s'%tid) 2026 2027 ## search string in tasks folder 2028 # @param[in] search query string 2029 # @ingroup EDI_CORE 2030 def search_string(search): 2031 global data_location_tasks 2032 global status_filters_d 2033 global STATUS_FILTER_STATES 2034 2035 tasks = sorted(os.listdir(data_location_tasks)) 2036 2037 # search in descriptions only 2038 all_grep_r = [] 2039 for tid in tasks: 2040 if tid != 'root': 2041 # search in visible tasks only (filter enable status) 2042 task_status = get_status(tid) 2043 if status_filters_d[task_status] == STATUS_FILTER_STATES[0]: 2044 f = os.popen('grep -i -R "%s" %s/description.txt'%(search,generate_task_path(tid))) 2045 # add tid and filename to results 2046 grep_r = ['/%s/description.txt:%s'%(tid,i) for i in f.readlines()] 2047 f.close() 2048 all_grep_r += grep_r 2049 2050 grep_r = all_grep_r 2051 2052 # r lists all hits 2053 r = [] 2054 for l in grep_r: 2055 # replace filename with tid and title (first line in file) 2056 # read first line in file 2057 l_l = l.split(':') 2058 hit_filename = l_l[0] 2059 f = open('%s%s'%(data_location_tasks,hit_filename)) 2060 title = f.readline().strip() 2061 f.close() 2062 # set tid 2063 tid = hit_filename.split('/')[1] 2064 group = ' ' 2065 if is_this_task_a_group(tid): 2066 group = 'GROUP' 2067 if is_linked(tid): 2068 group = ' LINK' 2069 l_l[0] = '%s %s - %s'%(tid,group,title) 2070 l = ':'.join(l_l) 2071 r.append(l) 2072 2073 return r 2074 2075 ## search string in group 2076 # @param[in] group task id 2077 # @param[in] search query string 2078 # @ingroup EDI_CORE 2079 def search_string_in_group(group,search): 2080 2081 tasks = sorted(os.listdir(generate_group_path(group))) 2082 2083 # r lists all hits 2084 r = [] 2085 for orderidtid in tasks: 2086 # tid for current task in group 2087 tid = orderidtid[ORDER_ID_LENGTH:] 2088 # search in visible tasks only (filter enable status) 2089 task_status = get_status(tid) 2090 grep_r = [] 2091 if status_filters_d[task_status] == STATUS_FILTER_STATES[0]: 2092 f = os.popen('grep -i -R "%s" %s/description.txt'%(search,generate_task_path(tid))) 2093 # add tid and filename to results 2094 grep_r = ['/%s/description.txt:%s'%(tid,i) for i in f.readlines()] 2095 f.close() 2096 2097 for l in grep_r: 2098 # replace filename with tid and title (first line in file) 2099 # read first line in file 2100 l_l = l.split(':') 2101 hit_filename = l_l[0] 2102 f = open('%s%s'%(data_location_tasks,hit_filename)) 2103 title = f.readline().strip() 2104 f.close() 2105 # set tid 2106 tid = hit_filename.split('/')[1] 2107 group = ' ' 2108 if is_this_task_a_group(tid): 2109 group = 'GROUP' 2110 if is_linked(tid): 2111 group = ' LINK' 2112 l_l[0] = '%s %s - %s'%(tid,group,title) 2113 l = ':'.join(l_l) 2114 r.append(l) 2115 2116 return r 2117 2118 ## search string in tree 2119 # @param[in] group task id 2120 # @param[in] search query string 2121 # @ingroup EDI_CORE 2122 def search_string_in_tree(group,search): 2123 # walk_group is the list of groups to visit. FIFO 2124 r = [] 2125 walk_group = [group] 2126 # the while loop goes through all the group that are found 2127 while walk_group: 2128 # list items in first group 2129 tasks = sorted(os.listdir(generate_group_path(walk_group[0]))) 2130 r += search_string_in_group(walk_group[0],search) 2131 2132 # add group found in first group 2133 for t in tasks: 2134 # Check tasks that are not title task in a group 2135 if (t[ORDER_ID_LENGTH:] != walk_group[0]) and is_this_task_a_group(t[ORDER_ID_LENGTH:]): 2136 walk_group.append(t[ORDER_ID_LENGTH:]) 2137 2138 # remove first group to list items in next group 2139 del walk_group[0] 2140 return r 2141 2142 ## show tree 2143 # @ingroup EDI_CORE 2144 # print all trees: group tids and titles 2145 def show_tree(): 2146 r = [] 2147 f = os.popen('cd %s;find .'%data_location_tree) 2148 # remove first empty line 2149 tidtree = f.readlines() [1:] 2150 f.close() 2151 2152 # remove './' from path 2153 tidtree = [i[2:] for i in tidtree] 2154 2155 # print titles path\n group tid path\n\n 2156 for l in tidtree: 2157 # find title for group tids 2158 group_titles_in_path = [] 2159 for g in l.strip().split(os.sep): 2160 group_titles_in_path.append(get_task_title(g)) 2161 if user_interface == 'web': 2162 # convert / to - to be able to create links correctly 2163 group_titles_in_path = [i.replace('/', '-') for i in group_titles_in_path] 2164 # create string title/title... 2165 group_titles_in_path_s = '/'.join(group_titles_in_path) 2166 r.append('%s\n'%group_titles_in_path_s) 2167 r.append('%s\n'%l) 2168 2169 return r 2170 2171 ## group statistics 2172 # @ingroup EDI_CORE 2173 # print statistics for a group recursively 2174 def group_statistics(group): 2175 global stats 2176 global stats_total 2177 global stats_creation_dates 2178 global stats_overtime 2179 2180 # compute total number of tasks in group 2181 path = generate_group_path(group) 2182 2183 tasks = [] 2184 # remove group title 2185 for i in os.listdir(path): 2186 if not baseconvert(0) in i[:ORDER_ID_LENGTH]: 2187 tasks.append(i[ORDER_ID_LENGTH:]) 2188 2189 stats_total += len(tasks) 2190 2191 # compute number of tasks in each state, groups and links 2192 for tid in tasks: 2193 task_status = get_status(tid) 2194 stats[task_status] += 1 2195 if is_this_task_a_group(tid): 2196 stats[' Group'] += 1 2197 group_statistics(tid) 2198 if is_linked(tid): 2199 stats[' Linked'] += 1 2200 2201 #stats_creation_dates 2202 # Figure out creation time and modification time for description 2203 task_ctime = get_creation_date(tid)[1] 2204 2205 task_path = generate_task_path(tid) 2206 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/description.txt'%task_path) 2207 # 'if' below to be compatible with first database format 2208 if task_ctime == 0: 2209 # ctime not available 2210 task_ctime = mtime 2211 stats_creation_dates.append(task_ctime) 2212 2213 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/status'%task_path) 2214 2215 cdate = time.strftime("%Y-%m-%d", time.localtime(task_ctime)) 2216 sdate = time.strftime("%Y-%m-%d", time.localtime(mtime)) 2217 if not stats_overtime.has_key(cdate): 2218 # initialize date dict 2219 stats_overtime[cdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))])) 2220 stats_overtime[cdate]['Creation'] += 1 2221 if not stats_overtime.has_key(sdate): 2222 # initialize date dict 2223 stats_overtime[sdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))])) 2224 stats_overtime[sdate][task_status] += 1 2225 #end 2226 2227 ## statistics 2228 # @ingroup EDI_CORE 2229 # print statistics for a group or a database 2230 # compute speed 2231 def statistics(group): 2232 global stats 2233 global stats_total 2234 global stats_creation_dates 2235 global stats_overtime 2236 r = [] 2237 2238 # initialize stats dictionary 2239 stat_keys = [TASK_STATUS[i] for i in range(len(TASK_STATUS))] 2240 stat_keys.append(' Group') 2241 stat_keys.append(' Linked') 2242 state_amounts = [0 for i in range(len(stat_keys))] 2243 stats = dict(zip(stat_keys,state_amounts)) 2244 2245 if group == 'for database': 2246 # compute total number of tasks, excluding root 2247 path = data_location_tasks 2248 tasks = [] 2249 for i in os.listdir(path): 2250 if i != 'root': 2251 tasks.append(i) 2252 2253 # compute number of tasks in each state, groups and links 2254 for tid in tasks: 2255 task_status = get_status(tid) 2256 stats[task_status] += 1 2257 if is_this_task_a_group(tid): 2258 stats[' Group'] += 1 2259 if is_linked(tid): 2260 stats[' Linked'] += 1 2261 2262 #:define stats_creation_dates 2263 # Figure out creation time and modification time for description 2264 task_ctime = get_creation_date(tid)[1] 2265 2266 task_path = generate_task_path(tid) 2267 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/description.txt'%task_path) 2268 if task_ctime == 0: 2269 # ctime not available 2270 task_ctime = mtime 2271 stats_creation_dates.append(task_ctime) 2272 2273 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/status'%task_path) 2274 2275 cdate = time.strftime("%Y-%m-%d", time.localtime(task_ctime)) 2276 sdate = time.strftime("%Y-%m-%d", time.localtime(mtime)) 2277 if not stats_overtime.has_key(cdate): 2278 # initialize date dict 2279 stats_overtime[cdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))])) 2280 stats_overtime[cdate]['Creation'] += 1 2281 if not stats_overtime.has_key(sdate): 2282 # initialize date dict 2283 stats_overtime[sdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))])) 2284 stats_overtime[sdate][task_status] += 1 2285 #:end 2286 2287 stats_total = len(tasks) 2288 else: 2289 # compute total number of tasks in group 2290 path = generate_group_path(group) 2291 2292 tasks = [] 2293 for i in os.listdir(path): 2294 if group == 'root': 2295 tasks.append(i[ORDER_ID_LENGTH:]) 2296 else: 2297 # the group task is not counted in the statistics 2298 if not baseconvert(0) in i[:ORDER_ID_LENGTH]: 2299 tasks.append(i[ORDER_ID_LENGTH:]) 2300 2301 stats_total = len(tasks) 2302 2303 # compute number of tasks in each state, groups and links, recursively 2304 for tid in tasks: 2305 task_status = get_status(tid) 2306 stats[task_status] += 1 2307 if is_this_task_a_group(tid): 2308 stats[' Group'] += 1 2309 group_statistics(tid) 2310 if is_linked(tid): 2311 stats[' Linked'] += 1 2312 2313 #:stats_creation_dates 2314 # Figure out creation time and modification time for description 2315 task_ctime = get_creation_date(tid)[1] 2316 2317 task_path = generate_task_path(tid) 2318 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/description.txt'%task_path) 2319 if task_ctime == 0: 2320 # ctime not available 2321 task_ctime = mtime 2322 stats_creation_dates.append(task_ctime) 2323 2324 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/status'%task_path) 2325 2326 cdate = time.strftime("%Y-%m-%d", time.localtime(task_ctime)) 2327 sdate = time.strftime("%Y-%m-%d", time.localtime(mtime)) 2328 if not stats_overtime.has_key(cdate): 2329 # initialize date dict 2330 stats_overtime[cdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))])) 2331 stats_overtime[cdate]['Creation'] += 1 2332 if not stats_overtime.has_key(sdate): 2333 # initialize date dict 2334 stats_overtime[sdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))])) 2335 stats_overtime[sdate][task_status] += 1 2336 2337 if not stats_total: 2338 r.append('0 task in statistics.') 2339 return r 2340 2341 # csv format, not used for now 2342 #col_names = ['Tasks'] + sorted(stats.keys()) 2343 #col_names = [i strip for i in col_names] 2344 #print ','.join(col_names) 2345 #csv = '%d'%len(tasks) 2346 #for k in sorted(stats.keys()) 2347 # csv += ',%d'%stats[k] 2348 #r append csv 2349 #r append '' 2350 2351 r.append('Number of items: %d\n'%stats_total) 2352 for k in sorted(stats.keys()): 2353 r.append('%s - %d'%(k, stats[k])) 2354 r.append('') 2355 2356 # speed 2357 start_time = sorted(stats_creation_dates)[0] 2358 now = time.time() 2359 done_and_inactive = stats[TASK_STATUS[TASK_STATUS_DONE]] + stats[TASK_STATUS[TASK_STATUS_INACTIVE]] 2360 if not done_and_inactive: 2361 r.append('Nothing is done or inactive') 2362 else: 2363 # subtract tasks in state void, because tasks in void state are informative or groups 2364 number_of_tasks = stats_total - stats[TASK_STATUS[TASK_STATUS_VOID]] 2365 remaining_tasks = number_of_tasks - done_and_inactive 2366 remaining_time = (now - start_time) / float(done_and_inactive) * remaining_tasks 2367 r.append('Number of tasks (excluding voids): %d'%number_of_tasks) 2368 r.append('Remaining tasks: %d'%remaining_tasks) 2369 r.append('Remaining days: %.3f'%(remaining_time/86400)) 2370 r.append('Start date: %s'%time.strftime("%Y-%m-%d", time.localtime(start_time))) 2371 r.append("Today's date: %s"%time.strftime("%Y-%m-%d", time.localtime(now))) 2372 r.append('Finish date: %s'%time.strftime("%Y-%m-%d", time.localtime(now + remaining_time))) 2373 2374 # create csv stats from stats_overtime 2375 f = open('%s/stats.csv'%data_location,'w') 2376 f.write('date,%s\n'%','.join([i.strip() for i in STATS_OVERTIME_KEYS])) 2377 2378 for d in sorted(stats_overtime.keys()): 2379 f.write('%s,%s\n'%(d, ','.join([str(stats_overtime[d][i]) for i in STATS_OVERTIME_KEYS]))) 2380 f.close() 2381 2382 # create creation, done and inactive stats 2383 f = open('%s/stats_creation_done_inactive.csv'%data_location,'w') 2384 f.write('date,Creation,Done/Inactive\n') 2385 2386 for d in sorted(stats_overtime.keys()): 2387 f.write('%s,%d,%d\n'%(d, stats_overtime[d]['Creation'], stats_overtime[d][TASK_STATUS[TASK_STATUS_DONE]] + stats_overtime[d][TASK_STATUS[TASK_STATUS_INACTIVE]])) 2388 f.close() 2389 2390 r.append('') 2391 return r 2392 2393 # test core functions 2394 #def test 2395 # # test 2396 # list_group('root') 2397 # 2398 # t1 = add_task('root', 'task1.txt') 2399 # add_task('root', 'task2.txt') 2400 # g3 = add_task('root', 'task3.txt') 2401 # 2402 # create_group(g3) 2403 # t4 = add_task(g3, 'task4.txt') 2404 # 2405 # list_group('root') 2406 # list_group(g3) 2407 # 2408 # #display_task(t1) 2409 # #display_task(t4) 2410 # 2411 # # Delete task in a group of 2 tasks 2412 # print '# Delete task in a group of 2 tasks' 2413 # delete_task(t4) 2414 # 2415 # list_group('root') 2416 # 2417 # create_group(g3) 2418 # t4 = add_task(g3, 'task4.txt') 2419 # 2420 # list_group('root') 2421 # 2422 # # Delete group first task 2423 # print '# Delete group first task' 2424 # delete_task(g3) 2425 # 2426 # list_group('root') 2427 # 2428 # # Delete group task of a group with more than 2 tasks 2429 # print '# Delete group task of a group with more than 2 tasks' 2430 # delete_task(t4) 2431 # 2432 # g3 = add_task('root', 'task3.txt') 2433 # create_group(g3) 2434 # t4 = add_task(g3, 'task4.txt') 2435 # t2 = add_task(g3, 'task2.txt') 2436 # 2437 # list_group('root') 2438 # list_group(g3) 2439 # new_group_id = delete_task(g3) 2440 # print 'New group id %s'%new_group_id 2441 # 2442 # list_group('root') 2443 # list_group(new_group_id) 2444 # 2445 # # Delete group 2446 # print '# Delete group' 2447 # delete_group(new_group_id) 2448 # 2449 # list_group('root') 2450 # 2451 # print baseconvert(100) 2452 # print baseconvert_to_dec(baseconvert(100)) 2453 # print 2454 # 2455 # #edit_task(t1) 2456 # 2457 # # Change order 2458 # print '# Change order' 2459 # g3 = add_task('root', 'task3.txt') 2460 # change_task_order('root',1,0) 2461 # change_task_order('root',0,2) 2462 # 2463 # list_group('root') 2464 # 2465 # # Change status 2466 # print '# Change status' 2467 # create_group(g3) 2468 # t4 = add_task(g3, 'task4.txt') 2469 # t2 = add_task(g3, 'task2.txt') 2470 # 2471 # list_group('root') 2472 # 2473 # set_status(t1,TASK_STATUS_DONE) 2474 # set_status(t4,TASK_STATUS_DONE) 2475 # 2476 # list_group('root') 2477 # list_group(g3) 2478 # 2479 # # Reset status 2480 # 2481 # reset_group_status(g3) 2482 # 2483 # list_group('root') 2484 # list_group(g3) 2485 # 2486 # reset_group_status('root') 2487 # 2488 # list_group('root') 2489 # list_group(g3) 2490 # 2491 # # Create a group in group 2492 # print '# Create a group in group' 2493 # create_group(t4) 2494 # t2 = add_task(t4,'task2.txt') 2495 # 2496 # list_group('root') 2497 # list_group(g3) 2498 # list_group(t4) 2499 # 2500 # # Delete task in a group of 2 tasks not in root 2501 # print '# Delete task in a group of 2 tasks not in root' 2502 # delete_task(t2) 2503 # 2504 # list_group('root') 2505 # list_group(g3) 2506 # 2507 # # Delete group with groups in it 2508 # print '# Delete group with groups in it' 2509 # create_group(t4) 2510 # t2 = add_task(t4,'task2.txt') 2511 # 2512 # list_group('root') 2513 # list_group(g3) 2514 # list_group(t4) 2515 # 2516 # delete_group(g3) 2517 # 2518 # list_group('root') 2519 # 2520 # # Search string 2521 # print '# Search string' 2522 # 2523 # search_string('AND') 2524 # 2525 # # List tree 2526 # print '# List tree' 2527 # g3 = add_task('root', 'task3.txt') 2528 # create_group(g3) 2529 # t4 = add_task(g3, 'task4.txt') 2530 # t2 = add_task(g3, 'task2.txt') 2531 # create_group(t4) 2532 # t2 = add_task(t4,'task2.txt') 2533 # 2534 # list_tree('root') 2535 # 2536 # # Show group for a task 2537 # print '# Show group for a task' 2538 # 2539 # print 'Show group for %s - %s' %(t2,get_task_title(t2)) 2540 # show_group_for_task(t2) 2541 # print 'Show group for %s - %s' %(t4,get_task_title(t4)) 2542 # show_group_for_task(t4) 2543 # print 'Show group for %s - %s' %(t1,get_task_title(t1)) 2544 # show_group_for_task(t1) 2545 2546 # create task 2547 #create_task(t4) 2548 #list_group(t4) 2549 2550 ## start - always called at startup. 2551 # @ingroup EDI_CORE 2552 # @param[in] interface selects current user interface. cli loads the configuration from user home, web loads the configuration located in edi_web folder. 2553 # creates default .easydoneit.ini<br> 2554 # creates database folders<br> 2555 # loads .easydoneit.ini 2556 def start(interface='cli'): 2557 global user_interface 2558 user_interface = interface 2559 2560 if interface=='web': 2561 # Do not try to access ~/.easydoneit.ini 2562 inipath = '.easydoneit.ini' 2563 else: 2564 inipath = '~/.easydoneit.ini' 2565 # create ~/.easydoneit.ini if it doesnt exist, (not in web interface mode) 2566 if not os.path.isfile(os.path.expanduser('~/.easydoneit.ini')): 2567 f = open(os.path.expanduser('~/.easydoneit.ini'),'w') 2568 f.write('[data]\n') 2569 f.write('location=~/easydoneit_data\n') 2570 f.write('1=~/easydoneit_data\n') 2571 f.write('\n') 2572 f.write('[locations]\n') 2573 f.write('selected=1\n') 2574 f.write('default_add_in=1\n') 2575 f.write('\n') 2576 f.write('[filters]\n') 2577 # enable all status filters 2578 fi = zip(TASK_STATUS,status_filters) 2579 for name,nfilter in fi: 2580 # strip to remove spaces in status strings 2581 f.write("%s=%s\n"%(name.strip(),nfilter)) 2582 f.write('\n') 2583 f.write('[colors]\n') 2584 co = zip(TASK_STATUS,status_fgColors) 2585 for name,color in co: 2586 f.write('%s_fgColor=%d,%d,%d,%d\n' % (name.strip(),color[0],color[1],color[2],color[3])) 2587 co = zip(TASK_STATUS,status_bgColors) 2588 for name,color in co: 2589 f.write('%s_bgColor=%d,%d,%d,%d\n' % (name.strip(),color[0],color[1],color[2],color[3])) 2590 2591 f.write('\n[settings]\n') 2592 f.write('editor=vi\n') 2593 2594 f.close() 2595 # default permissions 600 rw for user no access for group and world 2596 os.chmod(os.path.expanduser('~/.easydoneit.ini'),stat.S_IRUSR|stat.S_IWUSR) 2597 2598 # load config from inipath 2599 config = ConfigParser.ConfigParser() 2600 config.readfp(open(os.path.expanduser(inipath))) 2601 2602 # convert ~ to home path 2603 global data_location 2604 data_location = os.path.expanduser(config.get('data','location')) 2605 2606 # load available databases 2607 global databases 2608 data_section = config.items('data') 2609 # remove location which is a path to a database 2610 DATABASE_NAME = 0 2611 DATABASE_PATH = 1 2612 # clear databases for reentrant testing 2613 databases = [] 2614 for d in data_section: 2615 if d[DATABASE_NAME] != 'location': 2616 databases.append(d) 2617 2618 # load add_top_or_bottom 2619 if 'add_top_or_bottom' in dict(config.items('locations')).keys(): 2620 global add_top_or_bottom 2621 add_top_or_bottom = config.get('locations','add_top_or_bottom') 2622 2623 # load selected database paths 2624 global selected 2625 global default_add_in 2626 global selected_path 2627 selected = config.get('locations','selected').split(',') 2628 default_add_in = config.get('locations','default_add_in') 2629 for d in selected: 2630 selected_path.append(os.path.expanduser(config.get('data',d))) 2631 2632 # load autolink groups 2633 if 'autolink' in dict(config.items('locations')).keys(): 2634 global autolink 2635 autolink_cfg = config.get('locations','autolink') 2636 autolink = [ autolink_cfg[i:i+ID_LENGTH] for i in range(0,len(autolink_cfg), ID_LENGTH+1)] 2637 2638 # load list groups 2639 if 'list' in dict(config.items('locations')).keys(): 2640 global list_of_groups 2641 list_cfg = config.get('locations','list') 2642 list_of_groups = [ list_cfg[i:i+ID_LENGTH] for i in range(0,len(list_cfg), ID_LENGTH+1)] 2643 2644 # load status filters and status colors 2645 config_filter_names = dict(config.items('filters')).keys() 2646 config_color_names = dict(config.items('colors')).keys() 2647 for n,nfilter in enumerate(TASK_STATUS): 2648 # strip to remove spaces in status strings 2649 # check that task_status filter is in config file, to avoid problems between config file versions 2650 if nfilter.strip().lower() in config_filter_names: 2651 status_filters[n] = config.get('filters',nfilter.strip()) 2652 if '%s_fgcolor'%nfilter.strip().lower() in config_color_names: 2653 status_fgColors[n] = tuple([int(i) for i in config.get('colors','%s_fgcolor'%nfilter.strip()).split(',')]) 2654 if '%s_bgcolor'%nfilter.strip().lower() in config_color_names: 2655 status_bgColors[n] = tuple([int(i) for i in config.get('colors','%s_bgcolor'%nfilter.strip()).split(',')]) 2656 2657 # Set text editor 2658 global editor 2659 editor = config.get('settings','editor') 2660 2661 # set user name and email 2662 global user 2663 if 'username' in dict(config.items('settings')).keys(): 2664 user = config.get('settings','username') 2665 else: 2666 user = getpass.getuser() 2667 if 'useremail' in dict(config.items('settings')).keys(): 2668 global email 2669 email = config.get('settings','useremail') 2670 2671 # init 2672 init() 2673