示例#1
0
class MinimalStoryView(MyInnerWindowWithSaveAndTrash):   
    def __init__(self,wnd,ctrl, **kwargs):
        if not 'name' in kwargs:
            raise KeyError('Name attribute must be passed in kwargs')
        kwargs.setdefault('minimized_label', kwargs['name'])
        super(MinimalStoryView,self).__init__(**kwargs)
        if ctrl is None:
            ctrl = TestController(wnd,'minimal' if self.isMinimal else 'full')
        self.ctrl = ctrl
        try:
            self.isMinimal = True if self.isMinimal else False
        except Exception:
            self.isMinimal = True
        self.grid_size = minimal_size
        story_size = scale_tuple(self.grid_size, 0.2)
        self._name = kwargs['name']
        self._id = kwargs['id']
        self._description = kwargs.setdefault('description','')
        self.padding = 8
        self.spacing = 8
        self.font_height = 13 * 2
        self._full_width =  int(story_size[0])
        self._half_width = int(self._full_width /2) 
        self.visible_lines = 3
        
        self.layout = MTGridLayout(size=story_size,rows=3,cols=1,
                                   spacing=self.spacing)

        self.row0 = MTGridLayout(cols=2,rows=2,spacing=self.spacing,
                                 uniform_width=False, uniform_height=False)
        self.row1 = MTGridLayout(cols=1,rows=2, spacing=self.spacing,
                                 uniform_width=False, uniform_height=False)
        text_height = self.font_height * self.visible_lines
        sz=(self._full_width - int(len('Story Name: ')*pixels),self.font_height)
        self.story_name = MyTextInput(label=self._name,id='story_name_id',
                                      size=sz,keyboard_to_root='True',
                                      place_keyboard_by_control='True')
        self.description = MyTextArea(label=self._description,\
                                       id='description_id',
                                       size=(self._full_width,text_height),
                                       keyboard_to_root='True',
                                      place_keyboard_by_control='True')
        #push handlers for each text input fields
        self.story_name.push_handlers(on_text_change=self._set_name)
        self.description.push_handlers(on_text_change=self._set_description)
        
        
        self.row0.add_widget(MTLabel(label='Story Name:'))
        self.row0.add_widget(self.story_name)
        
        self.row1.add_widget(MTLabel(label='Description:'))
        self.row1.add_widget(self.description)
        self.layout.add_widget(self.row0)
        self.layout.add_widget(self.row1)
        self.layout.pos = (25,0)
        self.canvas = MTScatter(size=self.grid_size, pos=(0,2),
                                cls='storycardcss')
        self.canvas.add_widget(self.layout)
        self.add_widget(self.canvas)
    
    
    
    def fullscreen(self, *largs, **kwargs):
        self.isMinimal = not self.isMinimal
        self.ctrl.switch_view(self.isMinimal)
        
    
    def _get_name(self): return self.ctrl.Name 
    def _set_name(self, value): 
        self.ctrl.Name = value
    name = property(_get_name, _set_name)
    
    
    def _set_description(self, value): 
        self.ctrl.Description = value.value
        
    def save(self, touch, *largs, **kwargs):
        self.ctrl.save()

    def close(self, touch):
        self.ctrl.close(self)
        super(MinimalStoryView, self).close(touch)

    def trash(self, touch=None, *largs, **kwargs):
        self.ctrl.trash()
        super(MinimalStoryView, self).close(touch)
