commit 12f689dce35e14fbcd66c6e51583844edbe04bed
parent cc4cbb8d15e228008cfc2c32b5870abfb22486c1
Author: Remy Noulin <loader2x@gmail.com>
Date: Mon, 20 Oct 2014 20:56:21 +0200
Version 1.3
Diffstat:
| M | LICENSE | | | 0 | |
| M | README.md | | | 0 | |
| A | VERSION | | | 1 | + |
| A | edi | | | 23 | +++++++++++++++++++++++ |
| A | edi.py | | | 4174 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | edi_common.py | | | 72 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | edi_core.py | | | 2673 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | root.txt | | | 1 | + |
8 files changed, 6944 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
diff --git a/README.md b/README.md
diff --git a/VERSION b/VERSION
@@ -0,0 +1 @@
+v1.3
diff --git a/edi b/edi
@@ -0,0 +1,23 @@
+#! /bin/bash
+# Copyright (C) 2014 Spartatek AB
+#
+# contact@spartatek.se
+# http://spartatek.se
+#
+# EASYDONEIT CLI is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Genereric Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# EASYDONIT CLI is distributed in the hope that it will be usesul,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Genereral Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# GNU licences http://www.gnu.org/licenses
+#
+#
+# call python to avoid permission denied
+python `dirname $0`/edi.py "$@"
diff --git a/edi.py b/edi.py
@@ -0,0 +1,4174 @@
+#! /usr/bin/env python
+# -*- coding: latin-1 -*-
+## @package edi
+# Documentation for edi module. All Easydoneit CLI commands are defined in this module.
+#
+# Copyright (C) 2014 Spartatek AB
+#
+# contact@spartatek.se
+# http://spartatek.se
+#
+# EASYDONEIT CLI is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Genereric Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# EASYDONIT CLI is distributed in the hope that it will be usesul,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Genereral Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# GNU licences http://www.gnu.org/licenses
+
+import sys
+import re
+import shutil
+import os
+import ConfigParser
+# 0 disable, 1 enable print
+#:print_command_map=0
+#:profiler=0
+
+#:profiler
+#:end
+
+from edi_core import *
+# Import to access data_location in edi_core
+import edi_core
+
+## functions to print results from list_tree/list_group
+# @ingroup EDI_CLI
+def empty_line(tid,s):
+ return ''
+
+## functions to print results from list_tree/list_group
+# @ingroup EDI_CLI
+def print_list(task_array):
+ Functions = {'head group': generate_group_string_with_tid,'element': generate_task_string_with_tid}
+ for t in task_array:
+ print Functions.get(t['head'],empty_line)(t['tid'], '%3d - %s %s'%(t['position'],t['group'],t['title']))
+
+#--------------------------------------------
+# COMMAND LINE INTERFACE
+
+## Default Function.
+# @ingroup EDI_CLI
+def Nothing():
+ print 'Command not available'
+
+## add command name to COMMANDS, help to HELP_STRING and corresponding python functions to FUNCTIONS below
+COMMANDS = ['help','version','cr','lsid','add','cat','mkdir','show','rm','vi','order','set','reset','search','mv','cp','ln','path','tree','ls','stfi','data','html','select','default','cpb','mvb','fc','bc','createDatabase','topbot','in','re','st','zip','autolink','cv','user','email','stats','many','demo','list','merge','media','at']
+
+## ls help for both edi lsid and edi ls
+LS_HELP ='''edi lsid [-R|-G|-s|-L|-La|-Lx|-t] [group|task] [position]
+ list tasks in a group. Outside tree folder, without parameter list all tasks and groups,
+ with parameter list group or task title (selected with task id or group id and position),
+ with -R list group recursively, with position print task title.
+ In tree folder, without parameter list group,
+ with -G and when cwd is in tree, list group path with ids and titles (group1/group2...),
+ with task id parameter print tree path and title,
+ -s for sorted list by status,
+ -L list tasks in any group from groups in edi list (or),
+ -La list tasks in all groups in edi list (and),
+ -Lx list tasks in all groups in edi list exclusively (strictly and)
+ -t sort by date from oldest to newest task'''
+
+## help command displays these strings
+HELP_STRINGS = ['''edi help [command]
+ print cli commands and data location, command parameter is optional''',
+ 'print current version',
+ '''edi cr [group]
+ create task and open text editor, calls without parameters create task in root group,
+ when in tree and without parameter, create task in current group,
+ when group parameter is provided create task in group''',
+ LS_HELP,
+ '''edi add [-F|-t] [group] text_filename|text
+ add task from file. Without parameter add task in root group,
+ when in tree and without parameter, add task in current group,
+ add_task option (-F) text_filename adds task with filename in fist line of description in root group,
+ add_task option (-F) group text_filename adds task with filename in first line of description in group,
+ add_task group text_filename adds task in group
+ edi add -t [group] 'text' creates a task with description text''',
+ '''edi cat group|task|position [position]
+ print description. With position parameter print description for task at given position,
+ when in tree and without group parameter, print description at given position in current group''',
+ '''edi mkdir task|position
+ convert task to group
+ when in tree, convert task at given position''',
+ '''edi show task|position
+ print group title for task''',
+ '''edi rm task|group|position [task]
+ delete task or group
+ with group and task parameters, delete task and keep linked tasks
+ edi rm root deletes all tasks in default database''',
+ '''edi vi task|group|position [position]
+ edit description,
+ in tree, edit task or group at given position in current group''',
+ '''edi order [group] at_position to_position
+ change order in group. Without group parameter change order in root group,
+ in tree change order in current group''',
+ '''edi set task|group|position int_status
+ set status to int parameter,
+ int_status values are:
+%s'''%'\n'.join([' %d: %s'%(i,j.strip()) for i,j in enumerate(TASK_STATUS)]),
+ '''edi reset group
+ set tasks in group to active. Without parameter set tasks in root group''',
+ '''edi search string
+ search string in tasks folder,
+ in tree, searches in current group''',
+ '''edi mv group task|group group
+ edi mv position group
+ move task or group,
+ move task or group from group parameter 1 to group parameter 3''',
+ '''edi cp [-E] task|group|position group|path
+ copy task to a group,
+ option -E copy description to path, first line in description is filename''',
+ '''edi ln task|position group
+ add a reference in group''',
+ '''edi path task|group|position
+ print task filesystem path''',
+ '''edi tree
+ print tree path''',
+ LS_HELP.replace('edi lsid ','edi ls ').replace('list tasks in a group','ls groups and tasks with positions only'),
+ '''edi stfi status_filter state
+ enable/disable (set) status filter,
+ without parameters, print current filters,
+
+ status_filter values are:
+%s
+
+ state values are:
+%s'''%('\n'.join([' %s'%i.strip() for i in TASK_STATUS]), '\n'.join([' %s'%i for i in STATUS_FILTER_STATES])),
+ '''edi data [-d] [database_name] [path]
+ set path for database and create database when it doesnt exist,
+ without option, print current data path
+ Option -d delete a database''',
+ '''edi html [-R|-G|-s|-L|-La|-Lx|-t] group|task [position]
+ generate html for tasks, same options as ls''',
+ '''edi select [database_name_1,database_name2...]
+ set selected databases
+ without option, print selected databases''',
+ '''edi default [database_name]
+ set default database for new tasks
+ without option, print default database''',
+ '''edi cpb task|group|position database group
+ copy task or group to database/group''',
+ '''edi mvb group task|group database group
+ edi mvb position database group
+ move task or group to database/group''',
+ '''edi fc task|group|position [color]
+ set/get forground color from task or group
+ color is 4 decimal numbers seperated with commas
+ 0,0,0,255''',
+ '''edi bc task|group|position [color]
+ set/get background color from task or group
+ color is 4 decimal numbers seperated with commas
+ 0,0,0,255''',
+ '''edi createDatabase database_name path
+ create database folders and set path
+ this command creates the path when it doesnt exist''',
+ '''edi topbot [top|bottom]
+ select add new tasks in bottom of group or on top of group
+ Default is bottom
+ without parameter, print status''',
+ '''edi in [-A] group|position
+ print group tasks in md format
+ when in tree, print group at given position
+ option -A create an agenda after title for slideshows''',
+ '''edi re [-A] group|position
+ print group tasks in reStructuredText format
+ when in tree, print group at given position
+ option -A create an agenda after title for slideshows''',
+ '''edi st
+ shows trees for selected databases, print all trees: group tids and titles''',
+ '''edi zip database_name [path]
+ creates an archive of the database in database_name.tar.bz2 in path or current folder''',
+ '''edi autolink [group1,group2...]
+ set configuration for linking new tasks to autolink groups
+ without option, print autolink group list''',
+ '''edi cv group|pos
+ convert empty group to task''',
+ '''edi user [First name Last name]
+ set user name
+ without parameter, display username''',
+ '''edi email [user email]
+ set user email
+ without parameter, display user email''',
+ '''edi stats [group]
+ show statistics
+ without parameter, show statistics in selected databases or current group in tree''',
+ '''edi many [-1|-g] [group] text_file
+ add many tasks from a file, tasks are seperated with '---' lines
+ with -1, add one task per line from a file,
+ with -g, create a group per line from file, one space for deeper levels''',
+ '''edi demo
+ display commands to show how to use Easydoneit CLI''',
+ '''edi list group1,group2...
+ set a list of groups for edi ls -L,-La and -Lx options
+ the groups have to be in the same database
+ without parameter, list groups''',
+ '''edi merge [path|database_name] database_name_2
+ copy tasks from database_name to database_name_2 with no changes
+ WARNING - the source database is merged correctly several times when the tree remains the same since the first merge.''',
+ '''edi media [task|pos] [image file|sound file]
+ copy media file to task. Only one media file per task is allowed, for more attachments use edi at
+ without parameter, show media type''',
+ '''edi at [task|pos] [filename|*]
+ copy file to task as attachments.
+ without parameter, show attachment files''']
+
+HELP = zip(COMMANDS,HELP_STRINGS)
+HELP_DICT = dict(HELP)
+
+## print cli commands and data location
+#
+# print format:<br>
+# command<br>
+# TAB help string
+# @ingroup EDI_CLI
+def help():
+ print 'Easydoneit help'
+ print 'http://spartatek.se/easydoneit_cli'
+ print
+#:define edi_version
+ f = open('%s/VERSION'%os.path.abspath(os.path.dirname(sys.argv[0])))
+ v = f.readline()
+ f.close()
+ print 'Version %s'%v
+#:end
+ print 'Selected data_location'
+ print edi_core.data_location
+ print
+ if len(sys.argv) < 3:
+ # print complete help
+ for c,h in HELP:
+ print c
+ print ' %s\n'%h
+
+ # print help for specified command
+ if len(sys.argv) == 3:
+ if not sys.argv[2] in HELP_DICT.keys():
+ sys.exit('%s is invalid command.'%sys.argv[2])
+ print sys.argv[2]
+ print ' %s\n'%HELP_DICT[sys.argv[2]]
+
+
+## print current version
+# @ingroup EDI_CLI
+def version():
+#:edi_version
+ f = open('%s/VERSION'%os.path.abspath(os.path.dirname(sys.argv[0])))
+ v = f.readline()
+ f.close()
+ print 'Version %s'%v
+
+## create task and open text editor, calls without parameters create task in root group, when group parameter is provided create task in group
+# @ingroup EDI_CLI
+def create_task_cli():
+ # Select group and database
+
+#:define figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+#:end
+
+ # Initialize group in case len(sys.argv) != 2
+ group = ''
+ if len(sys.argv) == 2:
+ # no parameter - just command
+ # select current group when in tree
+ if status == 'in tree':
+ #get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+ #end
+ else:
+ # default group
+ group = 'root'
+
+ if (len(sys.argv) == 2) and (group == 'root') and (not status):
+ # set default database when group is root and cwd not in tree
+#:define set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+#:end
+ if len(sys.argv) >= 3:
+ group = sys.argv[2]
+
+ if (group != 'root') and ((not status) or (len(sys.argv) == 3)):
+ # find group in selected databases and setup data_location when not in tree or group is parameter
+#:define find_group_in_selected_databases_and_setup_data_location
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ groups = os.listdir(edi_core.data_location_groups)
+ if group in groups:
+ break
+#:end
+ # Verify parameter
+ if not is_this_task_a_group(group):
+ sys.exit('%s is not a group.'%group)
+ print create_task(group)
+
+## list tasks in a group. Outside tree folder, without parameter list all tasks and groups, with parameter list group or task title, with -R list group recursively, with position print task title. In tree folder, without parameter list group, with -G list group path with ids and titles, with task id parameter print tree path and title
+# @ingroup EDI_CLI
+def ls_generic():
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+ # Decide what to execute
+ if len(sys.argv) == 2:
+ if status:
+ exe = 'ls in tree'
+ else:
+ exe = 'ls root'
+ else:
+ if (sys.argv[2] == '-R') or (sys.argv[2] == '-G') or (sys.argv[2] == '-s') or (sys.argv[2] == '-t'):
+ if len(sys.argv) == 4:
+ # there is a task id parameter, use that instead of tree location
+ tid = sys.argv[3]
+ status = ''
+ else:
+ tid = 'root'
+ else:
+ tid = sys.argv[2]
+
+ if status:
+ # in tree, set tid to cwd
+ tid = cwd.split('/')[-1]
+
+#:define find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+#:end
+ if is_this_task_a_group(sys.argv[2]):
+ if len(sys.argv) == 3:
+ exe = 'ls group'
+ else:
+ exe = 'ls at position'
+ elif sys.argv[2] == '-R':
+ if (is_this_task_a_group(tid)) or (tid == 'tree'):
+ exe = 'ls tree'
+ # tid is tree when cwd is at root, change to root for ls -R.
+ if tid == 'tree':
+ tid = 'root'
+ else:
+ exe = 'ls task'
+ # replace -R option with tid and run regular ls task
+ sys.argv[2] = tid
+ elif sys.argv[2] == '-s':
+ exe = 'ls sort'
+ # tid is tree when cwd is at root, change to root for ls -s.
+ if tid == 'tree':
+ tid = 'root'
+ elif sys.argv[2] == '-t':
+ exe = 'ls sort by date'
+ # tid is tree when cwd is at root, change to root for ls -t.
+ if tid == 'tree':
+ tid = 'root'
+ elif sys.argv[2] == '-L':
+ exe = 'ls any selected groups'
+ elif sys.argv[2] == '-La':
+ exe = 'ls all in selected groups'
+ elif sys.argv[2] == '-Lx':
+ exe = 'ls all in selected groups exclusively'
+ elif sys.argv[2] == '-G' and status:
+ exe = 'ls groups in tree path'
+ else:
+ exe = 'ls task'
+ if sys.argv[2] == '-G':
+ if len(sys.argv) < 4:
+ exe = 'ls root'
+ else:
+ # replace -G option with tid and run regular ls task
+ sys.argv[2] = sys.argv[3]
+
+ if exe == 'ls in tree':
+ if cwd.split('/')[-1] == 'tree':
+ exe = 'ls group root'
+ else:
+ print_list(list_group(cwd.split('/')[-1]))
+ if exe == 'ls root':
+ # print root in all selected databases
+#:define loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ print '%s - %s' % (d,path)
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+#:end
+ print_list(list_tree('root'))
+ if exe == 'ls group root':
+ print_list(list_group('root'))
+ if exe == 'ls group':
+ group = sys.argv[2]
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:find_group_in_selected_databases_and_setup_data_location
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ groups = os.listdir(edi_core.data_location_groups)
+ if group in groups:
+ break
+ if group == 'root':
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ print '%s - %s' % (d,path)
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ print_list(list_group(group))
+ else:
+ print_list(list_group(group))
+ if exe == 'ls at position':
+ # Find task at given postion
+ group = sys.argv[2]
+ try:
+ position = int(sys.argv[3])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[3])
+#:define find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+#:end
+ # End - Find task at given postion
+ print generate_task_string_with_tid(tid,get_task_title(tid))
+ if exe == 'ls tree':
+ print_list(list_tree(tid))
+ if exe == 'ls groups in tree path':
+ # p is data_location folder/tree
+ p = '/'.join(p_l)
+ # if empty then command is run in tree root
+ if cwd.split(p)[-1]:
+ # print path of tids: tid/tid...
+ print cwd.split(p)[-1][1:]
+ group_titles_in_path = []
+ for g in cwd.split(p)[-1][1:].split('/'):
+ group_titles_in_path.append(get_task_title(g))
+ # print title/title...
+ print '/'.join(group_titles_in_path)
+ if exe == 'ls task':
+ if status:
+ # print group path titles and task
+ # in tree folder
+ p = '/'.join(p_l)
+ # if empty then command is run in tree root
+ if cwd.split(p)[-1]:
+ print cwd.split(p)[-1][1:]
+ group_titles_in_path = []
+ for g in cwd.split(p)[-1][1:].split('/'):
+ group_titles_in_path.append(get_task_title(g))
+ # print title/title...
+ print '/'.join(group_titles_in_path)
+ # Verify that task sys.argv[2] is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ print generate_task_string_with_tid(sys.argv[2],get_task_title(sys.argv[2]))
+ else:
+ #find_task_in_selected_databases_and_setup_data_location
+ tid_in_database = ''
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ tid_in_database = 'yes'
+ break
+ if not tid_in_database:
+ sys.exit('%s not found.'%sys.argv[2])
+ print generate_task_string_with_tid(sys.argv[2],get_task_title(sys.argv[2]))
+
+ if exe == 'ls sort':
+ if not is_this_task_a_group(tid):
+ sys.exit('%s is not a group.'%tid)
+ task_attributes = list_group(tid)
+ sorted_tasks = sort_task_attributes(task_attributes)
+ print_list(sorted_tasks)
+
+ if exe == 'ls sort by date':
+ if not is_this_task_a_group(tid):
+ sys.exit('%s is not a group.'%tid)
+ task_attributes = list_group(tid)
+ sorted_tasks = sort_task_attributes_by_date(task_attributes)
+ print_list(sorted_tasks)
+
+ if exe == 'ls any selected groups':
+ if not edi_core.list_of_groups:
+ sys.exit('List empty')
+
+ tid = edi_core.list_of_groups[0]
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+
+ # check that all groups are available in the database
+ status = 'search groups'
+ for g in edi_core.list_of_groups:
+ if not is_this_task_a_group(g):
+ status = 'group not found, edi cannot list all groups'
+ if status == 'group not found, edi cannot list all groups':
+ sys.exit(status)
+
+ # list tasks in all groups
+ task_attributes = list_group(tid)
+ empty_line = task_attributes[-1]
+ # remove empty lines between groups
+ del task_attributes[-1]
+
+ # add other groups to the list
+ for tid in edi_core.list_of_groups[1:]:
+ # remove head group, keep head group only for first group in the group list
+ # remove empty lines between groups
+ task_attributes += list_group(tid)[1:-1]
+
+ # remove duplicated tasks
+ tasks = []
+ tasks.append(task_attributes[0])
+ # array used to prevent duplicated tasks
+ tids = []
+ for t in task_attributes[1:]:
+ if not t['tid'] in tids:
+ tasks.append(t)
+ tids.append(t['tid'])
+
+ tasks.append(empty_line)
+ print_list(tasks)
+
+
+ if exe == 'ls all in selected groups':
+ if not edi_core.list_of_groups:
+ sys.exit('List empty')
+
+ tid = edi_core.list_of_groups[0]
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+
+ if len(edi_core.list_of_groups) == 1:
+ if not is_this_task_a_group(tid):
+ sys.exit('%s not found, edi cannot list all groups' % tid)
+ # only one group, regular ls
+ print_list(list_group(tid))
+ sys.exit(0)
+
+ # check that all groups are available in the database
+ status = 'search groups'
+ for g in edi_core.list_of_groups:
+ if not is_this_task_a_group(g):
+ status = 'group not found, edi cannot list all groups'
+ if status == 'group not found, edi cannot list all groups':
+ sys.exit(status)
+
+ # list tasks in all groups
+ task_attributes = list_group(tid)
+ empty_line = task_attributes[-1]
+ # remove empty lines between groups
+ del task_attributes[-1]
+
+ # add other groups to the list
+ for tid in edi_core.list_of_groups[1:]:
+ # remove head group, keep head group only for first group in the group list
+ # remove empty lines between groups
+ task_attributes += list_group(tid)[1:-1]
+
+ # keep tasks only in all listed groups
+ tasks = []
+ tasks.append(task_attributes[0])
+ # array used to prevent duplicated tasks
+ tids = []
+ for t in task_attributes[1:]:
+ if is_linked(t['tid']):
+ groups = os.listdir('%s/groups/' % generate_task_path(t['tid']))
+ # check that the task in all listed group
+ status = 'all in groups'
+ for g in edi_core.list_of_groups:
+ if not g in groups:
+ status = 'missing group'
+ if (status == 'all in groups') and (not t['tid'] in tids):
+ tasks.append(t)
+ tids.append(t['tid'])
+
+
+ tasks.append(empty_line)
+ print_list(tasks)
+
+ if exe == 'ls all in selected groups exclusively':
+ if not edi_core.list_of_groups:
+ sys.exit('List empty')
+
+ tid = edi_core.list_of_groups[0]
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+
+ # check that all groups are available in the database
+ status = 'search groups'
+ for g in edi_core.list_of_groups:
+ if not is_this_task_a_group(g):
+ status = 'group not found, edi cannot list all groups'
+ if status == 'group not found, edi cannot list all groups':
+ sys.exit(status)
+
+ # list tasks in all groups
+ task_attributes = list_group(tid)
+ empty_line = task_attributes[-1]
+ # remove empty lines between groups
+ del task_attributes[-1]
+
+ # add other groups to the list
+ for tid in edi_core.list_of_groups[1:]:
+ # remove head group, keep head group only for first group in the group list
+ # remove empty lines between groups
+ task_attributes += list_group(tid)[1:-1]
+
+ # keep tasks only in all listed groups
+ tasks = []
+ tasks.append(task_attributes[0])
+ # array used to prevent duplicated tasks
+ tids = []
+ for t in task_attributes[1:]:
+ if is_linked(t['tid']):
+ groups = os.listdir('%s/groups/' % generate_task_path(t['tid']))
+ # check that the task in all listed group exclusively
+ status = 'all groups in list_of_groups'
+ for g in groups:
+ if not g in edi_core.list_of_groups:
+ status = 'a group is not in the list'
+ if (status == 'all groups in list_of_groups') and (len(groups) == len(edi_core.list_of_groups)) and (not t['tid'] in tids):
+ tasks.append(t)
+ tids.append(t['tid'])
+
+
+ tasks.append(empty_line)
+ print_list(tasks)
+
+
+## list with tids
+# @ingroup EDI_CLI
+def lsid():
+ # list tids and positions
+ edi_core.list_option = 'tids'
+
+ ls_generic()
+
+# ls groups and tasks with positions
+# @ingroup EDI_CLI
+def ls():
+ # list positions only
+ edi_core.list_option = 'positions'
+
+ ls_generic()
+
+## add task from file. Without parameter add task in root group. add_task option (-F) text_filename add task with filename in fist line of description in root group. add_task option (-F) group text_filename add task with filename in first line of description in group. add_task group text_filename add task in group
+# @ingroup EDI_CLI
+def add_task_cli():
+ if len(sys.argv) < 3:
+ # not enough parameters, call create_task
+ create_task_cli()
+ return
+
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+ options = ''
+ group_parameter_status = 'group is a command line parameter'
+ if len(sys.argv) < 4:
+ # Parameters are: command filename
+ # select current group when in tree
+ if status == 'in tree':
+ #get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+ #end
+ else:
+ # default group
+ group = 'root'
+ group_parameter_status = 'edi selected group'
+
+ text_file = sys.argv[2]
+ else:
+ if ((sys.argv[2] == '-F') or (sys.argv[2] == '-t')) and len(sys.argv) == 4:
+ options = sys.argv[2]
+ text_file = sys.argv[3]
+ # select current group when in tree
+ if status == 'in tree':
+ #get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+ #end
+ else:
+ # default group
+ group = 'root'
+ group_parameter_status = 'edi selected group'
+ if ((sys.argv[2] == '-F') or (sys.argv[2] == '-t')) and len(sys.argv) == 5:
+ options = sys.argv[2]
+ group = sys.argv[3]
+ text_file = sys.argv[4]
+ if ((sys.argv[2] != '-F') and (sys.argv[2] != '-t')):
+ group = sys.argv[2]
+ text_file = sys.argv[3]
+ if not options:
+ exe = 'add'
+ else:
+ if options == '-F':
+ # option -F stores text_file filename in first line of description
+ exe = 'add task and filename'
+ elif options == '-t':
+ # option -F stores text_file filename in first line of description
+ exe = 'add text from command line'
+
+ if (group == 'root') and (not status):
+ # set default database when group is root and cwd not in tree
+#:set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ if (group != 'root') and ((not status) or (group_parameter_status == 'group is a command line parameter')):
+#:find_group_in_selected_databases_and_setup_data_location
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ groups = os.listdir(edi_core.data_location_groups)
+ if group in groups:
+ break
+
+ if not is_this_task_a_group(group):
+ sys.exit('%s is not a group.'%group)
+
+ if exe == 'add':
+ if not os.path.exists(text_file):
+ sys.exit('%s not accessible.'%text_file)
+ print add_task(group,text_file)
+ if exe == 'add task and filename':
+ if not os.path.exists(text_file):
+ sys.exit('%s not accessible.'%text_file)
+ print add_task_and_filename(group,text_file)
+ if exe == 'add text from command line':
+ print add_text(group,text_file)
+
+## print description. With position parameter print description for task at given position
+# @ingroup EDI_CLI
+def cat():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ tid = sys.argv[2]
+
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:define find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+#:end
+
+ if status == 'in tree':
+#:define get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:end
+
+ if (len(sys.argv) == 3) and (not status):
+ # group parameter and not in tree
+ tid = sys.argv[2]
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+
+ if (len(sys.argv) == 3) and (status == 'in tree'):
+ # Find task at given postion
+#:define get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:end
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+
+ if len(sys.argv) == 4:
+ group = sys.argv[2]
+ try:
+ position = int(sys.argv[3])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[3])
+ # find_task_in_selected_databases_and_setup_data_location needs tid, here tid = group
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+ if not is_this_task_a_group(group):
+ sys.exit('%s is not a group.'%group)
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not tid in tasks:
+ sys.exit('%s not found.'%tid)
+ print ''.join(display_task(tid))
+
+## convert task to group
+# @ingroup EDI_CLI
+def mkdir():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:define tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+#:end
+
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ # Verify that task is not already a group
+ if is_this_task_a_group(sys.argv[2]):
+ sys.exit('%s is already a group.'%sys.argv[2])
+ print '\n'.join(create_group(sys.argv[2]))
+
+## print group title for task
+# @ingroup EDI_CLI
+def show_group_for_task_cli():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ print '\n'.join(show_group_for_task(sys.argv[2]))
+
+## delete task or group, or delete task in specified group, keep linked tasks
+# @ingroup EDI_CLI
+def rm():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ if len(sys.argv) == 3:
+ if is_this_task_a_group(sys.argv[2]):
+ delete_group(sys.argv[2])
+ else:
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ delete_task(sys.argv[2])
+ if len(sys.argv) == 4:
+ # Verify that task tid is not root
+ if sys.argv[3] == 'root':
+ sys.exit('root is invalid task')
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ # Verify group exists
+ if not is_this_task_a_group(sys.argv[2]):
+ sys.exit('%s is not a group.'%sys.argv[2])
+ if not sys.argv[3] in tasks:
+ sys.exit('%s not found.'%sys.argv[3])
+ # delete task in specified group
+ delete_linked_task(sys.argv[2], sys.argv[3])
+
+## edit description
+# @ingroup EDI_CLI
+def vi():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ tid = sys.argv[2]
+
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+
+ if (len(sys.argv) == 3) and (not status):
+ # group parameter and not in tree
+ tid = sys.argv[2]
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+ if (len(sys.argv) == 3) and (status == 'in tree'):
+ # Find task at given postion
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ if len(sys.argv) == 4:
+ group = sys.argv[2]
+ try:
+ position = int(sys.argv[3])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[3])
+ # find_task_in_selected_databases_and_setup_data_location needs tid, here tid = group
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+ # Verify group exists
+ if not is_this_task_a_group(sys.argv[2]):
+ sys.exit('%s is not a group.'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not tid in tasks:
+ sys.exit('%s not found.'%tid)
+ edit_task(tid)
+
+## change order in group. Without group parameter change order in root group, in tree change order in current group
+# @ingroup EDI_CLI
+def change_order():
+ if len(sys.argv) < 4:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+ else:
+ group = 'root'
+
+
+ if len(sys.argv) < 5:
+ if not status:
+ sys.exit('Missing group parameter.')
+ at = sys.argv[2]
+ to = sys.argv[3]
+ else:
+ group = sys.argv[2]
+ at = sys.argv[3]
+ to = sys.argv[4]
+
+ if group == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ #find_group_in_selected_databases_and_setup_data_location
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ groups = os.listdir(edi_core.data_location_groups)
+ if group in groups:
+ break
+
+ # Verify group exists
+ if not is_this_task_a_group(group):
+ sys.exit('%s is not a group.'%group)
+ # Verify at and to are numbers
+ try:
+ test = int(at)
+ except:
+ sys.exit('%s is invalid position'%at)
+ try:
+ test = int(to)
+ except:
+ sys.exit('%s is invalid position'%to)
+
+ if int(at) < 0:
+ sys.exit('Position %s is invalid.'%at)
+ if int(to) < 0:
+ sys.exit('Position %s is invalid.'%to)
+ print '\n'.join(change_task_order(group,int(at),int(to)))
+
+## set status to int parameter
+# @ingroup EDI_CLI
+def set():
+ if len(sys.argv) < 4:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ # Verify int_status parameter
+ try:
+ test = int(sys.argv[3])
+ except:
+ sys.exit('int_status %s is invalid.'%sys.argv[3])
+ if test >= len(edi_core.TASK_STATUS):
+ sys.exit('int_status %s is too large.'%sys.argv[3])
+ if test < 0:
+ sys.exit('int_status %s is invalid.'%sys.argv[3])
+ set_status(sys.argv[2],int(sys.argv[3]))
+
+## set tasks to active. Without parameter set tasks in root group
+# @ingroup EDI_CLI
+def reset():
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+
+ if (len(sys.argv) == 2) and (status != 'in tree'):
+#:set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ group = 'root'
+ if (len(sys.argv) > 2) and (sys.argv[2] == 'root') and (not status):
+ # set default database when group is root and cwd not in tree
+#:set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ group = 'root'
+ # when group is root and in tree, dont change data_location
+ if (len(sys.argv) > 2) and (sys.argv[2] != 'root'):
+ group = sys.argv[2]
+#:find_group_in_selected_databases_and_setup_data_location
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ groups = os.listdir(edi_core.data_location_groups)
+ if group in groups:
+ break
+
+ if not is_this_task_a_group(group):
+ sys.exit('%s is not a group.'%group)
+ reset_group_status(group)
+
+## search string in tasks folder
+# @ingroup EDI_CLI
+def search():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+ print ''.join(search_string_in_tree(group,sys.argv[2]))
+ else:
+#:loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ print '%s - %s' % (d,path)
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ print ''.join(search_string(sys.argv[2]))
+
+## move task or group
+# @ingroup EDI_CLI
+def move_task():
+ if len(sys.argv) < 4:
+ sys.exit('Too little parameters.')
+ if len(sys.argv) == 5:
+ tid = sys.argv[2]
+#:find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+ # Verify parameters
+ # Verify that task tid is not root
+ if sys.argv[3] == 'root':
+ sys.exit('root is invalid task')
+ if not is_this_task_a_group(sys.argv[2]):
+ sys.exit('%s is not a group.'%sys.argv[2])
+ if not is_this_task_a_group(sys.argv[4]):
+ sys.exit('%s is not a group.'%sys.argv[4])
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[3] in tasks:
+ sys.exit('%s not found.'%sys.argv[3])
+ # Verify that when tid is a group, tid is not parent to destination group (sys.argv[3])
+ if is_this_task_a_group(sys.argv[3]):
+ tree_path = find_group_in_tree(sys.argv[4])
+ parent_path = tree_path.split(sys.argv[4])[0]
+ if sys.argv[3] in parent_path:
+ print tree_path
+ sys.exit('%s is a parent group for %s'%(sys.argv[3],sys.argv[4]))
+ # Verify that group is not move on itself
+ if sys.argv[3] == sys.argv[4]:
+ sys.exit('Moving %s group on itself.'%sys.argv[3])
+ # Verify that source group is not destination group
+ if sys.argv[2] == sys.argv[4]:
+ sys.exit('Source and destination groups are identical %s.'%sys.argv[2])
+ tid = move_task_to_a_group(sys.argv[2],sys.argv[3],sys.argv[4])
+ if len(tid) > edi_core.ID_LENGTH:
+ # Error when the task sys.argv[3] is not found in sys.argv[2]
+ sys.exit(tid)
+ return
+
+ # Always 4 parameters - other parameters are ignored
+ # Parameter 2 is position, find group
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+
+ # Verify parameters
+ if not 'group' in locals():
+ sys.exit('Invalid parameters.')
+ if not is_this_task_a_group(sys.argv[3]):
+ sys.exit('%s is not a group.'%sys.argv[3])
+ # Verify that when tid is a group, tid is not parent to destination group (sys.argv[3])
+ if is_this_task_a_group(tid):
+ tree_path = find_group_in_tree(sys.argv[3])
+ parent_path = tree_path.split(sys.argv[3])[0]
+ if tid in parent_path:
+ print tree_path
+ sys.exit('%s is a parent group for %s'%(tid,sys.argv[3]))
+ # Verify that group is not move on itself
+ if tid == sys.argv[3]:
+ sys.exit('Moving %s group on itself.'%tid)
+ # Verify that source group is not destination group
+ if group == sys.argv[3]:
+ sys.exit('Source and destination groups are identical %s.'%group)
+ move_task_to_a_group(group,tid,sys.argv[3])
+
+## copy task to a group. option -E copy description to path, first line in description is filename
+# @ingroup EDI_CLI
+def copy_task():
+ # Verify parameters
+ if len(sys.argv) < 4:
+ sys.exit('Too little parameters.')
+ # option
+ if sys.argv[2] == '-E':
+ # Verify parameters
+ if len(sys.argv) < 5:
+ sys.exit('Too little parameters.')
+ option = '-E'
+ sys.argv[2] = sys.argv[3]
+ tid = sys.argv[3]
+ else:
+ option = ''
+ tid = sys.argv[2]
+
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+
+ if option == '-E':
+ # Export task to a file
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+ if not os.path.exists(sys.argv[4]):
+ sys.exit('%s is unreachable.'%sys.argv[4])
+
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not tid in tasks:
+ sys.exit('%s not found.'%tid)
+
+ r = export_task_to_a_file(tid,sys.argv[4])
+ print 'Exported task to %s'%r
+ else:
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+ # Verify that task tid is not root
+ if tid == 'root':
+ sys.exit('root is invalid task')
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not tid in tasks:
+ sys.exit('%s not found.'%tid)
+
+ if not is_this_task_a_group(sys.argv[3]):
+ sys.exit('%s is not a group.'%sys.argv[3])
+ if tid == sys.argv[3]:
+ sys.exit('Source and destination are identical.')
+ # Verify that when tid is a group, tid is not parent to destination group (sys.argv[3])
+ if is_this_task_a_group(tid):
+ tree_path = find_group_in_tree(sys.argv[3])
+ parent_path = tree_path.split(sys.argv[3])[0]
+ if tid in parent_path:
+ print tree_path
+ sys.exit('%s is a parent group for %s'%(tid,sys.argv[3]))
+ print copy_task_to_a_group(tid,sys.argv[3])
+
+## add a reference in group
+# @ingroup EDI_CLI
+def link_task():
+ if len(sys.argv) < 4:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ # Verify parameters
+ if not is_this_task_a_group(sys.argv[3]):
+ sys.exit('%s is not a group.'%sys.argv[3])
+ if sys.argv[3] == find_group_containing_task(sys.argv[2]):
+ sys.exit('Task %s is already in group %s.'%(sys.argv[2],sys.argv[3]))
+ add_task_reference_to_a_group(sys.argv[2],sys.argv[3])
+
+## print task filesystem path
+# @ingroup EDI_CLI
+def get_task_path():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ print generate_task_path(sys.argv[2])
+
+## print tree path
+# @ingroup EDI_CLI
+def tree():
+ print edi_core.data_location_tree
+
+ DATABASE_NAME = 0
+ DATABASE_PATH = 1
+ print
+ print 'trees'
+ for d in edi_core.databases:
+ print '%s - %s/tree' % (d[DATABASE_NAME], d[DATABASE_PATH])
+
+## enable/disable (set) status filter
+# @ingroup EDI_CLI
+def set_status_filter():
+ if len(sys.argv)<3:
+ print edi_core.status_filters_d
+ else:
+ if len(sys.argv)<4:
+ sys.exit('Too little parameters.')
+ status_name = sys.argv[2]
+ state = sys.argv[3]
+
+ # verify parameters
+ task_status = [i.strip().lower() for i in edi_core.TASK_STATUS]
+ if not status_name.lower() in task_status:
+ sys.exit('%s is invalid status_filter. Possible status filter are %s.'%(status_name,','.join(task_status)))
+ if not state in edi_core.STATUS_FILTER_STATES:
+ sys.exit('%s is invalid state. Possible states are %s.'%(state,','.join(edi_core.STATUS_FILTER_STATES)))
+
+#:define load_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'))
+ # load config from user home
+ config = ConfigParser.ConfigParser()
+ config.readfp(f)
+ f.close()
+#:end
+ config.set('filters',status_name,state)
+
+#:define save_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ config.write(f)
+ f.close()
+#:end
+
+## set path for database
+# @ingroup EDI_CLI
+def data():
+ if len(sys.argv)<3:
+ print 'Selected data_location'
+ print edi_core.data_location
+ DATABASE_NAME = 0
+ DATABASE_PATH = 1
+ print
+ print 'Available databases'
+ for d in edi_core.databases:
+ print '%s - %s' % (d[DATABASE_NAME], d[DATABASE_PATH])
+ else:
+ if len(sys.argv)<4:
+ sys.exit('Too little parameters.')
+#:load_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'))
+ # load config from user home
+ config = ConfigParser.ConfigParser()
+ config.readfp(f)
+ f.close()
+ if sys.argv[2] == '-d':
+ name = sys.argv[3]
+ try:
+ path = config.get('data',name)
+ except:
+ sys.exit('%s is invalid database name.'%name)
+ if name == 'location':
+ sys.exit('%s is invalid database name.'%name)
+
+ # remove database from selected configuration and check that at least one database is selected
+ selected = config.get('locations','selected')
+ selected_l = selected.split(',')
+ new_selected_l = []
+ for i in selected_l:
+ if not name in i:
+ new_selected_l.append(i)
+ if not new_selected_l:
+ sys.exit('Select more databases before deleting %s'%name)
+ new_selected = ','.join(new_selected_l)
+ config.set('locations','selected',new_selected)
+
+ # delete database folder from file system
+ # delete database from config file
+
+ # delete database folder from file system
+ if os.path.exists(path):
+ shutil.rmtree(path)
+ # delete database from config file - data, selected and default_add_in
+ config.remove_option('data',name)
+
+ default = config.get('locations','default_add_in')
+ if name == default:
+ default = new_selected_l[0]
+ config.set('locations','default_add_in',default)
+
+ #save_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ config.write(f)
+ f.close()
+ #end
+
+ print 'Deleted database %s - %s' % (name,path)
+ print '\nSelected databases'
+ print new_selected
+ print '\nDefault for new tasks'
+ print default
+ return
+
+ name = sys.argv[2]
+ if name == 'location':
+ sys.exit('%s is invalid database name.'%name)
+ path = os.path.expanduser(sys.argv[3])
+
+ config.set('data',name,path)
+#:save_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ config.write(f)
+ f.close()
+ edi_core.data_location = path
+ init()
+
+## generate html for tasks
+# @ingroup EDI_CLI
+def html_cli():
+ # generate html header and content
+ edi_core.list_option = 'html'
+
+ html_header = '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+ <title>Tasks</title>
+ <style type="text/css">
+ .subject {text-align: left}
+ .plannedStartDateTime {text-align: right}
+ .priority {text-align: right}
+ .inactive {color: #5E5E5E}
+ .late {color: #A020F0}
+ .active {color: #000000}
+ .duesoon {color: #FF8000}
+ .overdue {color: #FF0000}
+ .completed {color: #037700}
+
+ body {
+ color: #333;
+ background-color: white;
+ font: 11px verdana, arial, helvetica, sans-serif;
+ }
+
+ /* Styles for the title and table caption */
+ h1, caption {
+ text-align: center;
+ font-size: 18px;
+ font-weight: 900;
+ color: #778;
+ }
+
+ /* Styles for the whole table */
+ #table {
+ border-collapse: collapse;
+ border: 2px solid #ebedff;
+ margin: 10px;
+ padding: 0;
+ }
+
+ /* Styles for the header row */
+ .header {
+ font: bold 12px/14px verdana, arial, helvetica, sans-serif;
+ color: #07a;
+ background-color: #ebedff;
+ }
+
+ /* Mark the column that is sorted on */
+ #sorted {
+ text-decoration: underline;
+ }
+
+ /* Styles for a specific column */
+ .subject {
+ font-weight: bold;
+ }
+
+ /* Styles for regular table cells */
+ td {
+ padding: 5px;
+ border: 2px solid #ebedff;
+ }
+
+ /* Styles for table header cells */
+ th {
+ padding: 5px;
+ border: 2px solid #ebedff;
+ }
+
+ </style>
+ </head>
+ <body>
+ <h1>Tasks</h1>
+ <table border="1" id="table">
+ <thead>
+ <tr class="header">
+ <th class="subject" scope="col">Subject</th>
+ <th class="description" scope="col">Description</th>
+ </tr>
+ </thead>
+ <tbody>'''
+
+ html_footer = ''' </tbody>
+ </table>
+ </body>
+</html>'''
+
+ print html_header
+ ls_generic()
+ print html_footer
+
+## list selected databases or set selected databases
+# @ingroup EDI_CLI
+def selected_cli():
+ if len(sys.argv)>3:
+ sys.exit('Too many parameters.')
+ if len(sys.argv)<3:
+ print 'Selected databases'
+ print ','.join(edi_core.selected)
+ print
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,p in z:
+ print '%s - %s' % (d,p)
+ if len(sys.argv)==3:
+#:load_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'))
+ # load config from user home
+ config = ConfigParser.ConfigParser()
+ config.readfp(f)
+ f.close()
+ # Verify parameter
+ selected = sys.argv[2].split(',')
+ if 'location' in selected:
+ sys.exit('location is invalid database name.')
+ for d in selected:
+ if not d in dict(edi_core.databases).keys():
+ sys.exit('%s is invalid database name.'%d)
+ if not edi_core.default_add_in in selected:
+ # default becomes first selected database
+ print 'Default database %s is not selected'%edi_core.default_add_in
+ print '%s is now default'%sys.argv[2].split(',')[0]
+ config.set('locations','default_add_in',sys.argv[2].split(',')[0])
+ config.set('locations','selected',sys.argv[2])
+#:save_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ config.write(f)
+ f.close()
+
+## set default database
+# @ingroup EDI_CLI
+def default_cli():
+ if len(sys.argv)>3:
+ sys.exit('Too many parameters.')
+ if len(sys.argv)<3:
+ print 'Default for new tasks'
+ print edi_core.default_add_in
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ print z[edi_core.default_add_in]
+ if len(sys.argv)==3:
+ # Verify parameter
+ if not sys.argv[2] in edi_core.selected:
+ sys.exit('%s is not selected'%sys.argv[2])
+#:load_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'))
+ # load config from user home
+ config = ConfigParser.ConfigParser()
+ config.readfp(f)
+ f.close()
+ config.set('locations','default_add_in',sys.argv[2])
+#:save_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ config.write(f)
+ f.close()
+
+
+## copy task or group to database/group
+# @ingroup EDI_CLI
+def cpb_cli():
+ if len(sys.argv) < 5:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify parameters
+ if not sys.argv[3] in edi_core.selected:
+ sys.exit('%s database is not selected.'%sys.argv[3])
+ # Verify that task tid is not root
+ if sys.argv[2] == 'root':
+ sys.exit('root is invalid task')
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ data_location = os.path.expanduser(z[sys.argv[3]])
+ data_location_groups = '%s/groups'%data_location
+ if not os.path.exists('%s/%s'%(data_location_groups,sys.argv[4])):
+ sys.exit('Missing destination group %s'%sys.argv[4])
+ print copy_task_to_database(sys.argv[2],sys.argv[3],sys.argv[4])
+
+## move task or group to database
+# @ingroup EDI_CLI
+# 2 group<br>
+# 3 task|group<br>
+# 4 location<br>
+# 5 group
+def mvb_cli():
+ if len(sys.argv) < 5:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+ database = sys.argv[3]
+ destination_group = sys.argv[4]
+ else:
+ if len(sys.argv) < 6:
+ sys.exit('Too little parameters.')
+ group = sys.argv[2]
+ tid = sys.argv[3]
+ database = sys.argv[4]
+ destination_group = sys.argv[5]
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify parameters
+ if not database in edi_core.selected:
+ sys.exit('%s database is not selected.'%database)
+ # Verify that task tid is not root
+ if tid == 'root':
+ sys.exit('root is invalid task')
+ if not is_this_task_a_group(group):
+ sys.exit('%s is not a group.'%group)
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not tid in tasks:
+ sys.exit('%s not found.'%tid)
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ data_location = os.path.expanduser(z[database])
+ data_location_groups = '%s/groups'%data_location
+ if not os.path.exists('%s/%s'%(data_location_groups,destination_group)):
+ sys.exit('Missing destination group %s'%destination_group)
+ print move_task_to_a_group_to_database(group,tid,database,destination_group)
+
+## set or show foreground color
+# @ingroup EDI_CLI
+def fc_cli():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ if len(sys.argv)<4:
+ color = get_forground_color(sys.argv[2])
+ print '%d,%d,%d,%d' % (color[0],color[1],color[2],color[3])
+ if len(sys.argv)==4:
+ set_forground_color(sys.argv[2],sys.argv[3])
+
+## set or show background color
+# @ingroup EDI_CLI
+def bc_cli():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ if len(sys.argv)<4:
+ color = get_background_color(sys.argv[2])
+ print '%d,%d,%d,%d' % (color[0],color[1],color[2],color[3])
+ if len(sys.argv)==4:
+ set_background_color(sys.argv[2],sys.argv[3])
+
+## Create database folder and set path in .easydoneit.ini
+# @ingroup EDI_CLI
+def createDatabase_cli():
+ if len(sys.argv) == 4:
+ name = sys.argv[2]
+ path = os.path.expanduser(sys.argv[3])
+ if name == 'location':
+ sys.exit('%s is invalid database name.'%name)
+ if (not os.path.exists(os.path.dirname(path))) and (os.path.dirname(path)):
+ # os.path.dirname(path) -- to be able to create a database in current directory
+ sys.exit('%s is unreachable.'%os.path.dirname(path))
+#:load_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'))
+ # load config from user home
+ config = ConfigParser.ConfigParser()
+ config.readfp(f)
+ f.close()
+ config.set('data',name,path)
+#:save_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ config.write(f)
+ f.close()
+ edi_core.data_location = path
+ init()
+ else:
+ sys.exit('Too little or too many parameters. This command takes 2 parameters: databasename path_to_new_database.')
+
+## choose or show where to add new tasks
+# @ingroup EDI_CLI
+def topbot_cli():
+ if len(sys.argv)<3:
+ print 'Add new tasks'
+ print '%s of group' % edi_core.add_top_or_bottom
+ else:
+ if (sys.argv[2]!='bottom') and (sys.argv[2] != 'top'):
+ sys.exit('%s is invalid parameter. Possible values are "top" or "bottom".'%sys.argv[2])
+#:load_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'))
+ # load config from user home
+ config = ConfigParser.ConfigParser()
+ config.readfp(f)
+ f.close()
+ config.set('locations','add_top_or_bottom',sys.argv[2])
+#:save_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ config.write(f)
+ f.close()
+
+## print group in md format
+# @ingroup EDI_CLI
+def in_cli():
+ edi_core.list_option = 'md'
+
+ option = ''
+ if len(sys.argv) > 2:
+ if sys.argv[2] == '-A':
+ # option to create agenda is set
+ option = 'create agenda'
+ del sys.argv[2]
+
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+ if len(sys.argv) == 2:
+ # no parameter - just command
+ # select current group when in tree
+ if status == 'in tree':
+ #get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+ #end
+
+ task_attributes = list_group(group)
+ if group == 'root':
+ # for root, first task is the title in the md document
+ task_attributes[0]['head'] = 'head group'
+ if option == 'create agenda':
+ # Generate agenda
+ for t in task_attributes[1:]:
+ if t['head'] != 'empty line':
+ edi_core.agenda.append('%3d - %s %s'%(t['position'],t['group'],t['title']))
+ print_list(task_attributes)
+ return
+ else:
+ # default group
+ group = 'root'
+ sys.argv.append(group)
+ # change status because there is no position parameter, no need to search for tid at given position
+ status = ''
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify that task tid is in database
+ if not is_this_task_a_group(sys.argv[2]):
+ sys.exit('%s is not a group.'%sys.argv[2])
+
+ task_attributes = list_group(sys.argv[2])
+ if sys.argv[2] == 'root':
+ # for root, first task is the title in the md document
+ task_attributes[0]['head'] = 'head group'
+ if option == 'create agenda':
+ # Generate agenda
+ for t in task_attributes[1:]:
+ if t['head'] != 'empty line':
+ edi_core.agenda.append('%3d - %s %s'%(t['position'],t['group'],t['title']))
+ print_list(task_attributes)
+
+## print group in reStructuredText format
+# @ingroup EDI_CLI
+def re_cli():
+ edi_core.list_option = 'rst'
+
+ option = ''
+ if len(sys.argv) > 2:
+ if sys.argv[2] == '-A':
+ # option to create agenda is set
+ option = 'create agenda'
+ del sys.argv[2]
+
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+ if len(sys.argv) == 2:
+ # no parameter - just command
+ # select current group when in tree
+ if status == 'in tree':
+ #get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+ #end
+
+ task_attributes = list_group(group)
+ if group == 'root':
+ # for root, first task is the title in the rst document
+ task_attributes[0]['head'] = 'head group'
+ if option == 'create agenda':
+ # Generate agenda
+ for t in task_attributes[1:]:
+ if t['head'] != 'empty line':
+ # keep only title
+ edi_core.agenda.append('#. %s'%t['title'])
+ print_list(task_attributes)
+ return
+ else:
+ # default group
+ group = 'root'
+ sys.argv.append(group)
+ # change status because there is no position parameter, no need to search for tid at given position
+ status = ''
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify that task tid is in database
+ if not is_this_task_a_group(sys.argv[2]):
+ sys.exit('%s is not a group.'%sys.argv[2])
+
+ task_attributes = list_group(sys.argv[2])
+ if sys.argv[2] == 'root':
+ # for root, first task is the title in the rst document
+ task_attributes[0]['head'] = 'head group'
+ if option == 'create agenda':
+ # Generate agenda
+ for t in task_attributes[1:]:
+ if t['head'] != 'empty line':
+ # keep only title
+ edi_core.agenda.append('#. %s'%t['title'])
+ print_list(task_attributes)
+
+## show trees
+# @ingroup EDI_CLI
+# print all trees: group tids and titles
+def show_trees_cli():
+
+#:loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ print '%s - %s' % (d,path)
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ print ''.join(show_tree())
+
+## zip database
+# @ingroup EDI_CLI
+# The path parameter is optional
+# When 2 parameters are given, run data function instead.
+def zip_cli():
+
+ if len(sys.argv) < 3:
+ data()
+ return
+ if len(sys.argv) == 3:
+ # path is current directory
+ zip_path = '.'
+ else:
+ zip_path = sys.argv[3]
+ database_name = sys.argv[2]
+
+ # Verify parameters
+ if not database_name in edi_core.selected:
+ sys.exit('%s database is not selected.'%database_name)
+ if not os.path.exists(zip_path):
+ sys.exit('%s is unreachable.'%zip_path)
+
+ # get database system path
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+
+ # Verify parameters
+ if not os.path.exists(z[database_name]):
+ sys.exit('%s is unreachable.'%z[database_name])
+
+ print 'zipped: %s' % database_name
+ print 'system path: %s' % z[database_name]
+ print 'target: %s/%s.tar.bz2' % (zip_path, database_name)
+ print
+ #print ('tar -c %s | bzip2 -9 > %s/%s.tar.bz2'%(z[database_name], zip_path, database_name))
+ os.system('tar -cj %s > %s/%s.tar.bz2'%(z[database_name], zip_path, database_name))
+
+## list autolink groups or set autolink groups
+# @ingroup EDI_CLI
+def autolink_cli():
+ if len(sys.argv)<3:
+ print 'Autolink groups'
+ if not edi_core.autolink:
+ print 'Empty'
+ else:
+ print ','.join(edi_core.autolink)
+ print
+ print '\n'.join(edi_core.autolink)
+ if len(sys.argv)==3:
+#:load_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'))
+ # load config from user home
+ config = ConfigParser.ConfigParser()
+ config.readfp(f)
+ f.close()
+ config.set('locations','autolink',sys.argv[2])
+#:save_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ config.write(f)
+ f.close()
+
+## convert empty group to task
+# @ingroup EDI_CLI
+def cv_cli():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify that task tid is not root
+ if sys.argv[2] == 'root':
+ sys.exit('root is invalid group')
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ if not is_this_task_a_group(sys.argv[2]):
+ sys.exit('%s is not a group.'%sys.argv[2])
+ print '\n'.join(convert_group_to_task(sys.argv[2]))
+
+## display user name or set user name
+# @ingroup EDI_CLI
+def user_cli():
+ if len(sys.argv)<3:
+ print 'User name: %s <%s>'%(edi_core.user,edi_core.email)
+ if len(sys.argv)>3:
+ sys.exit('Too many parameters.')
+ if len(sys.argv)==3:
+#:load_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'))
+ # load config from user home
+ config = ConfigParser.ConfigParser()
+ config.readfp(f)
+ f.close()
+ config.set('settings','username',sys.argv[2])
+#:save_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ config.write(f)
+ f.close()
+
+## display user email or set user email
+# @ingroup EDI_CLI
+def email_cli():
+ if len(sys.argv)<3:
+ print 'User name: %s <%s>'%(edi_core.user,edi_core.email)
+ if len(sys.argv)>3:
+ sys.exit('Too many parameters.')
+ if len(sys.argv)==3:
+#:load_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'))
+ # load config from user home
+ config = ConfigParser.ConfigParser()
+ config.readfp(f)
+ f.close()
+ config.set('settings','useremail',sys.argv[2])
+#:save_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ config.write(f)
+ f.close()
+
+## show statistics
+# @ingroup EDI_CLI
+def stats_cli():
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+ # Decide what to execute
+ if len(sys.argv) == 2:
+ tid = 'root'
+ if status:
+ # in tree, set tid to cwd
+ tid = cwd.split('/')[-1]
+ if tid == 'tree':
+ tid = 'root'
+ exe = 'stats in tree'
+ else:
+ exe = 'stats root'
+ else:
+ tid = sys.argv[2]
+
+#:find_task_in_selected_databases_and_setup_data_location
+ if tid == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ elif tid != 'tree':
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if tid in tasks:
+ break
+ exe = 'stats parameter'
+
+ if exe == 'stats root':
+#:loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ print '%s - %s' % (d,path)
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ print '\n'.join(statistics('for database'))
+ else:
+ if not is_this_task_a_group(tid):
+ sys.exit('Group %s not found.'%tid)
+
+ print '\n'.join(statistics(tid))
+
+## add many tasks or many groups
+# @ingroup EDI_CLI
+def many_cli():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+ options = ''
+ group_parameter_status = 'group is a command line parameter'
+ if len(sys.argv) < 4:
+ # Parameters are: command filename
+ # select current group when in tree
+ if status == 'in tree':
+ #get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+ #end
+ else:
+ # default group
+ group = 'root'
+ group_parameter_status = 'edi selected group'
+
+ text_file = sys.argv[2]
+ else:
+ if ((sys.argv[2] == '-1') or (sys.argv[2] == '-g')) and len(sys.argv) == 4:
+ options = sys.argv[2]
+ text_file = sys.argv[3]
+ # select current group when in tree
+ if status == 'in tree':
+ #get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+ #end
+ else:
+ # default group
+ group = 'root'
+ group_parameter_status = 'edi selected group'
+ if ((sys.argv[2] == '-1') or (sys.argv[2] == '-g')) and len(sys.argv) == 5:
+ options = sys.argv[2]
+ group = sys.argv[3]
+ text_file = sys.argv[4]
+ if ((sys.argv[2] != '-1') and (sys.argv[2] != '-g')):
+ group = sys.argv[2]
+ text_file = sys.argv[3]
+ if not options:
+ exe = 'add'
+ else:
+ if options == '-1':
+ # option -1 adds one task per line in text_file
+ exe = 'add one task per line'
+ elif options == '-g':
+ # option -F stores text_file filename in first line of description
+ exe = 'add groups from text file'
+
+ if (group == 'root') and (not status):
+ # set default database when group is root and cwd not in tree
+#:set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ if (group != 'root') and ((not status) or (group_parameter_status == 'group is a command line parameter')):
+#:find_group_in_selected_databases_and_setup_data_location
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ groups = os.listdir(edi_core.data_location_groups)
+ if group in groups:
+ break
+
+ if not is_this_task_a_group(group):
+ sys.exit('%s is not a group.'%group)
+
+ if not os.path.exists(text_file):
+ sys.exit('%s unreachable file.'%text_file)
+
+ if exe == 'add':
+ print add_many_tasks(group,text_file)
+ if exe == 'add one task per line':
+ print add_many_one_line_tasks(group,text_file)
+ if exe == 'add groups from text file':
+ print add_many_groups_from_text(group,text_file)
+
+## demo to show how to use Easydoneit CLI
+# @ingroup EDI_CLI
+def demo_cli():
+ print '# Easydoneit CLI - Demo'
+ # print (), to print without a newline
+ print ('#'),
+#:edi_version
+ f = open('%s/VERSION'%os.path.abspath(os.path.dirname(sys.argv[0])))
+ v = f.readline()
+ f.close()
+ print 'Version %s'%v
+
+ print '''
+# Run the commands below in a terminal
+
+# create the demo database
+edi createDatabase easydoneit_cli_demo ~/easydoneit_cli_demo
+
+# show selected databases - SAVE THIS STRING
+edi select
+
+# show default database - SAVE THIS STRING
+edi default
+
+# select demo database
+edi select easydoneit_cli_demo
+
+# demo database is default
+edi default easydoneit_cli_demo
+
+# the configuration file is in your home, change text editor if you dont use VI
+cat ~/.easydoneit.ini
+
+# create a task in default database, type your task in the text editor
+edi cr
+
+# list tasks
+edi ls
+
+# add task directly from command line
+edi add -t demo_task_1
+edi add -t 'demo task 2 - string with spaces'
+edi ls
+
+# tasks can be added on the top of the list
+edi topbot top
+
+# add task from file
+echo 'demo task 3' > ~/demo_3.txt
+edi add ~/demo_3.txt
+rm ~/demo_3.txt
+edi ls
+
+# choose to add tasks at the bottom
+edi topbot bottom
+
+# it is simpler to use Easydoneit CLI in the tree
+cd ~/easydoneit_cli_demo/tree
+edi ls
+
+# create a group using position
+edi mkdir 0
+
+# background color
+edi bc 0 230,230,230,255
+
+# change order, moves task at position 0 to position 2
+edi order 0 2
+
+# change state to done, pending and inactive
+edi set 0 1
+edi set 1 3
+edi set 2 4
+edi ls
+
+# display the complete tree of groups
+edi st
+
+# filter out done tasks
+edi stfi done disable
+edi ls
+
+# search
+edi search demo
+
+# delete task
+edi rm 1
+
+# display system path for a task
+edi path 2
+
+# generate html
+edi html
+
+# generate reStructuredText (rst)
+edi re
+
+# zip
+edi zip easydoneit_cli_demo ~/
+
+# delete database, RESELECT YOUR DATABASES WITH edi select BEFORE RUNNING THE COMMANDS BELOW
+cd -
+edi data -d easydoneit_cli_demo
+edi data
+
+# remove database zip
+rm ~/easydoneit_cli_demo.tar.bz2
+
+# end
+ '''
+
+## set list of group for edi ls -L, -La and -Lx options
+# @ingroup EDI_CLI
+def list_cli():
+ if len(sys.argv)<3:
+ print 'list of groups'
+ if not edi_core.list_of_groups:
+ print 'Empty'
+ else:
+ print ','.join(edi_core.list_of_groups)
+ print
+ print '\n'.join(edi_core.list_of_groups)
+ if len(sys.argv)==3:
+#:load_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'))
+ # load config from user home
+ config = ConfigParser.ConfigParser()
+ config.readfp(f)
+ f.close()
+ config.set('locations','list',sys.argv[2])
+#:save_ini_file
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ config.write(f)
+ f.close()
+
+## copy database sys.argv[2] to database sys.argv[3]
+# @ingroup EDI_CLI
+def merge_cli():
+ if len(sys.argv) < 4:
+ sys.exit('Too little parameters.')
+
+ # set source path (argv 2)
+ path = ''
+ if os.path.exists(sys.argv[2]):
+ path = sys.argv[2]
+ if not path:
+ # argv 2 could be a database name
+ dbs = dict(edi_core.databases)
+ if sys.argv[2] in dbs.keys():
+ path = dbs[sys.argv[2]]
+ if not path:
+ sys.exit('Source database %s not found.'%sys.argv[2])
+
+ # check source database structure
+ directories = ['tasks', 'groups', 'tree']
+
+ status = 'source database structure ok'
+ for d in directories:
+ directory = '%s/%s'%(path, d)
+ if not os.path.exists(directory):
+ print 'Missing %s'%directory
+ status = 'missing directories in source database'
+ if status == 'missing directories in source database':
+ sys.exit('Invalid source database %s'%path)
+
+ # check destination database
+ if not sys.argv[3] in edi_core.selected:
+ sys.exit('%s database not selected.'%sys.argv[3])
+
+ s = dict(zip(edi_core.selected, edi_core.selected_path))
+ destination_path = s[sys.argv[3]]
+
+ # copy database
+ for d in directories:
+ directory = '%s/%s'%(path, d)
+ ddirectory = '%s/%s'%(destination_path, d)
+ os.system('cp -R %s/* %s'%(directory,ddirectory))
+
+
+## copy media sys.argv[3] to task sys.argv[2]
+# @ingroup EDI_CLI
+def media_cli():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ if len(sys.argv)<4:
+ print '\n'.join(get_media(sys.argv[2]))
+ if len(sys.argv)==4:
+ if not os.path.exists(sys.argv[3]):
+ sys.exit('%s not found.'%sys.argv[3])
+ print set_media(sys.argv[2],sys.argv[3])
+
+
+## copy attachments sys.argv[3] to task sys.argv[2]
+# @ingroup EDI_CLI
+def at_cli():
+ if len(sys.argv) < 3:
+ sys.exit('Too little parameters.')
+ # Figure out if the command is run in the tree
+#:figure_out_if_in_tree
+ status = ''
+ cwd = os.path.realpath(os.getcwd())
+ #loop_on_selected_databases
+ z = zip(edi_core.selected, edi_core.selected_path)
+ for d,path in z:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ # check path from left
+ real_tree_path = os.path.realpath(edi_core.data_location_tree)
+ if real_tree_path == cwd[:len(real_tree_path)]:
+ status = 'in tree'
+ p_l = edi_core.data_location_tree.split('/')[-2:]
+ break
+
+#:find_task_sysargv2_in_selected_databases
+ # the goal for this code is to reset status when a tid is an existing task
+ # when parameter is taskid, it doesnt matter if cwd is in tree
+ # find task in selected databases
+ for path in edi_core.selected_path:
+ data_location = os.path.expanduser(path)
+ data_location_tasks = '%s/tasks'%data_location
+
+ if not os.path.exists(data_location):
+ sys.exit('%s is unreachable.'%path)
+ tasks = os.listdir(data_location_tasks)
+ if sys.argv[2] in tasks:
+ # found task, setup database below
+ status = ''
+ break
+
+ if status == 'in tree':
+#:get_current_group_in_tree
+ if cwd.split('/')[-1] == 'tree':
+ group = 'root'
+ else:
+ group = cwd.split('/')[-1]
+#:get_position_from_argv2
+ try:
+ position = int(sys.argv[2])
+ except:
+ sys.exit('%s is invalid position'%sys.argv[2])
+#:find_task_at_position
+ if position < 0:
+ sys.exit('Position %d is invalid.'%position)
+ group_tasks = os.listdir(generate_group_path(group))
+ order_id = baseconvert(position)
+ tid = ''
+ for t in group_tasks:
+ # find exact match
+ if order_id == t[:ORDER_ID_LENGTH]:
+ tid = t[ORDER_ID_LENGTH:]
+ if not tid:
+ sys.exit('Position %d does not exist.'%position)
+ sys.argv[2] = tid
+
+ # cant cover sys.exit('%s is unreachable.'%d) below, exception catched before. Dont add no cover because the code is reused in other functions.
+#:tab1_find_task_in_selected_databases_and_setup_data_location
+ if sys.argv[2] == 'root':
+ #set_default_database
+ z = dict(zip(edi_core.selected, edi_core.selected_path))
+ edi_core.data_location = os.path.expanduser(z[edi_core.default_add_in])
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location)
+ else:
+ for path in edi_core.selected_path:
+ edi_core.data_location = os.path.expanduser(path)
+ edi_core.data_location_tasks = '%s/tasks'%edi_core.data_location
+ edi_core.data_location_groups = '%s/groups'%edi_core.data_location
+ edi_core.data_location_tree = '%s/tree'%edi_core.data_location
+
+ if not os.path.exists(edi_core.data_location):
+ sys.exit('%s is unreachable.'%edi_core.data_location) #pragma: no cover
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if sys.argv[2] in tasks:
+ break
+ # Verify that task tid is in database
+ tasks = os.listdir(edi_core.data_location_tasks)
+ if not sys.argv[2] in tasks:
+ sys.exit('%s not found.'%sys.argv[2])
+ if len(sys.argv)<4:
+ print '\n'.join(get_attachments(sys.argv[2]))
+ if len(sys.argv)>3:
+ for fn in sys.argv[3:]:
+ if not os.path.exists(fn):
+ sys.exit('%s not found.'%fn)
+ print '\n'.join(set_attachments(sys.argv[2],sys.argv[3:]))
+
+
+## CLI command functions
+FUNCTIONS = [help,version,create_task_cli,lsid,add_task_cli,cat,mkdir,show_group_for_task_cli,rm,vi,change_order,set,reset,search,move_task,copy_task,link_task,get_task_path,tree,ls,set_status_filter,data,html_cli,selected_cli,default_cli,cpb_cli,mvb_cli,fc_cli,bc_cli,createDatabase_cli,topbot_cli,in_cli,re_cli,show_trees_cli,zip_cli,autolink_cli,cv_cli,user_cli,email_cli,stats_cli,many_cli,demo_cli,list_cli,merge_cli,media_cli,at_cli]
+
+COMMAND_MAP = dict(zip(COMMANDS,FUNCTIONS))
+
+
+if __name__ == '__main__':
+#:print_command_map
+#:end
+ start() #pragma: no cover
+
+ if len(sys.argv)> 1: #pragma: no cover
+ # remove whitespaces from parameters. ^M can come for scripts
+ for i,a in enumerate(sys.argv[1:]): #pragma: no cover
+ sys.argv[i+1] = a.strip() #pragma: no cover
+#:not profiler
+ COMMAND_MAP.get(sys.argv[1],Nothing)() #pragma: no cover
+#:else
+#:end
+ else:
+ help() #pragma: no cover
+#:profiler
+#:end
diff --git a/edi_common.py b/edi_common.py
@@ -0,0 +1,72 @@
+#!/usr/local/util/bin/python
+#
+## @package edi_common
+# Common data and functions for Easydoneit CLI.
+# python style<br>
+# basic functions that can be reused outside easydoneit<br>
+#
+# Copyright (C) 2014 Spartatek AB
+#
+# contact@spartatek.se
+# http://spartatek.se
+#
+# EASYDONEIT CLI is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Genereric Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# EASYDONIT CLI is distributed in the hope that it will be usesul,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Genereral Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# GNU licences http://www.gnu.org/licenses
+
+import os
+import logging
+
+#==========================================================================
+# Setup logger
+#==========================================================================
+if __name__ == '__main__':
+ console = logging.StreamHandler() #pragma: no cover
+ formatter = logging.Formatter('%(levelname)-8s (%(name)s) %(message)s') #pragma: no cover
+ console.setFormatter(formatter) #pragma: no cover
+ console.setLevel(logging.INFO) #pragma: no cover
+ logging.getLogger('').addHandler(console) #pragma: no cover
+logger = logging.getLogger(os.path.basename(__file__))
+logger.setLevel(logging.INFO);
+#==========================================================================
+# Logger has been set up
+#==========================================================================
+
+
+
+#==========================================================================
+
+## Find all files in tree path.
+# @return list of paths for files
+# @param[in] path to tree
+# @ingroup EDI_CORE
+def ffind(path, namefs=None, relative=True):
+ """
+ Finds files in the directory tree starting at 'path' (filtered by the
+ functions in the optional 'namefs' sequence); if the 'relative'
+ flag is not set, the result sequence will contain absolute paths.
+
+ Returns a sequence of paths for files found.
+ """
+ if not os.access(path, os.R_OK):
+ logger.error("cannot access path: '%s'" % path) #pragma: no cover
+
+ fileList = []
+ try:
+ for dir, subdirs, files in os.walk(path):
+ fileList.extend(['%s%s%s' % (dir, os.sep, f) for f in files])
+ if not relative: fileList = map(os.path.abspath, fileList)
+ if namefs:
+ for ff in namefs: fileList = filter(ff, fileList) #pragma: no cover
+ except Exception, e: logger.error(str(e)) #pragma: no cover
+ return(fileList)
diff --git a/edi_core.py b/edi_core.py
@@ -0,0 +1,2673 @@
+#! /usr/bin/env python
+# -*- coding: latin-1 -*-
+## @package edi_core
+# Core module
+#
+#
+# Copyright (C) 2014 Spartatek AB
+#
+# contact@spartatek.se
+# http://spartatek.se
+#
+# EASYDONEIT CLI is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Genereric Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# EASYDONIT CLI is distributed in the hope that it will be usesul,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Genereral Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# GNU licences http://www.gnu.org/licenses
+
+import sys
+import re
+import time
+import shutil
+import os
+import ConfigParser
+from string import ascii_lowercase
+from string import ascii_uppercase
+from string import digits
+import random
+import stat
+import getpass
+
+from edi_common import *
+
+## 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.
+# Values: 'cli', 'web'
+user_interface = 'cli'
+
+data_location = ''
+data_location_tasks = ''
+data_location_groups = ''
+data_location_tree = ''
+
+## Available databases
+databases = []
+
+## list selected databases
+selected = []
+selected_path = []
+## default database where tasks are created
+default_add_in = ''
+
+## add new tasks in 'bottom' of group or 'top' of group
+add_top_or_bottom = 'bottom'
+
+## Autlink groups (array of tids)
+autolink = ''
+
+## list of groups for edi ls -L, -La and -Lx (array of tids)
+list_of_groups = ''
+
+## characters for task ids
+ID_BASE = sorted(list(digits)+list(ascii_lowercase)+list(ascii_uppercase)+['_',','])
+ID_BASE_STRING = ''.join(ID_BASE)
+## Length of task id
+ID_LENGTH = 16
+## Length of order id in groups
+ORDER_ID_LENGTH = 8
+BASE = len(ID_BASE)
+
+TASK_STATUS = [' Active',
+ ' Done',
+ ' Ongoing',
+ ' Pending',
+ 'Inactive',
+ ' Void']
+TASK_STATUS_ACTIVE = 0
+TASK_STATUS_DONE = 1
+TASK_STATUS_ONGOING = 2
+TASK_STATUS_PENDING = 3
+TASK_STATUS_INACTIVE = 4
+TASK_STATUS_VOID = 5
+
+## Sort order for sort_task_attributes function, ongoing, active, pending, done, inactive, void
+SORT_TASK_ORDER = [2,0,3,1,4,5]
+
+LIST_OPTIONS = ['tids',
+ 'positions',
+ 'html']
+list_option = 'tids'
+
+STATUS_FILTER_STATES = ['enable','disable']
+
+## enables all status filters
+status_filters = [STATUS_FILTER_STATES[0] for i in range(len(TASK_STATUS))]
+## status filter dictionary, initialized after loading ini file
+status_filters_d = {}
+
+## colors
+no_color = (-1,-1,-1,255)
+#status_fgColors = [(0,0,0,255) for i in range(len(TASK_STATUS))]
+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)]
+status_fgColors_d = {}
+## no background color by default
+status_bgColors = [no_color for i in range(len(TASK_STATUS))]
+status_fgColors_d = {}
+
+## text editor in terminal - easydoneit.ini [settings] EDITOR
+editor = 'vi'
+
+## Agenda generated by edi.in_cli
+agenda = []
+
+## user name
+user = ''
+
+## user email
+email = ''
+
+## stats for statistics function
+stats = {}
+
+## total number of tasks for statistics function
+stats_total = 0
+
+## creation dates of tasks in statistics, used to find out oldest task
+stats_creation_dates = []
+
+## timely state changes and creation dates
+# Type: dict of dict of integers
+# keys are dates
+# each dates is a dictionary with STATS_OVERTIME_KEYS keys
+# each key is the amount for states and creation
+stats_overtime = {}
+
+## timely state changes and creation dates
+STATS_OVERTIME_KEYS = [TASK_STATUS[i] for i in range(len(TASK_STATUS))] + ['Creation']
+
+## Group directory, cache result to speed up color functions
+group_directory_file_list = []
+
+## initializes path to data.
+# creates directories<br>
+# creates root group and task<br>
+# @ingroup EDI_CORE
+def init():
+ # Set global variables
+ global data_location_tasks
+ global data_location_groups
+ global data_location_tree
+ global status_filters
+ global status_filters_d
+ global status_fgColors
+ global status_fgColors_d
+ global status_bgColors
+ global status_bgColors_d
+
+ status_filters_d = dict(zip(TASK_STATUS,status_filters))
+ status_fgColors_d = dict(zip(TASK_STATUS,status_fgColors))
+ status_bgColors_d = dict(zip(TASK_STATUS,status_bgColors))
+
+ data_location_tasks = '%s/tasks'%data_location
+ data_location_groups = '%s/groups'%data_location
+ data_location_tree = '%s/tree'%data_location
+
+ if not os.path.exists(data_location_tasks):
+ if not os.path.exists(data_location):
+ os.mkdir(data_location)
+ os.mkdir(data_location_tasks)
+ os.mkdir(data_location_groups)
+ os.mkdir(data_location_tree)
+ os.mkdir('%s/root'%data_location_groups)
+
+ # Create root description in tasks
+ task_path = '%s/root'%data_location_tasks
+ os.mkdir(task_path)
+ # copy root description from script directory
+ shutil.copy('%s/root.txt'%os.path.abspath(os.path.dirname(sys.argv[0])),'%s/description.txt'%task_path)
+
+ # Create status
+ f = open('%s/status'%task_path,'w')
+ f.write(TASK_STATUS[TASK_STATUS_VOID])
+ f.close()
+
+## log actions in database - data_location/log.txt
+# @param[in] s string without user name and timestamp
+# @ingroup EDI_CORE
+def edi_log(s):
+ global user
+ global email
+ f = open('%s/log.txt'%data_location,'a')
+ f.write('%s - %s - %s\n'%(time.strftime("%Y-%m-%d %H:%M"), '%s <%s>'%(user,email),s.strip()))
+ f.close()
+
+
+## select location database
+# @param[in] location database name
+# @ingroup EDI_CORE
+def select_database(location):
+ global data_location
+ global data_location_tasks
+ global data_location_groups
+ global data_location_tree
+
+ z = dict(zip(selected, selected_path))
+ data_location = os.path.expanduser(z[location])
+ data_location_tasks = '%s/tasks'%data_location
+ data_location_groups = '%s/groups'%data_location
+ data_location_tree = '%s/tree'%data_location
+ if not os.path.exists(data_location):
+ return '%s is unreachable.'%data_location
+ else:
+ return ''
+
+## Mix foreground colors from task, groups and default colors.
+# @return color array
+# @param[in] tid task id
+# @ingroup EDI_CORE
+# Collect all defined colors in link groups.
+# For each group, the group gets the first color defined in the tree<br>
+# compute average color
+def mix_fgcolors(tid):
+ # mix colors
+ # collect all defined colors in groups to root
+ colors = []
+ # root has no parent group, dont search parent group color
+ if tid != 'root':
+ # find all groups - link and parent group
+ if is_linked(tid):
+ groups = os.listdir('%s/groups/' % generate_task_path(tid))
+ else:
+ groups = [find_group_containing_task(tid)]
+
+ # walk parent groups until a color or root is found
+ while groups:
+ status_color = 'search'
+ if os.path.isfile('%s/fgColor'%generate_task_path(groups[0])):
+ f = open('%s/fgColor'%generate_task_path(groups[0]))
+ c = tuple([int(i) for i in f.readline().split(',')])
+ f.close()
+ # -1 means no_color, mix colors
+ if c[0] != -1:
+ colors.append(c)
+ status_color = 'found color'
+ if status_color == 'search':
+ parent_group = find_group_containing_task(groups[0])
+ if parent_group != 'root':
+ groups.append(parent_group)
+ del groups[0]
+
+ # compute average color
+ if colors:
+ color = [0,0,0,0]
+ for c in colors:
+ for i in range(len(c)):
+ color[i] += c[i]
+ colorCount = len(colors)
+ color = tuple([i/colorCount for i in color])
+ if not colors:
+ # no defined colors, use default color for status
+ task_status = get_status(tid)
+ color = status_fgColors_d[task_status]
+ return color
+
+## get color for task tid
+# @return color array
+# @param[in] tid task id
+# @ingroup EDI_CORE
+# when no color is set, mix colors from groups or use defaults
+def get_forground_color(tid):
+ color = no_color
+
+ if os.path.isfile('%s/fgColor'%generate_task_path(tid)):
+ f = open('%s/fgColor'%generate_task_path(tid))
+ color = tuple([int(i) for i in f.readline().split(',')])
+ f.close()
+ # -1 means no_color, mix colors
+ if color[0] == -1:
+ color = mix_fgcolors(tid)
+ else:
+ color = mix_fgcolors(tid)
+ return color
+
+## set color for task tid
+# @return color array
+# @param[in] tid task id
+# @param[in] color_s color array
+# @ingroup EDI_CORE
+def set_forground_color(tid,color_s):
+ f = open('%s/fgColor'%generate_task_path(tid),'w')
+ f.write(color_s)
+ f.close()
+ edi_log('set foreground color in %s to %s'%(tid,color_s))
+
+## Mix background colors from task, groups and default colors.
+# @return color array
+# @param[in] tid task id
+# @ingroup EDI_CORE
+# Collect all defined colors in link groups.
+# For each group, the group gets the first color defined in the tree<br>
+# compute average color
+def mix_bgcolors(tid):
+ # mix colors
+ # collect all defined colors in groups to root
+ colors = []
+ # root has no parent group, dont search parent group color
+ if tid != 'root':
+ # find all groups - link and parent group
+ if is_linked(tid):
+ groups = os.listdir('%s/groups/' % generate_task_path(tid))
+ else:
+ groups = [find_group_containing_task(tid)]
+
+ # walk parent groups until a color or root is found
+ while groups:
+ status_color = 'search'
+ if os.path.isfile('%s/bgColor'%generate_task_path(groups[0])):
+ f = open('%s/bgColor'%generate_task_path(groups[0]))
+ c = tuple([int(i) for i in f.readline().split(',')])
+ f.close()
+ # -1 means no_color, mix colors
+ if c[0] != -1:
+ colors.append(c)
+ status_color = 'found color'
+ if status_color == 'search':
+ parent_group = find_group_containing_task(groups[0])
+ if parent_group != 'root':
+ groups.append(parent_group)
+ del groups[0]
+
+ # compute average color
+ if colors:
+ color = [0,0,0,0]
+ for c in colors:
+ for i in range(len(c)):
+ color[i] += c[i]
+ colorCount = len(colors)
+ color = tuple([i/colorCount for i in color])
+ if not colors:
+ # no defined colors, use default color for status
+ task_status = get_status(tid)
+ color = status_bgColors_d[task_status]
+ return color
+
+## get color for task tid
+# @return color array
+# @param[in] tid task id
+# @ingroup EDI_CORE
+# when no color is set, mix colors from groups or use defaults
+def get_background_color(tid):
+ color = no_color
+
+ if os.path.isfile('%s/bgColor'%generate_task_path(tid)):
+ f = open('%s/bgColor'%generate_task_path(tid))
+ color = tuple([int(i) for i in f.readline().split(',')])
+ f.close()
+ # -1 means no_color, mix colors
+ if color[0] == -1:
+ color = mix_bgcolors(tid)
+ else:
+ color = mix_bgcolors(tid)
+ return color
+
+## set color for task tid
+# @return color array
+# @param[in] tid task id
+# @param[in] color_s color array
+# @ingroup EDI_CORE
+def set_background_color(tid,color_s):
+ f = open('%s/bgColor'%generate_task_path(tid),'w')
+ f.write(color_s)
+ f.close()
+ edi_log('set background color in %s to %s'%(tid,color_s))
+
+## converts color to hex format
+# @return color hex string
+# @param[in] color color array
+# @ingroup EDI_CORE
+def color_to_hex(color):
+ c = []
+ for n in color:
+ if n == -1:
+ # -1 is no color, white
+ c.append(255)
+ else:
+ c.append(n)
+ color = tuple(c)
+ return '%02x%02x%02x' % (color[0],color[1],color[2])
+
+## filesystem path for task in database tasks
+# @return path
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def generate_task_path(tid):
+ return '%s/%s'%(data_location_tasks,tid)
+
+## filesystem path for group in database groups
+# @return path
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def generate_group_path(tid):
+ return '%s/%s/'%(data_location_groups,tid)
+
+## get status for tid
+# @return status string
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def get_status(tid):
+ # open status file
+ try:
+ f = open('%s/status'%generate_task_path(tid))
+ status = f.readline()
+ f.close()
+ except:
+ print '%s is an invalid task.'%generate_task_path(tid)
+ status = TASK_STATUS[TASK_STATUS_VOID]
+ return status
+
+## creates a string with task id, status and string parameter
+# @return list of strings
+# @param[in] tid task id
+# @param[in] s task title string
+# @ingroup EDI_CORE
+# Displays a task depending on edi_core.list_option
+def generate_task_string_with_tid(tid,s):
+ status = get_status(tid)
+ if list_option == 'tids':
+ # %*s to dynamically adjust id length
+ r = '%*s - %s - %s'%(ID_LENGTH,tid,status,s)
+ if list_option == 'positions':
+ if is_this_task_a_group(tid):
+ # print tid for groups in the list
+ r = '%*s - %s - %s'%(ID_LENGTH,tid,status,s)
+ else:
+ tid = ' '
+ r = '%*s %s - %s'%(ID_LENGTH,tid,status,s)
+ if list_option == 'md':
+ # print title and description
+ f = open('%s/description.txt'%generate_task_path(tid))
+ # remove title line
+ description = ''.join(f.readlines()[1:])
+ f.close()
+
+ tid = ' '
+ # add <br> for long titles
+ r = '\n---\n#%s<br>\n%s'%(s,description)
+ if list_option == 'rst':
+ # print title and description
+ f = open('%s/description.txt'%generate_task_path(tid))
+ # remove title line
+ description_l = f.readlines()[1:]
+ f.close()
+
+ # convert urls to link
+ NOBRACKET = r'[^\]\[]*'
+ BRK = ( r'\[('
+ + (NOBRACKET + r'(\[')*6
+ + (NOBRACKET+ r'\])*')*6
+ + NOBRACKET + r')\]' )
+ NOIMG = r'(?<!\!)'
+ LINK_RE = NOIMG + BRK + \
+ r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12\s*)?\)'''
+ # [text](url) or [text](<url>) or [text](url "title")
+
+ compiled_re = re.compile("^(.*?)%s(.*?)$" % LINK_RE, re.DOTALL | re.UNICODE)
+
+ for line_index,l in enumerate(description_l):
+ if 'http' in l:
+ try:
+ match = compiled_re.match(l)
+ url = match.group(9)
+ text = match.group(2)
+ before_url = match.group(1)
+ #title = match.group(13)
+ after_url = match.group(14)
+
+ l = '%s`%s<%s>`_%s'% (before_url, text, url, after_url)
+ except:
+ # not used, information
+ link_status = 'WARNING: the link is not in format [title](url)'
+ description_l[line_index] = l
+
+ description = ''.join(description_l)
+
+ tid = ' '
+ # remove position and type (GROUP, LINK) from title
+ title = s[11:].lstrip()
+ r = '%s\n'%title + '='*len(title) + '\n%s\n\n'%description
+ if list_option == 'html':
+ #<tr bgcolor="#FEFFCC">
+ # <td class="subject"> <font color="#000000">t1</font></td>
+ # <td class="description"> <font color="#000000"> </font></td>
+ #</tr>
+#:define read_description
+ # convert < to < and > > to ignore html tags in title line
+ s = s.replace('<','<').replace('>','>')
+ f = open('%s/description.txt'%generate_task_path(tid))
+ # remove title line, convert < to < and > > to ignore html tags
+ description_l = ['%s<br>'%i.rstrip().replace('<','<').replace('>','>') for i in f.readlines()[1:]]
+ f.close()
+
+ # convert urls to link
+ NOBRACKET = r'[^\]\[]*'
+ BRK = ( r'\[('
+ + (NOBRACKET + r'(\[')*6
+ + (NOBRACKET+ r'\])*')*6
+ + NOBRACKET + r')\]' )
+ NOIMG = r'(?<!\!)'
+ LINK_RE = NOIMG + BRK + \
+ r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12\s*)?\)'''
+ # [text](url) or [text](<url>) or [text](url "title")
+
+ compiled_re = re.compile("^(.*?)%s(.*?)$" % LINK_RE, re.DOTALL | re.UNICODE)
+
+ for line_index,l in enumerate(description_l):
+ if 'http' in l:
+ try:
+ match = compiled_re.match(l)
+ url = match.group(9)
+ text = match.group(2)
+ before_url = match.group(1)
+ #title = match.group(13)
+ after_url = match.group(14)
+
+ l = '%s<a href="%s">%s</a>%s'% (before_url, url, text, after_url)
+ except:
+ # not used, information
+ link_status = 'WARNING: the link is not in format [title](url)'
+ description_l[line_index] = l
+
+ description = '\n'.join(description_l)
+#:end
+
+ if is_this_task_a_group(tid) and user_interface == 'web':
+ subject = '<a href="edi_web.py?tid=%s">%s - %s</a>'%(tid,status,s)
+ else:
+ subject = '%s - %s'%(status,s)
+ fg = color_to_hex(get_forground_color(tid))
+ bg = color_to_hex(get_background_color(tid))
+ 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)
+ return r
+
+## creates a string with task id, status and string parameter
+# @return list of strings
+# @param[in] tid task id
+# @param[in] s task title string
+# @ingroup EDI_CORE
+# Displays a group depending on edi_core.list_option
+def generate_group_string_with_tid(tid,s):
+ global agenda
+
+ status = get_status(tid)
+ if (list_option == 'tids') or (list_option == 'positions'):
+ # %*s to dynamically adjust id length
+ r = '%*s - %s - %s'%(ID_LENGTH,tid,status,s)
+ if list_option == 'md':
+ tid = ' '
+ # print group title only
+ r = '#%s'%s.replace(' 0 - GROUP','')
+ if agenda:
+ r += '\n---\n# Agenda\n%s' % '\n\n'.join(agenda)
+ if list_option == 'rst':
+ tid = ' '
+ # print group title only
+ title = s.replace(' 0 - GROUP','')
+ r = '='*len(title) + '\n%s\n'%title + '='*len(title) + '\n\n'
+ if agenda:
+ r += 'Agenda\n======\n%s\n\n' % '\n'.join(agenda)
+ else:
+ r += '\n.. contents::\n\n'
+ if list_option == 'html':
+ #<tr bgcolor="#FEFFCC">
+ # <td class="subject"> <font color="#000000">t1</font></td>
+ # <td class="description"> <font color="#000000"> </font></td>
+ #</tr>
+ # display description in html
+#:read_description
+ # convert < to < and > > to ignore html tags in title line
+ s = s.replace('<','<').replace('>','>')
+ f = open('%s/description.txt'%generate_task_path(tid))
+ # remove title line, convert < to < and > > to ignore html tags
+ description_l = ['%s<br>'%i.rstrip().replace('<','<').replace('>','>') for i in f.readlines()[1:]]
+ f.close()
+
+ # convert urls to link
+ NOBRACKET = r'[^\]\[]*'
+ BRK = ( r'\[('
+ + (NOBRACKET + r'(\[')*6
+ + (NOBRACKET+ r'\])*')*6
+ + NOBRACKET + r')\]' )
+ NOIMG = r'(?<!\!)'
+ LINK_RE = NOIMG + BRK + \
+ r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12\s*)?\)'''
+ # [text](url) or [text](<url>) or [text](url "title")
+
+ compiled_re = re.compile("^(.*?)%s(.*?)$" % LINK_RE, re.DOTALL | re.UNICODE)
+
+ for line_index,l in enumerate(description_l):
+ if 'http' in l:
+ try:
+ match = compiled_re.match(l)
+ url = match.group(9)
+ text = match.group(2)
+ before_url = match.group(1)
+ #title = match.group(13)
+ after_url = match.group(14)
+
+ l = '%s<a href="%s">%s</a>%s'% (before_url, url, text, after_url)
+ except:
+ # not used, information
+ link_status = 'WARNING: the link is not in format [title](url)'
+ description_l[line_index] = l
+
+ description = '\n'.join(description_l)
+
+ subject = '%s - %s'%(status, s)
+ fg = color_to_hex(get_forground_color(tid))
+ bg = color_to_hex(get_background_color(tid))
+ 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)
+ return r
+
+## determines if task is a group
+# @return empty string or 'this task is a group'
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def is_this_task_a_group(tid):
+ # Identify if task is a group
+ is_a_group = ''
+ # list groups
+ groups = os.listdir(data_location_groups)
+ if tid in groups:
+ is_a_group = 'this task is a group'
+ return is_a_group
+
+## get task title
+# @return first line of task description
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def get_task_title(tid):
+ task_path = generate_task_path(tid)
+ f = open('%s/description.txt'%task_path)
+ r = f.readline().strip()
+ f.close()
+ return r
+
+## get creation date
+# @return array of date string and unix time
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def get_creation_date(tid):
+ r = []
+ # Figure out creation time and modification times for description and status
+ task_path = generate_task_path(tid)
+ if not os.path.exists('%s/ctime.txt'%task_path):
+ task_ctime = 'Not available'
+ r.append(task_ctime)
+ r.append(0)
+ else:
+ f = open('%s/ctime.txt'%task_path)
+ task_ctime = f.readline()
+ f.close()
+ r.append(task_ctime)
+ r.append(time.mktime(time.strptime(task_ctime)))
+
+ return r
+
+## get media
+# @return media file name
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def get_media(tid):
+ r = ['None']
+ task_path = generate_task_path(tid)
+ if os.path.exists('%s/media'%task_path):
+ files = os.listdir('%s/media'%task_path)
+ r = [files[0].split('.')[0], '%s/media/%s'%(task_path,files[0])]
+ return r
+
+## set media, one media file per task
+# @return media file name
+# @param[in] tid task id
+# @param[in] filename media file to copy to task
+# @ingroup EDI_CORE
+def set_media(tid,filename):
+ r = '%s is not a supported media file.' % filename
+
+ task_path = generate_task_path(tid)
+ if filename.strip()[-3:] == 'wav':
+#:define create_media_folder
+ if not os.path.exists('%s/media'%task_path):
+ # create media folder
+ os.mkdir('%s/media'%task_path)
+#:end
+ else:
+ # media exists, check type
+ files = os.listdir('%s/media'%task_path)
+ if not files[0][-3:] == 'wav':
+ r = '%s is not a sound.'%tid
+ return r
+ # sound file
+ shutil.copy(filename,'%s/media/sound.wav'%task_path)
+ r = 'sound.wav'
+ if filename.strip()[-3:] == 'jpg':
+#:create_media_folder
+ if not os.path.exists('%s/media'%task_path):
+ # create media folder
+ os.mkdir('%s/media'%task_path)
+ else:
+ # media exists, check type
+ files = os.listdir('%s/media'%task_path)
+ if not files[0][-3:] == 'jpg':
+ r = '%s is not an image.'%tid
+ return r
+ # image file
+ shutil.copy(filename,'%s/media/image.jpg'%task_path)
+ r = 'image.jpg'
+ edi_log('added media %s in task %s'%(filename,tid))
+ return r
+
+## get attachments
+# @return attachment file names
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def get_attachments(tid):
+ r = ['None']
+ task_path = generate_task_path(tid)
+ if os.path.exists('%s/attachments'%task_path):
+ files = os.listdir('%s/attachments'%task_path)
+ r = ['%s/attachments/%s'%(task_path,i) for i in files]
+ return r
+
+## set attachments
+# @return attachment file names
+# @param[in] tid task id
+# @param[in] array of filenames
+# @ingroup EDI_CORE
+# With *, set_attachments copies multiple files at once
+def set_attachments(tid,filenames):
+ task_path = generate_task_path(tid)
+ if not os.path.exists('%s/attachments'%task_path):
+ # create attachment folder
+ os.mkdir('%s/attachments'%task_path)
+
+ r = []
+ for fn in filenames:
+ try:
+ shutil.copy(fn,'%s/attachments/'%task_path)
+ edi_log('added attachment %s in task %s'%(fn,tid))
+ # Remove path from fn, keep only filename
+ r.append('%s/attachments/%s'%(task_path,fn.split(os.sep)[-1]))
+ except:
+ r.append('Failed to copy %s'%fn)
+ return r
+
+## sort task attributes (ls in edi cli)
+# @return sorted task list by state
+# @param[in] result from list_group
+# @ingroup EDI_CORE
+def sort_task_attributes(task_attributes):
+ r = []
+ # Keep head group on top
+ if task_attributes[0]['head'] == 'head group':
+ r.append(task_attributes[0])
+ del task_attributes[0]
+ for s in SORT_TASK_ORDER:
+ for t in task_attributes:
+ if t['status'] == TASK_STATUS[s]:
+ r.append(t)
+ return r
+
+## sort function for sorting an array of tasks by date from newest to oldest
+# @return compare result
+# @param[in] result from list_group
+# @ingroup EDI_CORE
+def sortdatefunc(x,y):
+ return cmp(y['ctime'],x['ctime'])
+
+## sort task attributes (ls in edi cli)
+# @return sorted task list by date
+# @param[in] result from list_group
+# @ingroup EDI_CORE
+def sort_task_attributes_by_date(task_attributes):
+ r = []
+ # Keep head group on top
+ if task_attributes[0]['head'] == 'head group':
+ r.append(task_attributes[0])
+ del task_attributes[0]
+
+ # -1 to exclude the empty line
+ a = task_attributes[:-1]
+ a.sort(sortdatefunc)
+ #print task_attributes
+ r += a
+ r.append(task_attributes[-1])
+ return r
+
+## list id - status - group - first line from description
+# @return list of tasks and groups title
+# @param[in] tid task id
+# @ingroup EDI_CORE
+# items in groups have the format 'ORDER_ID''TASK_ID'<br>
+# task 0 is group tittle<br>
+#<br>
+# result array elements:<br>
+# {head :string, tid :string, position :value, group :string, title :string, status :string}<br>
+# head tells if the element is a group title.<br>
+#<br>
+# example:<br>
+#fTTB1KRWfDpoSR1_ - Active - GROUP Motivational Interviewing<br>
+#62ZvFA_q0pCZFr0Y - Active - news<br>
+def list_group(tid):
+ result = []
+ # list visible groups only
+ task_status = get_status(tid)
+ if status_filters_d[task_status] == STATUS_FILTER_STATES[0]:
+ # List task titles in group
+ group_path = generate_group_path(tid)
+
+ # List groups to indicate when a task is a group
+ # Identify if task is a group
+ groups = os.listdir(data_location_groups)
+ # End Identify if task is a group
+
+ # print items in tid in order
+ for n,fn in enumerate(sorted(os.listdir(group_path))):
+ # print position in list
+ current_postion = baseconvert_to_dec(fn[:ORDER_ID_LENGTH])
+
+ # Identify if task is a group
+ # Remove order_id, keep task id only
+ group = ' '
+ if fn[ORDER_ID_LENGTH:] in groups:
+ group = 'GROUP'
+ if is_linked(fn[ORDER_ID_LENGTH:]):
+ group = ' LINK'
+ # End Identify if task is a group
+ # Get task title
+ # Remove order_id, keep task id only
+ task_path = generate_task_path(fn[ORDER_ID_LENGTH:])
+ f = open('%s/description.txt'%task_path)
+ # Remove order_id, keep task id only
+ if (not n) and (not tid == 'root'):
+ # First task is group title
+ # filter status, keep task if status filter is enabled
+ task_status = get_status(fn[ORDER_ID_LENGTH:])
+ if status_filters_d[task_status] == STATUS_FILTER_STATES[0]:
+ task_d = {}
+ task_d['head'] = 'head group'
+ task_d['tid'] = fn[ORDER_ID_LENGTH:]
+ task_d['position'] = current_postion
+ task_d['group'] = group
+ task_d['title'] = f.readline().strip()
+ task_d['status'] = get_status(task_d['tid'])
+ task_d['ctime'] = get_creation_date(task_d['tid'])[1]
+ result.append(task_d)
+ else:
+ # filter status, keep task if status filter is enabled
+ task_status = get_status(fn[ORDER_ID_LENGTH:])
+ if status_filters_d[task_status] == STATUS_FILTER_STATES[0]:
+ task_d = {}
+ task_d['head'] = 'element'
+ task_d['tid'] = fn[ORDER_ID_LENGTH:]
+ task_d['position'] = current_postion
+ task_d['group'] = group
+ task_d['title'] = f.readline().strip()
+ task_d['status'] = get_status(task_d['tid'])
+ task_d['ctime'] = get_creation_date(task_d['tid'])[1]
+ result.append(task_d)
+ f.close()
+ task_d = {}
+ task_d['head'] = 'empty line'
+ task_d['tid'] = ''
+ task_d['position'] = 0
+ task_d['group'] = ''
+ task_d['title'] = ''
+ task_d['status'] = ''
+ task_d['ctime'] = 0
+ result.append(task_d)
+ return result
+
+## lists all items in the tid group
+# @return list of tasks and groups title
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def list_tree(tid):
+ result = []
+ # walk_group is the list of groups to visit. FIFO
+ walk_group = [tid]
+ # the while loop goes through all the group that are found
+ while walk_group:
+ # list visible groups only
+ task_status = get_status(walk_group[0])
+ if status_filters_d[task_status] == STATUS_FILTER_STATES[0]:
+ # list items in first group
+ tasks = sorted(os.listdir(generate_group_path(walk_group[0])))
+ result += list_group(walk_group[0])
+
+ # add group found in first group
+ for t in tasks:
+ # Check tasks that are not title task in a group
+ if (t[ORDER_ID_LENGTH:] != walk_group[0]) and is_this_task_a_group(t[ORDER_ID_LENGTH:]):
+ walk_group.append(t[ORDER_ID_LENGTH:])
+
+ # remove first group to list items in next group
+ del walk_group[0]
+ return result
+
+## generates a random task id
+# @return new task id
+# @ingroup EDI_CORE
+def generate_id():
+ return ''.join([random.choice(ID_BASE) for _ in range(ID_LENGTH)])
+
+## converts decimal number to BASE (base 65)
+# @return string representing a number in base BASE
+# @param[in] n integer
+# @ingroup EDI_CORE
+def baseconvert(n):
+ s = ""
+ while 1:
+ r = n % BASE
+ s = ID_BASE[r] + s
+ n = n / BASE
+ if n == 0:
+ break
+
+ s = s.rjust(ORDER_ID_LENGTH, ',')
+ return s
+
+## converts BASE number to decimal
+# @return n integer
+# @param[in] n string representing a number in base BASE
+# @ingroup EDI_CORE
+def baseconvert_to_dec(n):
+ r = 0
+ power = 1
+ for digit in reversed(list(n)):
+ r += ID_BASE_STRING.find(digit) * power
+ power *= BASE
+ return r
+
+## add task to group folder in database groups
+# @param[in] tid task id
+# @param[in] group task id
+# @ingroup EDI_CORE
+# Tasks are added at the top or bottom of the list.
+def add_task_to_group_folder(tid,group):
+ # Create an entry in group
+ tasks = sorted(os.listdir(generate_group_path(group)))
+ # Add +1 to last order_id to have the task last in the list
+ if tasks:
+ if (len(tasks) == 1) or (add_top_or_bottom == 'bottom'):
+ # add tasks in bottom
+ order_id = baseconvert(baseconvert_to_dec(tasks[-1][:ORDER_ID_LENGTH])+1)
+ else:
+ # add tasks on top
+ # temporary orderid #
+ orderid_and_tid = '#' * ORDER_ID_LENGTH + tid
+ if group == 'root':
+ to_pos = 0
+ else:
+ # add new tasks after group title
+ to_pos = 1
+
+ tasks = tasks[:to_pos] + [orderid_and_tid] + tasks[to_pos:]
+
+ # Move tasks
+ path = generate_group_path(group)
+ for n,t in enumerate(tasks):
+ if n == to_pos:
+ # set orderid on top
+ order_id = baseconvert(n)
+ else:
+ os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:]))
+
+ else:
+ # start at 0 when group is empty
+ order_id = baseconvert(0)
+ tid_in_groups_path = '%s/%s%s'%(generate_group_path(group),order_id,tid)
+ # remove double slash that can come up
+ tid_in_groups_path = tid_in_groups_path.replace('//','/')
+ f = open(tid_in_groups_path,'w')
+ f.close()
+
+ # return tid_in_groups_path to easily add new tasks to group_directory_file_list and save time
+ return tid_in_groups_path
+
+## creates a task and opens vi
+# @param[in] group task id
+# @ingroup EDI_CORE
+def create_task(group):
+ # Open text editor
+ # create task in tasks folder
+#:define create_task_part1
+ tid = generate_id()
+
+ # Save text in tasks
+ task_path = generate_task_path(tid)
+ os.mkdir(task_path)
+#:end
+ os.system('%s %s/description.txt' % (editor,task_path))
+ if not os.path.isfile('%s/description.txt' % task_path):
+ # file doesnt exist, abort task creation
+ shutil.rmtree(task_path)
+ return 'no new task, the description is empty'
+
+ # Save creation time
+#:define save_task_creation_time
+ f = open('%s/ctime.txt'%task_path,'w')
+ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/ctime.txt'%task_path)
+ task_mtime = time.ctime(mtime)
+ f.write(task_mtime)
+ f.close()
+#:end
+
+ # create status, active by default
+#:define create_task_part2
+ # Create status
+ f = open('%s/status'%task_path,'w')
+ f.write(TASK_STATUS[TASK_STATUS_ACTIVE])
+ f.close()
+
+ tid_in_groups_path = add_task_to_group_folder(tid, group)
+
+ global group_directory_file_list
+ if group_directory_file_list:
+ group_directory_file_list.append(tid_in_groups_path)
+
+ # autolink
+ global autolink
+ if autolink:
+ for g in autolink:
+ if is_this_task_a_group(g):
+ # link to groups existing in current database
+ add_task_reference_to_a_group(tid,g)
+#:end
+ edi_log('created %s in group %s'%(tid,group))
+ return tid
+
+## create task and copy text file
+# @param[in] group task id
+# @param[in] text_file filename of text file to copy
+# @ingroup EDI_CORE
+def add_task(group,text_file):
+#:create_task_part1
+ tid = generate_id()
+
+ # Save text in tasks
+ task_path = generate_task_path(tid)
+ os.mkdir(task_path)
+ shutil.copy(text_file,'%s/description.txt'%task_path)
+#:save_task_creation_time
+ f = open('%s/ctime.txt'%task_path,'w')
+ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/ctime.txt'%task_path)
+ task_mtime = time.ctime(mtime)
+ f.write(task_mtime)
+ f.close()
+#:create_task_part2
+ # Create status
+ f = open('%s/status'%task_path,'w')
+ f.write(TASK_STATUS[TASK_STATUS_ACTIVE])
+ f.close()
+
+ tid_in_groups_path = add_task_to_group_folder(tid, group)
+
+ global group_directory_file_list
+ if group_directory_file_list:
+ group_directory_file_list.append(tid_in_groups_path)
+
+ # autolink
+ global autolink
+ if autolink:
+ for g in autolink:
+ if is_this_task_a_group(g):
+ # link to groups existing in current database
+ add_task_reference_to_a_group(tid,g)
+ edi_log('created %s in group %s'%(tid,group))
+ return tid
+
+## create task with description text
+# @param[in] group task id
+# @param[in] text string
+# @ingroup EDI_CORE
+def add_text(group,text):
+#:create_task_part1
+ tid = generate_id()
+
+ # Save text in tasks
+ task_path = generate_task_path(tid)
+ os.mkdir(task_path)
+ f = open('%s/description.txt'%task_path,'w')
+ f.write(text)
+ f.close()
+#:save_task_creation_time
+ f = open('%s/ctime.txt'%task_path,'w')
+ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/ctime.txt'%task_path)
+ task_mtime = time.ctime(mtime)
+ f.write(task_mtime)
+ f.close()
+#:create_task_part2
+ # Create status
+ f = open('%s/status'%task_path,'w')
+ f.write(TASK_STATUS[TASK_STATUS_ACTIVE])
+ f.close()
+
+ tid_in_groups_path = add_task_to_group_folder(tid, group)
+
+ global group_directory_file_list
+ if group_directory_file_list:
+ group_directory_file_list.append(tid_in_groups_path)
+
+ # autolink
+ global autolink
+ if autolink:
+ for g in autolink:
+ if is_this_task_a_group(g):
+ # link to groups existing in current database
+ add_task_reference_to_a_group(tid,g)
+ edi_log('created %s in group %s'%(tid,group))
+ return tid
+
+## create task and copy text file, filename is task title
+# @param[in] group task id
+# @param[in] text_file filename of text file to copy
+# @ingroup EDI_CORE
+def add_task_and_filename(group,text_file):
+ tid = add_task(group,text_file)
+
+ # Add file name on first line
+ f = open('%s/description.txt'%generate_task_path(tid))
+ des = f.readlines()
+ f.close()
+ f = open('%s/description.txt'%generate_task_path(tid),'w')
+ # save text filename only
+ f.write('%s\n'%text_file.split('/')[-1])
+ for l in des:
+ f.write(l)
+ f.close()
+
+ return tid
+
+## create many tasks from a text file
+# @param[in] group task id
+# @param[in] text_file filename of text file to copy
+# @ingroup EDI_CORE
+def add_many_tasks(group,text_file):
+ r = ''
+
+ # Copy a task to text string
+ # Remove '#' from first line, first character position
+ # Add task to database
+ f = open(text_file)
+ status = 'start'
+ text = ''
+ number_of_tasks = 0
+ for l in f.readlines():
+ if l.rstrip() == '---':
+ if status != 'start':
+ # add task to database
+ add_text(group,text)
+ number_of_tasks += 1
+ text = ''
+ status = 'start'
+ else:
+ if (status == 'start') and (l[0] == '#'):
+ l = l[1:]
+ text += l
+ status = 'writing task'
+ f.close()
+ if status == 'writing task':
+ # add task to database
+ add_text(group,text)
+ number_of_tasks += 1
+
+ edi_log('created %s tasks in group %s'%(number_of_tasks,group))
+ r = 'created %s tasks in group %s'%(number_of_tasks,group)
+ return r
+
+## create many one line tasks from a text file
+# @param[in] group task id
+# @param[in] text_file filename of text file to copy
+# @ingroup EDI_CORE
+def add_many_one_line_tasks(group,text_file):
+ r = []
+
+ f = open(text_file)
+ number_of_tasks = 0
+ for l in f.readlines():
+ add_text(group,l.strip())
+ number_of_tasks += 1
+ f.close()
+
+ edi_log('created %s tasks in group %s'%(number_of_tasks,group))
+ r = 'created %s tasks in group %s'%(number_of_tasks,group)
+ return r
+
+## create group from text file
+# @param[in] group task id
+# @param[in] text_file with group structure
+# @ingroup EDI_CORE
+def add_many_groups_from_text(group,text_file):
+ r = []
+
+ f = open(text_file)
+ number_of_tasks = 0
+ group_stack = [group]
+ for l in f.readlines():
+ # count space indents
+ l_l = l.split(' ')
+ indent = 0
+ for s in l_l:
+ if s:
+ break
+ indent += 1
+ if indent < len(group_stack)-1:
+ # remove groups from stack if same level or higher levels
+ del group_stack[-(len(group_stack)-1 - indent):]
+ l = l.strip()
+ tid = add_text(group_stack[-1],l)
+ create_group(tid)
+ group_stack.append(tid)
+ number_of_tasks += 1
+ f.close()
+
+ edi_log('created %s groups in group %s'%(number_of_tasks,group))
+ r = 'created %s groups in group %s'%(number_of_tasks,group)
+ return r
+
+## copy description to path using first line of description as filename
+# @return path and filname
+# @param[in] tid task id
+# @param[in] path destination directory for task description
+# @ingroup EDI_CORE
+def export_task_to_a_file(tid,path):
+ f = open('%s/description.txt'%generate_task_path(tid))
+ fn = f.readline().strip()
+ des = f.readlines()
+ f.close()
+ f = open('%s/%s'%(path,fn),'w')
+ for l in des:
+ f.write(l)
+ f.close()
+ r = '%s/%s'%(path,fn)
+ # remove eventual double //
+ return r.replace(os.sep*2,os.sep)
+
+## print description of task tid
+# @return list of strings
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def display_task(tid):
+ task_path = generate_task_path(tid)
+ f = open('%s/description.txt'%task_path)
+ description = f.readlines()
+ f.close()
+
+ # print tid, status and first line
+ description[0] = generate_task_string_with_tid(tid,description[0])
+ return description
+
+## find group containing task tid in groups folder
+# @return tid
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def find_group_containing_task(tid):
+ if tid == 'root':
+ # root has not parent group, return root
+ return tid
+#:define walk_groups
+ global data_location_groups
+ # reuse previously created group list, to save time
+ global group_directory_file_list
+ if not group_directory_file_list:
+ group_directory_file_list = ffind(data_location_groups)
+ groups_and_tasks = group_directory_file_list
+ for t in groups_and_tasks:
+ if (tid == t[-ID_LENGTH:]) and (tid != t.split('/')[-2]):
+ group = t.split('/')[-2]
+#:end
+ try:
+ a = group
+ except:
+ print '\n\nEDI_ERROR: Database inconsistent - run: find %s|grep %s and remove reference.\n'%(data_location, tid)
+ group = 'error'
+ return group
+
+## find group containing task tid in groups folder and print
+# @return list of strings
+# @param[in] tid task id
+# @ingroup EDI_CORE
+# Shows path in tree and title for each group in path
+def show_group_for_task(tid):
+ global data_location_tree
+ r = []
+
+ # set group string to be displayed on first line
+ group_s = ' '
+ if is_this_task_a_group(tid):
+ group_s = 'GROUP'
+ if is_linked(tid):
+ group_s = ' LINK'
+
+#:walk_groups
+ global data_location_groups
+ # reuse previously created group list, to save time
+ global group_directory_file_list
+ if not group_directory_file_list:
+ group_directory_file_list = ffind(data_location_groups)
+ groups_and_tasks = group_directory_file_list
+ for t in groups_and_tasks:
+ if (tid == t[-ID_LENGTH:]) and (tid != t.split('/')[-2]):
+ group = t.split('/')[-2]
+ tree_path = find_group_in_tree(group)
+ # p is data_location folder/tree
+ p_l = data_location_tree.split('/')[-2:]
+ p = '/'.join(p_l)
+ # if empty then command is run in tree root
+ if tree_path.split(p)[-1]:
+ # print path of tids: tid/tid...
+ r.append(tree_path.split(p)[-1][1:])
+ group_titles_in_path = []
+ for g in tree_path.split(p)[-1][1:].split('/'):
+ group_titles_in_path.append(get_task_title(g))
+ # print title/title...
+ r.append('/'.join(group_titles_in_path))
+ r.append(generate_group_string_with_tid(group,get_task_title(group)))
+ r.append('')
+
+ # Print media and attachments
+ # media type
+ media = get_media(tid)[0]
+ # attachment list
+ attachments = get_attachments(tid)
+
+ # Print task, colors, group list
+ color = get_forground_color(tid)
+ fc = '%d,%d,%d,%d' % (color[0],color[1],color[2],color[3])
+ color = get_background_color(tid)
+ bc = '%d,%d,%d,%d' % (color[0],color[1],color[2],color[3])
+ # Figure out creation time and modification times for description and status
+ task_ctime = get_creation_date(tid)[0]
+
+ task_path = generate_task_path(tid)
+ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/description.txt'%task_path)
+ description_mtime = time.ctime(mtime)
+ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/status'%task_path)
+ status_mtime = time.ctime(mtime)
+ 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
+ return r
+
+## find group in tree folder
+# @return path_in_tree
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def find_group_in_tree(group):
+ if group == 'root':
+ path_in_tree = data_location_tree
+ else:
+ path_in_tree = ''
+ # list all group paths in tree
+ f = os.popen('find %s'%data_location_tree)
+ groups = [i.strip() for i in f.readlines()]
+ f.close()
+ # remove data_location_tree from paths
+ del groups[0]
+ # find the group in group paths
+ for g in groups:
+ if g.split('/')[-1] == group:
+ path_in_tree = g
+ return path_in_tree
+
+## Determines if a task has multiple references
+# @return integer 0 or 1
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def is_linked(tid):
+ status = 0
+ task_linked_groups_path = '%s/groups/' % generate_task_path(tid)
+ # check if tid/groups exists
+ if os.path.exists(task_linked_groups_path):
+ groups = os.listdir(task_linked_groups_path)
+ # check if task is linked to more than 1 group
+ if len(groups) > 1:
+ status = 1
+ return status
+
+## convert task to group
+# @return list of stings when there is an error
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def create_group(tid):
+ r = []
+ # convert task to group only when task is not linked
+ if is_linked(tid):
+ r.append('Converting linked task to group removes links. The task groups are:')
+ r += show_group_for_task(tid)
+
+ # delete tid/groups because groups are not linked
+ task_linked_groups_path = '%s/groups/' % generate_task_path(tid)
+ groups = os.listdir(task_linked_groups_path)
+ # remove all links except for the first group
+ for g in groups[1:]:
+ delete_linked_task(g,tid)
+ if os.path.exists(task_linked_groups_path):
+ shutil.rmtree(task_linked_groups_path)
+ r.append('Created group %s in %s'%(tid,groups[0]))
+ # create new group in groups folder
+ os.mkdir('%s/%s'%(data_location_groups,tid))
+ # First task in group is group title task
+ order_id = baseconvert(0)
+ f = open('%s/%s%s'%(generate_group_path(tid),order_id,tid),'w')
+ f.close()
+
+ # Add group in tree
+ # update group list
+ global group_directory_file_list
+ group_directory_file_list = []
+ group = find_group_containing_task(tid)
+ if group == 'root':
+ os.mkdir('%s/%s'%(data_location_tree,tid))
+ else:
+ os.mkdir('%s/%s'%(find_group_in_tree(group),tid))
+
+ # Change status active to void by default
+ # To avoid filtering groups
+ if get_status(tid) == TASK_STATUS[TASK_STATUS_ACTIVE]:
+ # set status to void
+ set_status(tid,TASK_STATUS_VOID)
+ edi_log('created group %s'%tid)
+ return r
+
+## convert group to task
+# @param[in] group group to convert to task
+# @ingroup EDI_CORE
+def convert_group_to_task(group):
+ r = []
+
+ # list all tasks (data_location_groups/GROUP/'ORDER_ID''TASK_ID') in groups folder
+ groups_and_tasks = ffind(data_location_groups)
+
+ convert_group_status = 'delete'
+ for t2 in groups_and_tasks:
+ # search a task in group that is not tid
+ if ('%s/'%group in t2) and (group not in t2.split('/')[-1]):
+ convert_group_status = 'There is another task in the group, keep group.'
+ r.append(convert_group_status)
+ if (convert_group_status == 'delete') and (not 'root' in group):
+ # Delete group title and group folder
+ os.remove('%s/%s%s'%(generate_group_path(group),baseconvert(0),group))
+ os.rmdir(generate_group_path(group))
+ # Delete group in tree
+ if find_group_in_tree(group):
+ shutil.rmtree(find_group_in_tree(group))
+ # change state from void to active
+ set_status(group,TASK_STATUS_ACTIVE)
+ edi_log('converted group %s to task'%group)
+ return r
+
+## delete task tid in all groups
+# @return group id
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def delete_task(tid):
+ # save original tid parameter for log
+ tid_param = tid
+ # Delete task in tasks
+ shutil.rmtree(generate_task_path(tid))
+
+ # Delete task reference in groups
+ # list all tasks (data_location_groups/GROUP/'ORDER_ID''TASK_ID') in groups folder
+ groups_and_tasks = ffind(data_location_groups)
+
+ # Identify if task is a group
+ is_a_group = is_this_task_a_group(tid)
+
+ # Return group task, it changes when first task is deleted
+ group_id = ''
+ # list groups to reorder at the end
+ reorder_groups = []
+ for t in groups_and_tasks:
+ # Delete reference in upper group, not the title task of the group
+ if (tid == t[-ID_LENGTH:]) and (tid != t.split('/')[-2]):
+ # group is the group for task tid
+ group = t.split('/')[-2]
+ group_id = group
+ # Delete task reference in group
+ os.remove(t)
+ reorder_groups.append(group_id)
+ if is_a_group:
+ # First task becomes a group, add a reference in group group
+ # list tasks in order in tid group
+ group_tasks = sorted(os.listdir(generate_group_path(tid)))
+ # Delete emtpy group or first task in group becomes the group title.
+ if len(group_tasks) == 1:
+ shutil.rmtree(generate_group_path(tid))
+ # Delete group in tree
+ # when a group is deleted, subgroups are automatically deleted in the tree
+ if find_group_in_tree(tid):
+ shutil.rmtree(find_group_in_tree(tid))
+ else:
+ # Remove order_id, keep task id only
+ first_task = group_tasks[1][ORDER_ID_LENGTH:]
+ group_id = first_task
+ # Create an entry in group at the same position as tid had
+ order_id = t.split('/')[-1][:ORDER_ID_LENGTH]
+ f = open('%s/%s%s'%(generate_group_path(group),order_id,first_task),'w')
+ f.close()
+
+ # Delete group task of group of more then 2 tasks, first task becomes a group
+ # delete orderidtaskid
+ os.remove('%s/%s'%(generate_group_path(tid),group_tasks[0]))
+ os.rename('%s/%s'%(generate_group_path(tid),group_tasks[1]),'%s/%s%s'%(generate_group_path(tid),baseconvert(0),first_task))
+ # reorder tasks to remove gap between group title task and first task
+ path = generate_group_path(tid)
+ tasks = sorted(os.listdir(path))
+ for n,t in enumerate(tasks):
+ os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:]))
+ # rename group in groups folder
+ os.rename(generate_group_path(tid),generate_group_path(first_task))
+ # rename group in tree folder
+ group_tree_path = find_group_in_tree(tid)
+ group_tree_path_l = group_tree_path.split('/')
+ group_tree_path_l[-1] = first_task
+ new_group_tree_path = '/'.join(group_tree_path_l)
+ os.rename(group_tree_path,new_group_tree_path)
+
+ # reorder tasks to remove gaps in reorder_groups
+ for tid in reorder_groups:
+ path = generate_group_path(tid)
+ tasks = sorted(os.listdir(path))
+ for n,t in enumerate(tasks):
+ os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:]))
+
+ # Return group task
+ edi_log('deleted %s in %s'%(tid_param,' '.join(reorder_groups)))
+ return group_id
+
+## Delete task only if it is linked in one group
+# @return group id
+# @param[in] group task id
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def delete_linked_task(group,tid):
+ group_id = group
+ if not is_linked(tid):
+ group_id = delete_task(tid)
+ else:
+ # Delete task reference in group
+ # find task in group: ORDER_IDTASK_ID
+ tasks = os.listdir(generate_group_path(group))
+ for t in tasks:
+ if t[ORDER_ID_LENGTH:] == tid:
+ os.remove('%s%s' % (generate_group_path(group), t))
+
+ # delete group in tid/groups
+ os.remove('%s/groups/%s' % (generate_task_path(tid), group))
+
+ # reorder tasks to remove gaps in group
+ path = generate_group_path(group)
+ tasks = os.listdir(path)
+ for n,t in enumerate(tasks):
+ os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:]))
+
+ edi_log('deleted %s in group %s'%(tid,group))
+
+ return group_id
+
+## delete group tid
+# @return group id
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def delete_group(tid):
+ # Delete tasks in group
+ group_tasks = sorted(os.listdir(generate_group_path(tid)))
+ if (len(group_tasks) == 0) and (tid == 'root'):
+ # the database is already empty
+ return tid
+ if tid != 'root':
+ # root group doesnt have a title
+ # Remove group title from the loop to delete tasks only and then group
+ del group_tasks[0]
+
+ # Delete tasks and groups recursively
+ for t in group_tasks:
+ # Remove order_id, keep task id only
+ oid = t[ORDER_ID_LENGTH:]
+ if not is_this_task_a_group(oid):
+ # delete tasks that are linked only once
+ delete_linked_task(tid, oid)
+ else:
+ delete_group(oid)
+
+ if tid != 'root':
+ # never delete root group
+ return delete_task(tid)
+ else:
+ # return root and keep root task
+ return tid
+
+## edit task with vi
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def edit_task(tid):
+ os.system('%s %s/description.txt' % (editor, generate_task_path(tid)))
+ edi_log('edited %s'%tid)
+
+## move task from group at at_pos to to_pos and reorder
+# @return list of stings when there is an error
+# @param[in] group task id
+# @param[in] at_pos selected position
+# @param[in] to_pos insert position
+# @ingroup EDI_CORE
+def change_task_order(group,at_pos,to_pos):
+ # save original group parameter for log
+ group_param = group
+ # List tasks in group
+ r = []
+ path = generate_group_path(group)
+ tasks = sorted(os.listdir(path))
+
+ # Verify position
+ # Get task
+ try:
+ orderid_and_tid = tasks[at_pos]
+ except:
+ r.append('%d is an invalid position.'%at_pos)
+ return r
+ if to_pos == 0:
+ tid = orderid_and_tid[ORDER_ID_LENGTH:]
+ # do not move linked tasks to position 0
+ if is_linked(tid):
+ r.append('Converting linked task to group removes links. The task groups are:')
+ r += show_group_for_task(tid)
+
+ # delete tid/groups because groups are not linked
+ task_linked_groups_path = '%s/groups/' % generate_task_path(tid)
+ groups = os.listdir(task_linked_groups_path)
+ # remove all links except for the first group
+ for g in groups:
+ if not g == group:
+ delete_linked_task(g,tid)
+ if os.path.exists(task_linked_groups_path):
+ shutil.rmtree(task_linked_groups_path)
+ parent_group = find_group_containing_task(group)
+ r.append('Created group %s in %s'%(tid,parent_group))
+ # do not move groups to position 0
+ if is_this_task_a_group(tid):
+ r.append('Having a group in group title is not supported.')
+ return r
+ # Insert task at to_pos
+ if to_pos > at_pos:
+ # +1 because at_pos reference will be deleted and will shift to_pos reference
+ to_pos += 1
+ tasks = tasks[:to_pos] + [orderid_and_tid] + tasks[to_pos:]
+ # Delete task at at_pos
+ del tasks[at_pos]
+ else:
+ # rename group title, when to_pos in 0
+ if (to_pos == 0) and (not group == 'root'):
+ to_pos_tid = tasks[to_pos][ORDER_ID_LENGTH:]
+
+ tasks = tasks[:to_pos] + [orderid_and_tid] + tasks[to_pos:]
+ # Delete task at at_pos+1 because the new position is before at_pos
+ # at_pos was shifted when to_pos reference was added above
+ del tasks[at_pos+1]
+
+#:define reorder_tasks
+ # Move tasks
+ for n,t in enumerate(tasks):
+ os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:]))
+#:end
+
+ # rename group title, when to_pos in 0
+ if (to_pos == 0) and (not group == 'root'):
+ # rename group in parent group
+ # group is tid for parent group and to_pos_tid is equal to group input parameter because to_pos is 0
+ group = find_group_containing_task(group)
+ # to_pos_orderid_and_tid is group in parent group
+ parent_group_tasks = os.listdir(generate_group_path(group))
+ for t in parent_group_tasks:
+ if t[ORDER_ID_LENGTH:] == to_pos_tid:
+ to_pos_orderid_and_tid = t
+ # remove order_id, keep task id only for new group
+ group_id = orderid_and_tid[ORDER_ID_LENGTH:]
+ # create an entry in parent group at the same position as to_pos tid had
+ order_id = to_pos_orderid_and_tid[:ORDER_ID_LENGTH]
+ f = open('%s/%s%s'%(generate_group_path(group),order_id,group_id),'w')
+ f.close()
+
+ # delete group task of group
+ # delete orderidtaskid
+ os.remove('%s/%s'%(generate_group_path(group),to_pos_orderid_and_tid))
+
+ # rename group in groups folder
+ os.rename(generate_group_path(to_pos_tid),generate_group_path(group_id))
+ # rename group in tree folder
+ group_tree_path = find_group_in_tree(to_pos_tid)
+ group_tree_path_l = group_tree_path.split('/')
+ group_tree_path_l[-1] = group_id
+ new_group_tree_path = '/'.join(group_tree_path_l)
+ os.rename(group_tree_path,new_group_tree_path)
+
+ # rename group in linked task and former linked task with a 'groups' folder in tasks database folder
+ group_tasks = os.listdir(generate_group_path(group_id))
+ for group_task in group_tasks:
+ if os.path.exists('%s/groups' % generate_task_path(group_task[ORDER_ID_LENGTH:])):
+ # search for to_pos_tid (group input parameter) in task path groups folder
+ link_groups = os.listdir('%s/groups' % generate_task_path(group_task[ORDER_ID_LENGTH:]))
+ for lg in link_groups:
+ if to_pos_tid == lg:
+ # rename group to new group id since the title changed
+ 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))
+
+ edi_log('changed task order %s to %s in group %s'%(at_pos, to_pos, group_param))
+ return r
+
+
+## move task to group
+# @return tid
+# @param[in] tgroup task id, select a task in this group
+# @param[in] tid task id
+# @param[in] group task id, destination group
+# @ingroup EDI_CORE
+def move_task_to_a_group(tgroup,tid,group):
+
+ # find task tid in tgroup and remove task
+ path = generate_group_path(tgroup)
+ tasks = sorted(os.listdir(path))
+ task_in_group = ''
+ for n,t in enumerate(tasks):
+ # remove task from tasks to reorder tgroup
+ if t[ORDER_ID_LENGTH:] == tid:
+ task_in_group = t
+ del tasks[n]
+ break
+ if not task_in_group:
+ return '%s not found in %s'%(tid,tgroup)
+
+ # Move group in tree
+ if is_this_task_a_group(tid):
+ if find_group_in_tree(tid)[:-ID_LENGTH-1] == find_group_in_tree(group):
+ # prevent moving a group on itself, in tree and moving group title position to parent group
+ return tid
+ shutil.move(find_group_in_tree(tid),find_group_in_tree(group))
+
+ # Remove task in source group
+ os.remove('%s/%s'%(path,task_in_group))
+
+#:reorder_tasks
+ # Move tasks
+ for n,t in enumerate(tasks):
+ os.rename('%s/%s'%(path,t),'%s/%s%s'%(path,baseconvert(n),t[ORDER_ID_LENGTH:]))
+ # check that tid is not already linked in destination group
+ if is_linked(tid):
+ link_groups = os.listdir('%s/groups/' % generate_task_path(tid))
+ if group in link_groups:
+ # removed task reference from tgroup. Remove tgroup reference in task. There is already a tid reference in group, nothing more to do
+ os.remove('%s/groups/%s' % (generate_task_path(tid), tgroup))
+ return tid
+
+ # Create reference in destination group
+ add_task_to_group_folder(tid, group)
+
+ # linked tasks, remove source group and add destination group
+ if is_linked(tid):
+ # remove source tgroup from tid/groups
+ os.remove('%s/groups/%s' % (generate_task_path(tid), tgroup))
+ f = open('%s/groups/%s'%(generate_task_path(tid), group),'w')
+ f.close()
+
+ edi_log('moved %s in group %s to group %s'%(tid,tgroup,group))
+ return tid
+
+## copy task to group with new tid
+# @return new tid
+# @param[in] tid task id
+# @param[in] group task id, destination group
+# @ingroup EDI_CORE
+def copy_task_to_a_group(tid,group):
+ # Generate new tid
+ newtid = generate_id()
+ # Copy task in tasks
+ shutil.copytree(generate_task_path(tid),generate_task_path(newtid))
+ # delete tid/groups because new task is not linked
+ task_linked_groups_path = '%s/groups/' % generate_task_path(newtid)
+ if os.path.exists(task_linked_groups_path):
+ shutil.rmtree(task_linked_groups_path)
+ # Copy group in groups and in tree
+ if is_this_task_a_group(tid):
+ # create new group
+ shutil.copytree(generate_group_path(tid),generate_group_path(newtid))
+ # Change group title task to newtid
+ shutil.move('%s/%s%s'%(generate_group_path(newtid),baseconvert(0),tid),'%s/%s%s'%(generate_group_path(newtid),baseconvert(0),newtid))
+
+ # delete tasks to be recreated with new tid
+ tasks = sorted(os.listdir(generate_group_path(newtid)))
+ del tasks[0]
+ for t in tasks:
+ os.remove('%s/%s'%(generate_group_path(newtid),t))
+
+ # Add group in tree
+ if group == 'root':
+ os.mkdir('%s/%s'%(data_location_tree,newtid))
+ else:
+ os.mkdir('%s/%s'%(find_group_in_tree(group),newtid))
+
+
+ # Add reference in group
+ tmp_tid = tid
+ tid = newtid
+ # Create reference in destination group
+ add_task_to_group_folder(tid, group)
+ tid = tmp_tid
+
+ if is_this_task_a_group(tid):
+ # walk in group
+ # list items in group
+ tasks = sorted(os.listdir(generate_group_path(tid)))
+
+ # add group found in first group
+ for t in tasks:
+ # Check tasks that are not title task in a group
+ if t[ORDER_ID_LENGTH:] != tid:
+ # copy_task_to_a_group recursively
+ copy_task_to_a_group(t[ORDER_ID_LENGTH:],newtid)
+ edi_log('copied %s to group %s, created %s'%(tid,group,newtid))
+ return newtid
+
+## create task path in database using database name
+# @return path to task in tasks
+# @param[in] tid task id
+# @param[in] location database name in data section of easydoneit.ini
+# @ingroup EDI_CORE
+def generate_task_path_in_database(tid,location):
+ global selected
+ global selected_path
+
+ z = dict(zip(selected, selected_path))
+ location_tasks = '%s/tasks'%z[location]
+ return '%s/%s'%(location_tasks,tid)
+
+## create group path in database using database name
+# @return path to group in groups
+# @param[in] tid task id
+# @param[in] location database name in data section of easydoneit.ini
+# @ingroup EDI_CORE
+def generate_group_path_in_database(tid,location):
+ global selected
+ global selected_path
+
+ z = dict(zip(selected, selected_path))
+ location_groups = '%s/groups'%z[location]
+ return '%s/%s/'%(location_groups,tid)
+
+## find group in tree folder in database
+# @return path to group in tree
+# @param[in] group task id
+# @param[in] location database name in data section of easydoneit.ini
+# @ingroup EDI_CORE
+def find_group_in_tree_in_database(group,location):
+ global selected
+ global selected_path
+
+ z = dict(zip(selected, selected_path))
+ location_tree = '%s/tree'%z[location]
+
+ if group == 'root':
+ path_in_tree = location_tree
+ else:
+ path_in_tree = ''
+ # list all group paths in tree
+ f = os.popen('find %s'%location_tree)
+ groups = [i.strip() for i in f.readlines()]
+ f.close()
+ # remove data_location_tree from paths
+ del groups[0]
+ # find the group in group paths
+ for g in groups:
+ if g.split('/')[-1] == group:
+ path_in_tree = g
+ return path_in_tree
+
+## add task with new tid in selected database
+# @param[in] tid task id
+# @param[in] group task id
+# @param[in] location database name in data section of easydoneit.ini
+# @ingroup EDI_CORE
+def add_task_to_group_folder_in_database(tid,group,location):
+ # Create an entry in group
+ tasks = sorted(os.listdir(generate_group_path_in_database(group,location)))
+ # Add +1 to last order_id to have the task last in the list
+ if tasks:
+ order_id = baseconvert(baseconvert_to_dec(tasks[-1][:ORDER_ID_LENGTH])+1)
+ else:
+ # start at 0 when group is empty
+ order_id = baseconvert(0)
+ f = open('%s/%s%s'%(generate_group_path_in_database(group,location),order_id,tid),'w')
+ f.close()
+
+## copy task to group in selected database with new tid
+# @return new tid
+# @param[in] tid task id
+# @param[in] group task id
+# @param[in] location database name in data section of easydoneit.ini
+# @ingroup EDI_CORE
+def copy_task_to_database(tid,location,group):
+ # Generate new tid
+ newtid = generate_id()
+ # Copy task in tasks
+ shutil.copytree(generate_task_path(tid),generate_task_path_in_database(newtid,location))
+ # delete tid/groups because new task is not linked
+ task_linked_groups_path = '%s/groups/' % generate_task_path_in_database(newtid,location)
+ if os.path.exists(task_linked_groups_path):
+ shutil.rmtree(task_linked_groups_path)
+ # Copy group in groups and in tree
+ if is_this_task_a_group(tid):
+ # create new group
+ shutil.copytree(generate_group_path(tid),generate_group_path_in_database(newtid,location))
+ # Change group title task to newtid
+ 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))
+
+ # delete tasks to be recreated with new tid
+ tasks = sorted(os.listdir(generate_group_path_in_database(newtid,location)))
+ del tasks[0]
+ for t in tasks:
+ os.remove('%s/%s'%(generate_group_path_in_database(newtid,location),t))
+
+ # Add group in tree
+ global selected
+ global selected_path
+
+ z = dict(zip(selected, selected_path))
+ location_tree = '%s/tree'%z[location]
+ if group == 'root':
+ os.mkdir('%s/%s'%(location_tree,newtid))
+ else:
+ os.mkdir('%s/%s'%(find_group_in_tree_in_database(group,location),newtid))
+
+
+ # Add reference in group
+ # Create reference in destination group
+ add_task_to_group_folder_in_database(newtid, group, location)
+
+ if is_this_task_a_group(tid):
+ # walk in group
+ # list items in group
+ tasks = sorted(os.listdir(generate_group_path(tid)))
+
+ # add group found in first group
+ for t in tasks:
+ # Check tasks that are not title task in a group
+ if t[ORDER_ID_LENGTH:] != tid:
+ # copy_task_to_database recursively
+ copy_task_to_database(t[ORDER_ID_LENGTH:],location,newtid)
+ return newtid
+
+## move task to database/group
+# @param[in] tgroup group id for task tid
+# @param[in] tid task id
+# @param[in] location database name in data section of easydoneit.ini
+# @param[in] group destination group id
+# @ingroup EDI_CORE
+# copy and delete
+def move_task_to_a_group_to_database(tgroup,tid,location,group):
+ newtid = copy_task_to_database(tid,location,group)
+ if is_this_task_a_group(tid):
+ delete_group(tid)
+ else:
+ delete_linked_task(tgroup,tid)
+ return newtid
+
+## add reference to tid in group
+# @param[in] tid task id
+# @param[in] group destination group id
+# @ingroup EDI_CORE
+# Used in edi ln to link tasks
+def add_task_reference_to_a_group(tid,group):
+ prints = []
+ if is_this_task_a_group(tid):
+ prints.append('Select a task to link instead of a group')
+ else:
+ # add group to task folder
+ task_path = generate_task_path(tid)
+ if not os.path.exists('%s/groups/' % task_path):
+ os.mkdir('%s/groups/' % task_path)
+
+ # add first group when task is linked to tid/groups/
+ first_group = find_group_containing_task(tid)
+ f = open('%s/groups/%s'%(task_path,first_group),'w')
+ f.close()
+ f = open('%s/groups/%s'%(task_path,group),'w')
+ f.close()
+
+ # add task to group
+ add_task_to_group_folder(tid, group)
+
+ edi_log('linked %s to group %s'%(tid,group))
+ return prints
+
+## set status for tid
+# @param[in] tid task id
+# @param[in] status_number index in edi_core.TASK_STATUS
+# @ingroup EDI_CORE
+def set_status(tid,status_number):
+ # Change status
+ f = open('%s/status'%generate_task_path(tid),'w')
+ f.write(TASK_STATUS[status_number])
+ f.close()
+ edi_log('set status for %s to %s'%(tid,TASK_STATUS[status_number].strip()))
+
+## set all tasks in group to active
+# @param[in] tid task id
+# @ingroup EDI_CORE
+def reset_group_status(tid):
+ group_tasks = os.listdir(generate_group_path(tid))
+ for t in group_tasks:
+ 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])):
+ set_status(t[ORDER_ID_LENGTH:],TASK_STATUS_ACTIVE)
+ edi_log('reset group %s'%tid)
+
+## search string in tasks folder
+# @param[in] search query string
+# @ingroup EDI_CORE
+def search_string(search):
+ global data_location_tasks
+ global status_filters_d
+ global STATUS_FILTER_STATES
+
+ tasks = sorted(os.listdir(data_location_tasks))
+
+ # search in descriptions only
+ all_grep_r = []
+ for tid in tasks:
+ if tid != 'root':
+ # search in visible tasks only (filter enable status)
+ task_status = get_status(tid)
+ if status_filters_d[task_status] == STATUS_FILTER_STATES[0]:
+ f = os.popen('grep -i -R "%s" %s/description.txt'%(search,generate_task_path(tid)))
+ # add tid and filename to results
+ grep_r = ['/%s/description.txt:%s'%(tid,i) for i in f.readlines()]
+ f.close()
+ all_grep_r += grep_r
+
+ grep_r = all_grep_r
+
+ # r lists all hits
+ r = []
+ for l in grep_r:
+ # replace filename with tid and title (first line in file)
+ # read first line in file
+ l_l = l.split(':')
+ hit_filename = l_l[0]
+ f = open('%s%s'%(data_location_tasks,hit_filename))
+ title = f.readline().strip()
+ f.close()
+ # set tid
+ tid = hit_filename.split('/')[1]
+ group = ' '
+ if is_this_task_a_group(tid):
+ group = 'GROUP'
+ if is_linked(tid):
+ group = ' LINK'
+ l_l[0] = '%s %s - %s'%(tid,group,title)
+ l = ':'.join(l_l)
+ r.append(l)
+
+ return r
+
+## search string in group
+# @param[in] group task id
+# @param[in] search query string
+# @ingroup EDI_CORE
+def search_string_in_group(group,search):
+
+ tasks = sorted(os.listdir(generate_group_path(group)))
+
+ # r lists all hits
+ r = []
+ for orderidtid in tasks:
+ # tid for current task in group
+ tid = orderidtid[ORDER_ID_LENGTH:]
+ # search in visible tasks only (filter enable status)
+ task_status = get_status(tid)
+ grep_r = []
+ if status_filters_d[task_status] == STATUS_FILTER_STATES[0]:
+ f = os.popen('grep -i -R "%s" %s/description.txt'%(search,generate_task_path(tid)))
+ # add tid and filename to results
+ grep_r = ['/%s/description.txt:%s'%(tid,i) for i in f.readlines()]
+ f.close()
+
+ for l in grep_r:
+ # replace filename with tid and title (first line in file)
+ # read first line in file
+ l_l = l.split(':')
+ hit_filename = l_l[0]
+ f = open('%s%s'%(data_location_tasks,hit_filename))
+ title = f.readline().strip()
+ f.close()
+ # set tid
+ tid = hit_filename.split('/')[1]
+ group = ' '
+ if is_this_task_a_group(tid):
+ group = 'GROUP'
+ if is_linked(tid):
+ group = ' LINK'
+ l_l[0] = '%s %s - %s'%(tid,group,title)
+ l = ':'.join(l_l)
+ r.append(l)
+
+ return r
+
+## search string in tree
+# @param[in] group task id
+# @param[in] search query string
+# @ingroup EDI_CORE
+def search_string_in_tree(group,search):
+ # walk_group is the list of groups to visit. FIFO
+ r = []
+ walk_group = [group]
+ # the while loop goes through all the group that are found
+ while walk_group:
+ # list items in first group
+ tasks = sorted(os.listdir(generate_group_path(walk_group[0])))
+ r += search_string_in_group(walk_group[0],search)
+
+ # add group found in first group
+ for t in tasks:
+ # Check tasks that are not title task in a group
+ if (t[ORDER_ID_LENGTH:] != walk_group[0]) and is_this_task_a_group(t[ORDER_ID_LENGTH:]):
+ walk_group.append(t[ORDER_ID_LENGTH:])
+
+ # remove first group to list items in next group
+ del walk_group[0]
+ return r
+
+## show tree
+# @ingroup EDI_CORE
+# print all trees: group tids and titles
+def show_tree():
+ r = []
+ f = os.popen('cd %s;find .'%data_location_tree)
+ # remove first empty line
+ tidtree = f.readlines() [1:]
+ f.close()
+
+ # remove './' from path
+ tidtree = [i[2:] for i in tidtree]
+
+ # print titles path\n group tid path\n\n
+ for l in tidtree:
+ # find title for group tids
+ group_titles_in_path = []
+ for g in l.strip().split(os.sep):
+ group_titles_in_path.append(get_task_title(g))
+ if user_interface == 'web':
+ # convert / to - to be able to create links correctly
+ group_titles_in_path = [i.replace('/', '-') for i in group_titles_in_path]
+ # create string title/title...
+ group_titles_in_path_s = '/'.join(group_titles_in_path)
+ r.append('%s\n'%group_titles_in_path_s)
+ r.append('%s\n'%l)
+
+ return r
+
+## group statistics
+# @ingroup EDI_CORE
+# print statistics for a group recursively
+def group_statistics(group):
+ global stats
+ global stats_total
+ global stats_creation_dates
+ global stats_overtime
+
+ # compute total number of tasks in group
+ path = generate_group_path(group)
+
+ tasks = []
+ # remove group title
+ for i in os.listdir(path):
+ if not baseconvert(0) in i[:ORDER_ID_LENGTH]:
+ tasks.append(i[ORDER_ID_LENGTH:])
+
+ stats_total += len(tasks)
+
+ # compute number of tasks in each state, groups and links
+ for tid in tasks:
+ task_status = get_status(tid)
+ stats[task_status] += 1
+ if is_this_task_a_group(tid):
+ stats[' Group'] += 1
+ group_statistics(tid)
+ if is_linked(tid):
+ stats[' Linked'] += 1
+
+ #stats_creation_dates
+ # Figure out creation time and modification time for description
+ task_ctime = get_creation_date(tid)[1]
+
+ task_path = generate_task_path(tid)
+ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/description.txt'%task_path)
+ # 'if' below to be compatible with first database format
+ if task_ctime == 0:
+ # ctime not available
+ task_ctime = mtime
+ stats_creation_dates.append(task_ctime)
+
+ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/status'%task_path)
+
+ cdate = time.strftime("%Y-%m-%d", time.localtime(task_ctime))
+ sdate = time.strftime("%Y-%m-%d", time.localtime(mtime))
+ if not stats_overtime.has_key(cdate):
+ # initialize date dict
+ stats_overtime[cdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))]))
+ stats_overtime[cdate]['Creation'] += 1
+ if not stats_overtime.has_key(sdate):
+ # initialize date dict
+ stats_overtime[sdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))]))
+ stats_overtime[sdate][task_status] += 1
+ #end
+
+## statistics
+# @ingroup EDI_CORE
+# print statistics for a group or a database
+# compute speed
+def statistics(group):
+ global stats
+ global stats_total
+ global stats_creation_dates
+ global stats_overtime
+ r = []
+
+ # initialize stats dictionary
+ stat_keys = [TASK_STATUS[i] for i in range(len(TASK_STATUS))]
+ stat_keys.append(' Group')
+ stat_keys.append(' Linked')
+ state_amounts = [0 for i in range(len(stat_keys))]
+ stats = dict(zip(stat_keys,state_amounts))
+
+ if group == 'for database':
+ # compute total number of tasks, excluding root
+ path = data_location_tasks
+ tasks = []
+ for i in os.listdir(path):
+ if i != 'root':
+ tasks.append(i)
+
+ # compute number of tasks in each state, groups and links
+ for tid in tasks:
+ task_status = get_status(tid)
+ stats[task_status] += 1
+ if is_this_task_a_group(tid):
+ stats[' Group'] += 1
+ if is_linked(tid):
+ stats[' Linked'] += 1
+
+#:define stats_creation_dates
+ # Figure out creation time and modification time for description
+ task_ctime = get_creation_date(tid)[1]
+
+ task_path = generate_task_path(tid)
+ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/description.txt'%task_path)
+ if task_ctime == 0:
+ # ctime not available
+ task_ctime = mtime
+ stats_creation_dates.append(task_ctime)
+
+ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/status'%task_path)
+
+ cdate = time.strftime("%Y-%m-%d", time.localtime(task_ctime))
+ sdate = time.strftime("%Y-%m-%d", time.localtime(mtime))
+ if not stats_overtime.has_key(cdate):
+ # initialize date dict
+ stats_overtime[cdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))]))
+ stats_overtime[cdate]['Creation'] += 1
+ if not stats_overtime.has_key(sdate):
+ # initialize date dict
+ stats_overtime[sdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))]))
+ stats_overtime[sdate][task_status] += 1
+#:end
+
+ stats_total = len(tasks)
+ else:
+ # compute total number of tasks in group
+ path = generate_group_path(group)
+
+ tasks = []
+ for i in os.listdir(path):
+ if group == 'root':
+ tasks.append(i[ORDER_ID_LENGTH:])
+ else:
+ # the group task is not counted in the statistics
+ if not baseconvert(0) in i[:ORDER_ID_LENGTH]:
+ tasks.append(i[ORDER_ID_LENGTH:])
+
+ stats_total = len(tasks)
+
+ # compute number of tasks in each state, groups and links, recursively
+ for tid in tasks:
+ task_status = get_status(tid)
+ stats[task_status] += 1
+ if is_this_task_a_group(tid):
+ stats[' Group'] += 1
+ group_statistics(tid)
+ if is_linked(tid):
+ stats[' Linked'] += 1
+
+#:stats_creation_dates
+ # Figure out creation time and modification time for description
+ task_ctime = get_creation_date(tid)[1]
+
+ task_path = generate_task_path(tid)
+ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/description.txt'%task_path)
+ if task_ctime == 0:
+ # ctime not available
+ task_ctime = mtime
+ stats_creation_dates.append(task_ctime)
+
+ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat('%s/status'%task_path)
+
+ cdate = time.strftime("%Y-%m-%d", time.localtime(task_ctime))
+ sdate = time.strftime("%Y-%m-%d", time.localtime(mtime))
+ if not stats_overtime.has_key(cdate):
+ # initialize date dict
+ stats_overtime[cdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))]))
+ stats_overtime[cdate]['Creation'] += 1
+ if not stats_overtime.has_key(sdate):
+ # initialize date dict
+ stats_overtime[sdate] = dict(zip(STATS_OVERTIME_KEYS,[0 for i in range(len(STATS_OVERTIME_KEYS))]))
+ stats_overtime[sdate][task_status] += 1
+
+ if not stats_total:
+ r.append('0 task in statistics.')
+ return r
+
+ # csv format, not used for now
+ #col_names = ['Tasks'] + sorted(stats.keys())
+ #col_names = [i strip for i in col_names]
+ #print ','.join(col_names)
+ #csv = '%d'%len(tasks)
+ #for k in sorted(stats.keys())
+ # csv += ',%d'%stats[k]
+ #r append csv
+ #r append ''
+
+ r.append('Number of items: %d\n'%stats_total)
+ for k in sorted(stats.keys()):
+ r.append('%s - %d'%(k, stats[k]))
+ r.append('')
+
+ # speed
+ start_time = sorted(stats_creation_dates)[0]
+ now = time.time()
+ done_and_inactive = stats[TASK_STATUS[TASK_STATUS_DONE]] + stats[TASK_STATUS[TASK_STATUS_INACTIVE]]
+ if not done_and_inactive:
+ r.append('Nothing is done or inactive')
+ else:
+ # subtract tasks in state void, because tasks in void state are informative or groups
+ number_of_tasks = stats_total - stats[TASK_STATUS[TASK_STATUS_VOID]]
+ remaining_tasks = number_of_tasks - done_and_inactive
+ remaining_time = (now - start_time) / float(done_and_inactive) * remaining_tasks
+ r.append('Number of tasks (excluding voids): %d'%number_of_tasks)
+ r.append('Remaining tasks: %d'%remaining_tasks)
+ r.append('Remaining days: %.3f'%(remaining_time/86400))
+ r.append('Start date: %s'%time.strftime("%Y-%m-%d", time.localtime(start_time)))
+ r.append("Today's date: %s"%time.strftime("%Y-%m-%d", time.localtime(now)))
+ r.append('Finish date: %s'%time.strftime("%Y-%m-%d", time.localtime(now + remaining_time)))
+
+ # create csv stats from stats_overtime
+ f = open('%s/stats.csv'%data_location,'w')
+ f.write('date,%s\n'%','.join([i.strip() for i in STATS_OVERTIME_KEYS]))
+
+ for d in sorted(stats_overtime.keys()):
+ f.write('%s,%s\n'%(d, ','.join([str(stats_overtime[d][i]) for i in STATS_OVERTIME_KEYS])))
+ f.close()
+
+ # create creation, done and inactive stats
+ f = open('%s/stats_creation_done_inactive.csv'%data_location,'w')
+ f.write('date,Creation,Done/Inactive\n')
+
+ for d in sorted(stats_overtime.keys()):
+ 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]]))
+ f.close()
+
+ r.append('')
+ return r
+
+# test core functions
+#def test
+# # test
+# list_group('root')
+#
+# t1 = add_task('root', 'task1.txt')
+# add_task('root', 'task2.txt')
+# g3 = add_task('root', 'task3.txt')
+#
+# create_group(g3)
+# t4 = add_task(g3, 'task4.txt')
+#
+# list_group('root')
+# list_group(g3)
+#
+# #display_task(t1)
+# #display_task(t4)
+#
+# # Delete task in a group of 2 tasks
+# print '# Delete task in a group of 2 tasks'
+# delete_task(t4)
+#
+# list_group('root')
+#
+# create_group(g3)
+# t4 = add_task(g3, 'task4.txt')
+#
+# list_group('root')
+#
+# # Delete group first task
+# print '# Delete group first task'
+# delete_task(g3)
+#
+# list_group('root')
+#
+# # Delete group task of a group with more than 2 tasks
+# print '# Delete group task of a group with more than 2 tasks'
+# delete_task(t4)
+#
+# g3 = add_task('root', 'task3.txt')
+# create_group(g3)
+# t4 = add_task(g3, 'task4.txt')
+# t2 = add_task(g3, 'task2.txt')
+#
+# list_group('root')
+# list_group(g3)
+# new_group_id = delete_task(g3)
+# print 'New group id %s'%new_group_id
+#
+# list_group('root')
+# list_group(new_group_id)
+#
+# # Delete group
+# print '# Delete group'
+# delete_group(new_group_id)
+#
+# list_group('root')
+#
+# print baseconvert(100)
+# print baseconvert_to_dec(baseconvert(100))
+# print
+#
+# #edit_task(t1)
+#
+# # Change order
+# print '# Change order'
+# g3 = add_task('root', 'task3.txt')
+# change_task_order('root',1,0)
+# change_task_order('root',0,2)
+#
+# list_group('root')
+#
+# # Change status
+# print '# Change status'
+# create_group(g3)
+# t4 = add_task(g3, 'task4.txt')
+# t2 = add_task(g3, 'task2.txt')
+#
+# list_group('root')
+#
+# set_status(t1,TASK_STATUS_DONE)
+# set_status(t4,TASK_STATUS_DONE)
+#
+# list_group('root')
+# list_group(g3)
+#
+# # Reset status
+#
+# reset_group_status(g3)
+#
+# list_group('root')
+# list_group(g3)
+#
+# reset_group_status('root')
+#
+# list_group('root')
+# list_group(g3)
+#
+# # Create a group in group
+# print '# Create a group in group'
+# create_group(t4)
+# t2 = add_task(t4,'task2.txt')
+#
+# list_group('root')
+# list_group(g3)
+# list_group(t4)
+#
+# # Delete task in a group of 2 tasks not in root
+# print '# Delete task in a group of 2 tasks not in root'
+# delete_task(t2)
+#
+# list_group('root')
+# list_group(g3)
+#
+# # Delete group with groups in it
+# print '# Delete group with groups in it'
+# create_group(t4)
+# t2 = add_task(t4,'task2.txt')
+#
+# list_group('root')
+# list_group(g3)
+# list_group(t4)
+#
+# delete_group(g3)
+#
+# list_group('root')
+#
+# # Search string
+# print '# Search string'
+#
+# search_string('AND')
+#
+# # List tree
+# print '# List tree'
+# g3 = add_task('root', 'task3.txt')
+# create_group(g3)
+# t4 = add_task(g3, 'task4.txt')
+# t2 = add_task(g3, 'task2.txt')
+# create_group(t4)
+# t2 = add_task(t4,'task2.txt')
+#
+# list_tree('root')
+#
+# # Show group for a task
+# print '# Show group for a task'
+#
+# print 'Show group for %s - %s' %(t2,get_task_title(t2))
+# show_group_for_task(t2)
+# print 'Show group for %s - %s' %(t4,get_task_title(t4))
+# show_group_for_task(t4)
+# print 'Show group for %s - %s' %(t1,get_task_title(t1))
+# show_group_for_task(t1)
+
+ # create task
+ #create_task(t4)
+ #list_group(t4)
+
+## start - always called at startup.
+# @ingroup EDI_CORE
+# @param[in] interface selects current user interface. cli loads the configuration from user home, web loads the configuration located in edi_web folder.
+# creates default .easydoneit.ini<br>
+# creates database folders<br>
+# loads .easydoneit.ini
+def start(interface='cli'):
+ global user_interface
+ user_interface = interface
+
+ if interface=='web':
+ # Do not try to access ~/.easydoneit.ini
+ inipath = '.easydoneit.ini'
+ else:
+ inipath = '~/.easydoneit.ini'
+ # create ~/.easydoneit.ini if it doesnt exist, (not in web interface mode)
+ if not os.path.isfile(os.path.expanduser('~/.easydoneit.ini')):
+ f = open(os.path.expanduser('~/.easydoneit.ini'),'w')
+ f.write('[data]\n')
+ f.write('location=~/easydoneit_data\n')
+ f.write('1=~/easydoneit_data\n')
+ f.write('\n')
+ f.write('[locations]\n')
+ f.write('selected=1\n')
+ f.write('default_add_in=1\n')
+ f.write('\n')
+ f.write('[filters]\n')
+ # enable all status filters
+ fi = zip(TASK_STATUS,status_filters)
+ for name,nfilter in fi:
+ # strip to remove spaces in status strings
+ f.write("%s=%s\n"%(name.strip(),nfilter))
+ f.write('\n')
+ f.write('[colors]\n')
+ co = zip(TASK_STATUS,status_fgColors)
+ for name,color in co:
+ f.write('%s_fgColor=%d,%d,%d,%d\n' % (name.strip(),color[0],color[1],color[2],color[3]))
+ co = zip(TASK_STATUS,status_bgColors)
+ for name,color in co:
+ f.write('%s_bgColor=%d,%d,%d,%d\n' % (name.strip(),color[0],color[1],color[2],color[3]))
+
+ f.write('\n[settings]\n')
+ f.write('editor=vi\n')
+
+ f.close()
+ # default permissions 600 rw for user no access for group and world
+ os.chmod(os.path.expanduser('~/.easydoneit.ini'),stat.S_IRUSR|stat.S_IWUSR)
+
+ # load config from inipath
+ config = ConfigParser.ConfigParser()
+ config.readfp(open(os.path.expanduser(inipath)))
+
+ # convert ~ to home path
+ global data_location
+ data_location = os.path.expanduser(config.get('data','location'))
+
+ # load available databases
+ global databases
+ data_section = config.items('data')
+ # remove location which is a path to a database
+ DATABASE_NAME = 0
+ DATABASE_PATH = 1
+ # clear databases for reentrant testing
+ databases = []
+ for d in data_section:
+ if d[DATABASE_NAME] != 'location':
+ databases.append(d)
+
+ # load add_top_or_bottom
+ if 'add_top_or_bottom' in dict(config.items('locations')).keys():
+ global add_top_or_bottom
+ add_top_or_bottom = config.get('locations','add_top_or_bottom')
+
+ # load selected database paths
+ global selected
+ global default_add_in
+ global selected_path
+ selected = config.get('locations','selected').split(',')
+ default_add_in = config.get('locations','default_add_in')
+ for d in selected:
+ selected_path.append(os.path.expanduser(config.get('data',d)))
+
+ # load autolink groups
+ if 'autolink' in dict(config.items('locations')).keys():
+ global autolink
+ autolink_cfg = config.get('locations','autolink')
+ autolink = [ autolink_cfg[i:i+ID_LENGTH] for i in range(0,len(autolink_cfg), ID_LENGTH+1)]
+
+ # load list groups
+ if 'list' in dict(config.items('locations')).keys():
+ global list_of_groups
+ list_cfg = config.get('locations','list')
+ list_of_groups = [ list_cfg[i:i+ID_LENGTH] for i in range(0,len(list_cfg), ID_LENGTH+1)]
+
+ # load status filters and status colors
+ config_filter_names = dict(config.items('filters')).keys()
+ config_color_names = dict(config.items('colors')).keys()
+ for n,nfilter in enumerate(TASK_STATUS):
+ # strip to remove spaces in status strings
+ # check that task_status filter is in config file, to avoid problems between config file versions
+ if nfilter.strip().lower() in config_filter_names:
+ status_filters[n] = config.get('filters',nfilter.strip())
+ if '%s_fgcolor'%nfilter.strip().lower() in config_color_names:
+ status_fgColors[n] = tuple([int(i) for i in config.get('colors','%s_fgcolor'%nfilter.strip()).split(',')])
+ if '%s_bgcolor'%nfilter.strip().lower() in config_color_names:
+ status_bgColors[n] = tuple([int(i) for i in config.get('colors','%s_bgcolor'%nfilter.strip()).split(',')])
+
+ # Set text editor
+ global editor
+ editor = config.get('settings','editor')
+
+ # set user name and email
+ global user
+ if 'username' in dict(config.items('settings')).keys():
+ user = config.get('settings','username')
+ else:
+ user = getpass.getuser()
+ if 'useremail' in dict(config.items('settings')).keys():
+ global email
+ email = config.get('settings','useremail')
+
+ # init
+ init()
+
diff --git a/root.txt b/root.txt
@@ -0,0 +1 @@
+First Level