示例#2
0
class StoryApp(MyInnerWindow):
    def __init__(self, **kwargs):
        global _storyapp_singleton #IGNORE:W0603
        # Make ourself globally known
        _storyapp_singleton = self
        super(StoryApp, self).__init__(**kwargs)
        set_caption("Collaborative Multitouch Agile Planner")
        AsyncHandler().set_handler(ON_GITHUBNOTIFICATION,
                                   self.on_github_notification)
        self.no_local_repo = Config().localRepoNotAvailable
        if not self.no_local_repo:
            AsyncHandler().save([],
                                'Cleanup any outstanding uncommitted edits')
        self.root_window = kwargs['root_window']
        self.canvas = None
        self.backlog_list_layout = MTGridLayout(rows=1)
        self._x_range = range(self.root_window.x, self.root_window.x + \
                              self.root_window.width - minimal_size[0])
        self._y_range = range(self.root_window.y, self.root_window.y + \
                              self.root_window.height - minimal_size[1])

        self.main_ctlrs_container = MTSidePanel(\
                                        corner=MTToggleButton(padding=5,
                                                        label='File...',
                                                        size=(60,45)),
                                        layout=MTGridLayout(rows=1),
                                        align='left',
                                        side='top') 
        self.backlog_container = MTSidePanel(\
                                        corner=MTToggleButton(padding=5,
                                                        label='Backlog...',
                                                        size=(100,45)),
                                        align='middle',
                                        side='bottom',
                                        pos=(0,self.root_window.height-100)) 
        self.buttons = {}
        self.labels = {}
        self.backlog = {}
        self.artifacts = {}
        self.story_files = []
        self.tasks = {}
        self.backlog_flow_open = True
        self.projects_flow_open = True
        self.releases_flow_open = True
        self.story_flow_open = True
        self.sprint_flow_open = True
        self.task_flow_open = True
        self.current_backlog = None
        self.current_project = None
        self.current_release = None
        self.current_sprint = None
        self.current_story = None
        self.current_task = None
        self.currentProjectView = None
        self.currentReleaseView = None
        self.currentSprintView = None
        self.currentStoryView = None
        self.currentTaskView = None
        
        self.checked_for_releases = False
        self.checked_for_sprints = False
        self.checked_for_stories = False
        self.checked_for_tasks = False
        
        # Load the default image for buttons and cover flows
        path = os.path.join(os.getcwd(), 'data', 'ModelScribble.jpg')
        self._default_button_size = (100, 100)
        self._default_image = Loader.image(path)
        _sf_pos = (200,200)
        try:
            self.backlog_flow = MTCoverFlow(
                                      layout=MTGridLayout(spacing=10,rows=1),
                                      pos=_sf_pos,
                                      size=self._default_button_size,
                                      cover_spacing=20,
                                      cover_distance=115,
                                      thumbnail_size=self._default_button_size)
            self.backlog_flow.push_handlers(on_select=self.flow_backlog_select)
            self.backlog_flow.add_widget(self.createNewStoryButton())
        except Exception: #IGNORE:W0703
            Log.exception("Unable to create backlog cover flow")
            self.backlog_flow = MTGridLayout(rows=1, pos=_sf_pos)
            self.backlog_flow.add_widget(self.createNewStoryButton())
        try:
            self.projects_flow = MTCoverFlow(\
                                      layout=MTGridLayout(spacing=10,rows=1),
                                      pos=_sf_pos,
                                      size=self._default_button_size,
                                      cover_spacing=20,
                                      cover_distance=115,
                                      thumbnail_size=self._default_button_size)
            self.projects_flow.push_handlers(\
                                           on_select=self.flow_projects_select)
            self.projects_flow.add_widget(self.createNewProjectButton())
        except Exception: #IGNORE:W0703
            Log.exception("Unable to create project cover flow")
            self.projects_flow = MTGridLayout(rows=1, pos=_sf_pos)
        try:
            self.release_flow = MTCoverFlow(\
                                      layout=MTGridLayout(spacing=10,rows=1),
                                      pos=_sf_pos,
                                      size=self._default_button_size,
                                      cover_spacing=20,
                                      cover_distance=115,
                                      thumbnail_size=self._default_button_size)
            self.release_flow.push_handlers(\
                                            on_select=self.flow_release_select)
            self.release_flow.add_widget(self.createNewReleaseButton())
        except Exception: #IGNORE:W0703
            Log.exception("Unable to create release cover flow")
            self.release_flow = MTGridLayout(rows=1, pos=_sf_pos)
        try:
            self.sprint_flow = MTCoverFlow(\
                                      layout=MTGridLayout(spacing=10,rows=1),
                                      pos=_sf_pos,
                                      size=self._default_button_size,
                                      cover_spacing=20,
                                      cover_distance=115,
                                      thumbnail_size=self._default_button_size)
            self.sprint_flow.push_handlers(\
                                            on_select=self.flow_sprint_select)
            self.sprint_flow.add_widget(self.createNewSprintButton())
        except Exception: #IGNORE:W0703
            Log.exception("Unable to create sprint cover flow")
            self.sprint_flow = MTGridLayout(rows=1, pos=_sf_pos)
        try:
            self.story_flow = MTCoverFlow(\
                                      layout=MTGridLayout(spacing=10,rows=1),
                                      pos=_sf_pos,
                                      size=self._default_button_size,
                                      cover_spacing=20,
                                      cover_distance=115,
                                      thumbnail_size=self._default_button_size)
            self.story_flow.push_handlers(on_select=self.flow_story_select)
            self.story_flow.add_widget(self.createNewStoryButton())
        except Exception: #IGNORE:W0703
            Log.exception("Unable to create story cover flow")
            self.story_flow = MTGridLayout(rows=1, pos=_sf_pos)
        try:
            self.task_flow = MTCoverFlow(\
                                      layout=MTGridLayout(spacing=10,rows=1),
                                      pos=_sf_pos,
                                      size=self._default_button_size,
                                      cover_spacing=20,
                                      cover_distance=115,
                                      thumbnail_size=self._default_button_size)
            self.task_flow.push_handlers(on_select=self.flow_task_select)
            self.task_flow.add_widget(self.createNewTaskButton())
        except Exception: #IGNORE:W0703
            Log.exception("Unable to create task coverflow")
            self.task_flow = MTGridLayout(rows=1, pos=_sf_pos)
        #dragable containers for the flow objects so we can move them around 
        #the screen
        self.dragable_backlog_flow = MyDragableContainer(self.backlog_flow,
                                    True, size_scaler=(-.1,-.1), cls='dragcss',
                                    use_widget_size=False)
        self.dragable_project_flow = MyDragableContainer(self.projects_flow,
                                    True, size_scaler=(-.2,-.2), cls='dragcss')
        self.dragable_release_flow = MyDragableContainer(self.release_flow,
                                    True, size_scaler=(-.2,-.2), cls='dragcss')
        self.dragable_sprint_flow = MyDragableContainer(self.sprint_flow,
                                    True, size_scaler=(-.2,-.2), cls='dragcss')
        self.dragable_story_flow = MyDragableContainer(self.story_flow,
                                    True, size_scaler=(-.2,-.2), cls='dragcss')
        self.dragable_task_flow = MyDragableContainer(self.task_flow,
                                                          True)
        self.backlog_list = MTList(size=(self.root_window.width,100),pos=(0,0))
        self.backlog_list.add_widget(self.backlog_list_layout)
        self.backlog_container.add_widget(self.backlog_list)
        self.path = Config().datastore
        Log.debug('Path to repository: %s' % self.path)

        #enable gesture detection
        self.enable_gestures()
        self.canvas.add_widget(self.backlog_container)
        self.addMainControls()
        self.xmlFiles = self.get_local_artifacts()
        self.load_backlog()
        self.load_projects()
        #make sure no projects or stories are set as the current ones
        self.current_project = None
        self.current_story = None
        
    def on_github_notification(self, ret):
        msg = ret[1]
        print("GOOGLE APP HOOK CCALLED ***************************************************************************")
        for c in msg['commits']:
            for a in c['added']:
                Log.debug('%s added by %s' % (a, c['author']['name']))
            for m in c['modified']:
                Log.debug('%s modified by %s' % (m, c['author']['name']))
            for r in c['removed']:
                Log.debug('%s removed by %s' % (r, c['author']['name']))

        
    def flow_backlog_select(self,btn):
        #make the selected backlog item the active one
        try:
            idu = btn.Id if isinstance(btn, ArtifactController)\
                         else btn._id #IGNORE:W0212
            self.current_backlog = self.backlog[idu] 
            self.viewCurrentBacklog(btn)     
        except KeyError:
            #create new backlog story
            lbl = self.new_backlog_pressed()
            self.current_backlog = self.backlog[lbl.Id]
        self.current_story = self.current_backlog
        set_caption(self.current_backlog[0].Name)
    def flow_projects_select(self,btn):
        #make the selected project the active one
        try:
            idu = btn.Id if isinstance(btn, ArtifactController)\
                         else btn._id #IGNORE:W0212
            self.current_project = self.artifacts[idu]
            self.viewCurrentProject(btn)     
        except KeyError:
            #create new project
            lbl = self.new_project_pressed()
            if lbl is None: return
            self.current_project = self.artifacts[lbl.Id]
        set_caption(self.current_project[0].Name)
        #we need to populate the releases flow with any release 
        #in the current project
        self.load_releases()
        self.container_reset_children('release_flow')
        self.container_reset_children('sprint_flow')
        self.container_reset_children('story_flow')
        self.container_reset_children('task_flow')
        if self.current_project and self.current_project[0].Children:
            for release in self.current_project[0].Children:
                if release in self.artifacts:
                    r = self.artifacts[release][0]
                    if r.ArtifactType != 'Release': continue
                    self.newRelease(r)
                else:
                    Log.debug('Project: %s found a release: %s not in releases' % \
                              (self.current_project[0].Name,release))
    def flow_release_select(self,btn):
        #make the selected release the active one
        try:
            idu = btn.Id if isinstance(btn, ArtifactController)\
                         else btn._id #IGNORE:W0212
            self.current_release = self.artifacts[idu]
            self.viewCurrentRelease(btn)
        except KeyError:
            #create new release
            lbl = self.new_release_pressed()
            if lbl is None: return
            self.current_release = self.artifacts[lbl.Id]
        #we need to populate the sprint flow with any sprints already
        #in the current release
        self.load_sprints()
        self.container_reset_children('sprint_flow')
        self.container_reset_children('story_flow')
        self.container_reset_children('task_flow')
        if self.current_release and self.current_release[0].Children:
            for sprint in self.current_release[0].Children:
                if sprint in self.artifacts.keys():
                    s = self.artifacts[sprint][0]
                    if s.ArtifactType != 'Sprint': continue
                    self.newSprint(s)
                else:
                    Log.debug('Release: %s found a sprint: %s not in sprints' % \
                              (self.current_release[0].Name,sprint))
    def flow_sprint_select(self,btn):
        #make the selected sprint the active one
        try:
            idu = btn.Id if isinstance(btn, ArtifactController)\
                         else btn._id #IGNORE:W0212
            self.current_sprint = self.artifacts[idu]
            self.viewCurrentSprint(btn)
        except KeyError:
            #create new sprint
            lbl = self.new_sprint_pressed()
            if lbl is None: return
            self.current_sprint = self.artifacts[lbl.Id]
        #we need to populate the story flow with any stories already
        #in the current sprint
        self.load_stories()
        self.container_reset_children('story_flow')
        self.container_reset_children('task_flow')
        if self.current_sprint and self.current_sprint[0].Children:
            for story in self.current_sprint[0].Children:
                if story in self.artifacts.keys():
                    s = self.artifacts[story][0]
                    if s.ArtifactType != 'Story': continue
                    self.newStory(s)
                else:
                    Log.debug('Sprint: %s found a story: %s not in stories' % \
                              (self.current_sprint[0].Name,story))
    def flow_story_select(self,btn):
        try:
            idu = btn.Id if isinstance(btn, ArtifactController)\
                         else btn._id #IGNORE:W0212
            self.current_story = self.artifacts[idu]
            self.viewCurrentStory(btn)
        except KeyError:
            #create new story
            lbl = self.new_story_pressed()
            if lbl is None: return
            if lbl.Parent:
                self.current_story = self.artifacts[lbl.Id]
        #we need to populate the task flow with any tasks already
        #in the current story
        self.load_tasks()
        self.container_reset_children('task_flow')
        if self.current_story and self.current_story[0].Children:
            for task in self.current_story[0].Children:
                if task in self.artifacts.keys():
                    t = self.artifacts[task][0]
                    if t.ArtifactType != 'Task': continue
                    self.newTask(t)
                else:
                    Log.debug('Story: %s found a task: %s not in tasks' % \
                              (self.current_story[0].Name,task))
    def flow_task_select(self,btn):
        try:
            idu = btn.Id if isinstance(btn, ArtifactController)\
                         else btn._id #IGNORE:W0212
            self.current_task = self.artifacts[idu]
            self.viewCurrentTask(btn)
        except KeyError:
            #create new task
            lbl = self.new_task_pressed()
            if lbl is None: return
            self.current_task = self.artifacts[lbl.Id]
    def _flow_pressed(self, flag, widget, *largs): #IGNORE:W0613
        _flag =  not self.__getattribute__(flag)
        self.__setattr__(flag, _flag)
        if _flag:
            super(StoryApp,self).remove_widget(widget)
        else:
            widget.pos = self.get_new_random_position()
            super(StoryApp,self).add_widget(widget)
    def get_new_random_position(self):
        return (choice(self._x_range),choice(self._y_range))
    def enable_gestures(self):
        print("enabling gestures")
        gdb = myGestures()
        defn =  os.path.join(Config().gestures,'xmlGestures','gestures.xml')
        #import S for New Story
        try:
            get_and_or_store_gesture(gdb, 'New Story', 'New_Story',defn)
            get_and_or_store_gesture(gdb, 'Projects', 'Projects',defn)
            get_and_or_store_gesture(gdb, 'Projects1', 'Projects1',defn)
            get_and_or_store_gesture(gdb, 'Releases', 'Releases',defn)
            get_and_or_store_gesture(gdb, 'Releases1', 'Releases1',defn)
            get_and_or_store_gesture(gdb, 'Sprints', 'Sprints',defn)
            get_and_or_store_gesture(gdb, 'Sprints1', 'Sprints1',defn)
            get_and_or_store_gesture(gdb, 'Sprints2', 'Sprints2',defn)
            get_and_or_store_gesture(gdb, 'Tasks', 'Tasks',defn)
            get_and_or_store_gesture(gdb, 'Tasks1', 'Tasks1',defn)
            get_and_or_store_gesture(gdb, 'Backlog', 'Backlog',defn)
            get_and_or_store_gesture(gdb, 'Backlog1', 'Backlog1',defn)
            get_and_or_store_gesture(gdb, 'Square', 'square',defn)
            get_and_or_store_gesture(gdb, 'X', 'x',defn)
        except Exception: #IGNORE:W0703
            print("Some exception in gestures")
        self.canvas = MTScatter(cls='gesturecss') 
        self.canvas.size = self.root_window.size
        self.canvas.pos = (0,0)
        super(StoryApp,self).add_widget(self.canvas)
        if not self.canvas:
            print("No canvas in gestures ")
        capture = MyCaptureWidget(gdb, self)
        capture.size = self.canvas.size
        if not capture:
            print("No capture device in gestures")
        self.canvas.add_widget(capture)
    def get_local_artifacts(self):
        '''
        Loads the file names of all local artifacts into a dictionary
        keyed on artifact type
        '''
        Log.debug('path: %s' % self.path)
        xmlFiles = {}
        for atype in artifact_types.values():
            xmlFiles[atype] = []
            files = glob(os.path.join(self.path, atype, '*.xml'))
            for f in files:
                Log.debug('xmlFile: %s' % f)
                xmlFiles[atype].append(f)
        return xmlFiles
        
    def load_projects(self):
        for f in self.xmlFiles[artifact_types[PROJECTS]]:
            Log.debug('load only xmlFile: %s' % f)
            
            p = self.getProject(f,model=ProjectModel,
                    get_artifact='newProject',
                    name=os.path.splitext(os.path.basename(f))[0])
            self.artifacts[p.Id] = (p,{})
            self.newProject(p)
    def load_releases(self):
        if self.checked_for_releases: return
        self.checked_for_releases = True
        for f in self.xmlFiles[artifact_types[RELEASES]]:
            Log.debug('only loading release %s' % f)
            r = self.getRelease(f, model=ReleaseModel,
                    get_artifact='newRelease',name=\
                os.path.splitext(os.path.basename(f))[0])
            self.artifacts[r.Id] = (r,{})
            self.newRelease(r)
    def load_sprints(self):
        if self.checked_for_sprints: return
        self.checked_for_sprints = True
        for f in self.xmlFiles[artifact_types[SPRINTS]]:
            Log.debug('only loading sprint %s' % f)
            s = self.getSprint(f, model=SprintModel, name=\
                os.path.splitext(os.path.basename(f))[0])
            self.artifacts[s.Id] = (s, {})
            self.newSprint(s)
    def load_backlog(self):
        Log.debug('BackLog loading:')
        for f in self.xmlFiles[artifact_types[BACKLOG]]:
            b = self.getBacklog(f)
            self.backlog[b.Id] = (b,{})
            self.newBacklog(b)
        Log.debug('Backlog Done loading')
    def load_stories(self):
        if self.checked_for_stories: return
        self.checked_for_stories = True
        Log.debug('Stories loading: ')
        for f in self.xmlFiles[artifact_types[STORIES]]:
            Log.debug('only loading story %s' % f)
            s = self.getStory(f)
            self.artifacts[s.Id] = (s ,{})
            self.newStory(s)
        Log.debug('Stories Done loading')
    def load_tasks(self):
        if self.checked_for_tasks: return
        self.checked_for_tasks = True
        for f in self.xmlFiles[artifact_types[TASKS]]:
            Log.debug('%s' % f)
            t = self.getTask(f, name=\
                os.path.splitext(os.path.basename(f))[0])
            self.artifacts[t.Id] = (t, {})
            self.newTask(t)


    def createNewProjectButton(self):
        return self.create_button(LABEL_NEW_PROJECT,curry(\
                                                    self.new_project_pressed))
    def createNewReleaseButton(self):
        return self.create_button(LABEL_NEW_RELEASE,curry(\
                                                    self.new_release_pressed))
    def createNewSprintButton(self):
        return self.create_button(LABEL_NEW_SPRINT, curry(\
                                                    self.new_sprint_pressed))
    def createNewStoryButton(self):
        return self.create_button(LABEL_NEW_STORY,curry(self.new_story_pressed))
    def createNewTaskButton(self):
        return self.create_button(LABEL_NEW_TASK,curry(self.new_task_pressed))
    def new_backlog_pressed(self): # *largs
        _b = self.new_artifact_pressed(StoryController,
                                       'currentBacklogView',
                                       model=StoryModel,
                                       mini_view_type=MinStoryView,
                                       view_type=StoryView,
                                       get_artifact='newBacklog',
                                       folder='backlog')
        self.backlog[_b.Id] = (_b,{})
        return _b
    def new_project_pressed(self, *largs): #IGNORE:W0613
        _p = self.new_artifact_pressed(ProjectController,
                                       'currentProjectView',
                                       model=ProjectModel,
                                       mini_view_type=ProjectMinView,
                                       view_type=ProjectView,
                                       get_artifact='newProject')
        pobj = (_p, {})
        self.artifacts[_p.Id] = pobj
        self.current_project = pobj
        return _p
    def new_release_pressed(self, *largs): #IGNORE:W0613
        project = None if self.current_project is None \
                                        else self.current_project[0].Id
        if not project:
            return # Do not support releases in backlog now
        _r = self.new_artifact_pressed(ReleaseController,
                                       'currentReleaseView',
                                       project=project if project else None,
                                       model=ReleaseModel,
                                       view_type=ReleaseView,
                                       mini_view_type=ReleaseMinView,
                                       get_artifact='newRelease' if project\
                                                     else 'newBacklog',
                                       folder='releases' if project\
                                               else 'backlog')
        robj = (_r, {})
        self.artifacts[_r.Id] = robj
        if project:
            self.current_project[0].Children = _r.Id
            _r.Parent = project
            self.current_release = robj
        return _r
    def new_sprint_pressed(self, *largs): #IGNORE:W0613
        project = None if self.current_project is None \
                                        else self.current_project[0]
        release = None if self.current_release is None \
                        else self.current_release[0].Id
        if not release:
            return # No appropriate parent
        _s = self.new_artifact_pressed(SprintController,
                                       'currentSprintView',
                                       p_artifact=release,
                                       model=SprintModel,
                                       view_type=SprintView,
                                       mini_view_type=SprintMinView,
                                       get_artifact='newSprint'\
                                                    if release or project\
                                                    else 'newBacklog',
                                       folder='sprints' if release or project\
                                              else 'backlog',
                                       release=release)
        sobj = (_s, {})
        self.artifacts[_s.Id] = sobj
        if release:
            self.current_release[0].Children = _s.Id
            _s.Parent = release
            self.current_sprint = sobj
        return _s
    def new_story_pressed(self, *largs): #IGNORE:W0613
        project = None if self.current_project is None \
                                        else self.current_project[0]
        sprint = None if self.current_sprint is None \
                        else self.current_sprint[0]
        parent = sprint if sprint else project
        _s = self.new_artifact_pressed(StoryController,
                                       'currentStoryView',
                                        p_artifact=parent,
                                        project=project.Id if project else None,
                                        model=StoryModel,
                                        view_type=StoryView,
                                        mini_view_type=MinStoryView,
                                        get_artifact='newStory' if parent\
                                                      else 'newBacklog',
                                        folder='stories' if parent\
                                                else 'backlog')
        sobj = (_s, {})
        if sprint:
            self.current_sprint[0].Children = _s.Id
            _s.Parent = sprint.Id
            self.artifacts[_s.Id] = sobj
            self.current_story = sobj
        elif not parent:
            self.backlog[_s.Id] = sobj
        _s.register_observer(self)
        return _s
    def new_task_pressed(self, *largs): #IGNORE:W0613
        story = None if self.current_story is None \
                        else self.current_story[0]
        sprint = None if self.current_sprint is None \
                         else self.current_sprint[0]
        parent = None
        if story:
            parent = story
        elif sprint:
            parent = sprint
        if not parent:
            return # No appropriate parent
        _t = self.new_artifact_pressed(TaskController,
                                       'currentTaskView',
                                       p_artifact=parent,
                                       model=TaskModel,
                                       view_type=TaskView,
                                       mini_view_type=TaskMinView,
                                       get_artifact='newTask',
                                       folder='tasks')
        tobj = (_t, {})
        self.artifacts[_t.Id] = tobj
        if story:
            story.Children = _t.Id
            _t.Parent = story.Id
        elif sprint:
            sprint.Children = _t.Id
            _t.Parent = sprint.Id
        else:    
            self.backlog[_t.Id] = tobj
        if story or sprint:
            self.current_task = tobj
        return _t
    def new_artifact_pressed(self,ctrl,view, **kwargs): # *largs
        _r = ctrl(self,None,**kwargs)
        self.__setattr__(view, _r.newDialog(minv=True))
        super(StoryApp,self).add_widget(self.__getattribute__(view))
        return _r 
    def getBacklog(self,defn,**kwargs):
        kwargs['view_type'] = StoryView
        kwargs['mini_view_type'] = MinStoryView
        kwargs['get_artifact'] = 'newBacklog'
        kwargs['model'] = StoryModel
        kwargs['folder'] = 'backlog'
        return self.getArtifact(defn, StoryController, **kwargs)
    def getProject(self,defn,**kwargs):
        kwargs['view_type'] = ProjectView
        kwargs['mini_view_type'] = ProjectMinView
        kwargs['get_artifact'] = 'newProject'
        kwargs['model'] = ProjectModel
        kwargs['folder'] = 'projects'
        return self.getArtifact(defn, ProjectController, **kwargs)
    def getRelease(self,defn,**kwargs):
        kwargs['view_type'] = ReleaseView
        kwargs['mini_view_type'] = ReleaseMinView
        kwargs['get_artifact'] = 'newRelease'
        kwargs['model'] = ReleaseModel
        kwargs['folder'] = 'releases'
        return self.getArtifact(defn, ReleaseController, **kwargs)
    def getSprint(self,defn,**kwargs):
        kwargs['view_type'] = SprintView
        kwargs['mini_view_type'] = SprintMinView
        kwargs['get_artifact'] = 'newSprint'
        kwargs['model'] = SprintModel
        kwargs['folder'] = 'sprints'
        return self.getArtifact(defn, SprintController, **kwargs)
    def getStory(self,defn,**kwargs):
        kwargs['view_type'] = StoryView
        kwargs['mini_view_type'] = MinStoryView
        kwargs['get_artifact'] = 'newStory'
        kwargs['model'] = StoryModel
        kwargs['folder'] = 'stories'
        return self.getArtifact(defn, StoryController, **kwargs)
    def getTask(self,defn,**kwargs):
        kwargs['view_type'] = TaskView
        kwargs['mini_view_type'] = TaskMinView
        kwargs['get_artifact'] = 'newTask'
        kwargs['model'] = TaskModel
        kwargs['folder'] = 'tasks'
        return self.getArtifact(defn, TaskController,**kwargs)
    def getArtifact(self,defn,ctrl,**kwargs):
        _p = kwargs.setdefault('controller', None)
        if _p is None:
            _p = ctrl(self,defn,**kwargs)
        return _p
    def remove_project_view(self,w):
        super(StoryApp,self).remove_widget(w)
    def newBacklog(self,ctrl):
        return self.add_new_artifact(ctrl, CONTAINERS_BACKLOG, 
                              'viewCurrentBacklog', self.backlog[ctrl.Id][1])
    def newProject(self,ctrl):
        return self.add_new_artifact(ctrl,
                                     CONTAINERS_PROJECT,
                                     'viewCurrentProject',
                                     self.artifacts[ctrl.Id][1])
    def newRelease(self,ctrl):
        return self.add_new_artifact(ctrl,
                                     CONTAINERS_RELEASE,
                                     'viewCurrentRelease',
                                     self.artifacts[ctrl.Id][1])
    def newSprint(self,ctrl):
        return self.add_new_artifact(ctrl,
                                     CONTAINERS_SPRINT,
                                     'viewCurrentSprint',
                                     self.artifacts[ctrl.Id][1])
    def newStory(self,ctrl):
        return self.add_new_artifact(ctrl,
                                     CONTAINERS_STORY, 
                                     'viewCurrentStory',
                                     self.artifacts[ctrl.Id][1])
    def newTask(self,ctrl):
        return self.add_new_artifact(ctrl,
                                     CONTAINERS_TASK,
                                     'viewCurrentTask',
                                     self.artifacts[ctrl.Id][1])
    def add_new_artifact(self, ctrl, container, callback, ret):
        for c in container:
            _lbl = MyImageButton(id=ctrl.Id, size=self._default_button_size,
                                 image=self._default_image)
            self.check_btn_width(_lbl)
            _lbl.push_handlers(on_press=curry(self.__getattribute__(callback), 
                                                                        _lbl))
            self.__getattribute__(c).add_widget(_lbl)
            ret[c] = _lbl
        return ret
            
    def viewCurrentBacklog(self,lbl, *largs): #IGNORE:W0613
        self.view_current_Artifact(lbl, 'current_backlog',\
                                    'flow_backlog_select', 'backlog')
    def viewCurrentProject(self,lbl, *largs): #IGNORE:W0613
        self.view_current_Artifact(lbl, 'current_project',\
                                    'flow_projects_select', 'projects')
    def viewCurrentRelease(self,lbl, *largs): #IGNORE:W0613
        self.view_current_Artifact(lbl, 'current_release',\
                                    'flow_release_select', 'releases')
    def viewCurrentSprint(self,lbl, *largs): #IGNORE:W0613
        self.view_current_Artifact(lbl, 'current_sprint',\
                                    'flow_sprint_select', 'sprints')
    def viewCurrentStory(self,lbl, *largs): #IGNORE:W0613
        self.view_current_Artifact(lbl, 'current_story',\
                                    'flow_story_select', 'stories')
    def viewCurrentTask(self,lbl, *largs): #IGNORE:W0613
        self.view_current_Artifact(lbl, 'current_task',\
                                    'flow_task_select', 'tasks')
    def view_current_Artifact(self,lbl,curr,flow_select, container):
        try:
            _c = self.__getattribute__(curr)[0]
        except Exception: #IGNORE:W0703
            _c = Dummy()
            _c.Id = ''

        idu =  lbl.Id if isinstance(lbl, ArtifactController)\
                      else lbl._id #IGNORE:W0212          
        if _c.Id != idu:
            #set current artifact to the one selected
            self.__getattribute__(flow_select)\
                            (self.__getattribute__(container)[lbl._id][0]) #IGNORE:W0212
            return #to avoid add then removing the same widget
        _view = _c.view
        if _view in self.container.children:
            super(StoryApp,self).remove_widget(_view)
        else:
            super(StoryApp,self).add_widget(_view)
    def addMainControls(self):
        self.main_ctlrs_container.add_widget(self.createNewProjectButton())
        self.main_ctlrs_container.add_widget(self.createNewReleaseButton())
        self.main_ctlrs_container.add_widget(self.createNewSprintButton())
        self.main_ctlrs_container.add_widget(self.createNewStoryButton())
        self.main_ctlrs_container.add_widget(self.createNewTaskButton())
        self.main_ctlrs_container.add_widget(self.createNewBacklogFlowButton())
        self.main_ctlrs_container.add_widget(self.createNewProjectFlowButton())
        self.main_ctlrs_container.add_widget(self.createNewReleasesFlowButton())
        self.main_ctlrs_container.add_widget(self.createNewSprintFlowButton())
        self.main_ctlrs_container.add_widget(self.createNewStoryFlowButton())
        self.main_ctlrs_container.add_widget(self.createNewTaskFlowButton())
        self.canvas.add_widget(self.main_ctlrs_container)
    def createNewBacklogFlowButton(self):
        return self.create_button('Browse\nBacklog...',
                                  curry(self._flow_pressed,\
                        'backlog_flow_open',self.dragable_backlog_flow))
    def createNewProjectFlowButton(self):
        return self.create_button('Browse\nProjects...',\
                                  curry(self._flow_pressed,\
                        'projects_flow_open',self.dragable_project_flow))
    def createNewReleasesFlowButton(self):
        return self.create_button('Browse\nReleases...',
                                  curry(self._flow_pressed,\
                        'releases_flow_open',self.dragable_release_flow))
    def createNewSprintFlowButton(self):
        return self.create_button('Browse\nSprints...',
                                  curry(self._flow_pressed,\
                        'sprint_flow_open',self.dragable_sprint_flow))
    def createNewStoryFlowButton(self):
        return self.create_button('Browse\nStories...', 
            curry(self._flow_pressed,\
                        'story_flow_open',self.dragable_story_flow))
    def createNewTaskFlowButton(self):
        return self.create_button('Browse\nTasks...',
                                  curry(self._flow_pressed,\
                        'task_flow_open',self.dragable_task_flow))
    def create_button(self,label,callback):#,flag=None,widget=None):
        _btn = MTButton(multiline=True, label=label, anchor_x='left',
                        halign='left', padding_x=8)
        _btn.connect('on_press', callback)
        _max = 1
        for _s in label.splitlines():
            _l = len(_s)
            if _l > _max:
                _max = _l
        _btn.width = min(int(_max * pixels),100)
        return _btn
    def update_story_btn_name(self,story):
        idu = story.Id
        btn = self.buttons[idu]
        if btn.id == idu:
            btn.label = story.Name
            self.check_btn_width(btn)
        return
    def trash(self,artifact,atype=None):
        if atype is None:# or type is 'stories':
            btn = self.buttons[artifact.Id]
            lbl = self.labels[artifact.Id]
            self.backlog_list_layout.remove_widget(btn)
            self.story_flow.remove_widget(lbl)
            self.remove_widget(artifact)
            del self.artifacts[artifact.Id]
        else:
            if artifact.Id in self.Artifacts:
                dic = self.Artifacts[artifact.Id][1]
                for l in dic.keys():
                    self.__getattribute__(l).remove_widget(dic[l])
                del self.Artifacts[artifact.Id]
                super(StoryApp,self).remove_widget(artifact.view)
        return
    def check_btn_width(self,btn):
        _label = btn.label
        _w = int(len(_label) *pixels)
        if _w > btn.width:
            if not btn.multiline:
                btn.multiline = True
            btn.label = '\n'.join(_label.split('-'))
    def list_button_pressed(self, btn):
        ctrl = self.backlog[btn.id][0]
        view = ctrl.view
        if view not in self.container.children:
            super(StoryApp,self).add_widget(view)
        else: ctrl.app_btn_pressed()
    def fullscreen(self, *largs, **kwargs):
        super(StoryApp,self).fullscreen()
        root_win = self.parent.children
        for rw in root_win:
            try:
                if rw.label == 'Back':
                    self.parent.remove_widget(rw)
                    rw.label = 'Exit'
                    rw.pos = (self.width - 100, 0)
                    rw.size=self._default_button_size
                    self.main_ctlrs_container.add_widget(rw)
                    Log.debug('Renamed back label to exit')
                    return
            except Exception: #IGNORE:W0703
                pass
    def unfullscreen(self, *largs, **kwargs):
        self.root_window.remove_widget(self)
        stopTouchApp()
        Log.info('Closing CMAP application')
    def close(self,touch=None):
        #close all the artifacts
        for a in self.artifacts.values():
            a[0].close()
        for b in self.backlog.values():
            b[0].close()
        self.add_to_git()
        AsyncHandler().shutdown()
        super(StoryApp, self).close(touch)
    def add_to_git(self):
        AsyncHandler().save(None, 'Commit session edits')    
    def container_reset_children(self,container):
        f = self.__getattribute__(container)
        x = len(f.children) -1
        while x > 0:
            w = f.children[x]
            f.remove_widget(w) 
            x = len(f.children) -1
    
    @property        
    def Artifacts(self):
        return self.artifacts