Follow QueSucede on Twitter
    hacker emblem

    Agile software development models in Python 3

    IconDetails...




    Agile software development models in Python 3

    Some links:

    Managing development teams

    Trying to effectively manage several teams of developers is a challenge, to say the least.

    Obviously, in a typical software development process there are quite a few different actors and entities, including (in no specific order):

    • backlog items
    • bugs
    • developers
    • iterations (in the Agile/Scrum sense of the word)
    • (project) portfolio
    • projects
    • products
    • release criteria
    • requirements
    • risks
    • stakeholders
    • tasks
    • teams

    Each one of the above actors or entities have their own properties and/or include references to the other actors/entities. For example, a bug has a description, a severity and a priority. A bug in itself, is linked back to a specific backlog-item. Backlog-items, however, also have a list of associated tasks, and so on.

    Anyway, code speaks louder than words, so take a look at the (unfinished, Python 3) source below:

    entity.py:

    import uuid
    
    from datetime import datetime
    
    #===============================================================================
    
    class Entity():
        
        def __init__(self,
                identifier=None,
                name='undefined',
                comment='',
                active=True):
            self.__identifier = (uuid.uuid1() if identifier is None else identifier)
            self.__creation_date = datetime.now()
            self.name = name
            self.comment = comment
            self.active = active
            
        @property
        def identifier(self):
            return self.__identifier
        
        @property
        def creation_date(self):
            return self.__creation_date
    

    developer.py:

    import uuid
    
    from entity import Entity
    
    #===============================================================================
    
    class Developer(Entity):
        
        def __init__(self, 
                identifier=uuid.uuid1(), 
                name='undefined', 
                comment='', 
                position='undefined'):
            super().__init__(identifier, name, comment)
            
            self.__position = position
            self.__knowledge_areas = []
            
        @property
        def position(self):
            return self.__position
        
        @property
        def knowledge_area(self):
            return self.__knowledge_areas
        
        def add_knowledge_area(self, knowledge_area):
            self.__knowledge_areas.append(knowledge_area)
            
        def remove_knowledge_area(self, knowledge_area):
            self.__knowledge_areas = 
                list(item for item in self.__knowledge_areas if item != knowledge_area)
    

    task.py:

    from entity import Entity
    from developer import Developer
    
    #===============================================================================
    
    class Task(Entity):
        
        def __init__(self,
                description,
                identifier=None,
                name='undefined',
                comment='',
                active=True):
            super().__init__(identifier, name, comment, active)
            self.description = description
            # one of: not started, in progress, impeded or done
            self.__status = 'not started'
            # list of Developer objects of which the first one is the point person
            self.__developers = []  
            # minimum amount of time that a task can take
            self.__estimated_hours = 1
            
        @property
        def status(self):
            return self.__status
        
        @status.setter
        def status(self, value):
            states = {'not started', 'in progress', 'impeded', 'done'}
            if len(states - {value.lower()}) == 4:
                # temporary code, should raise an appropriate exception, 
                # i.e., unknown status
                pass  
            else:
                self.__status = value
        
        @property
        def developers(self):
            return self.__developers
        
        @property
        def estimated_hours(self):
            return self.__estimated_hours
        
        @property
        def point_person(self):
            return self.__developers[0]
        
        @point_person.setter
        def point_person(self, value):
            # check to see if the point person (passed through as a parameter) 
            # is already in the list of developers assigned to this task and 
            # if she is, set her as the first person in the list of developers
            # effectively making her the point person for this task 
            # (see the 'point_person' property). If the point person is not 
            # in the list of developers (i.e., assigning a new developer to 
            # the task), then prepend her to the list of developers.
            pass  
        
        def add_developer(self, developer):
            self.__developers.append(developer)
            
        def remove_developer(self, identity):
            pass
    
    

    backlogitem.py:

    from entity import Entity
    from task import Task
    
    #===============================================================================
    
    class BacklogItem(Entity):
        
        def __init__(self,
                description,
                identifier=None,
                name='undefined',
                comment='',
                active=True):
            super().__init__(identifier, name, comment, active)
            
            self.description = description
            self.__tasks = []  # list of Task objects
            self.__bugs = []  # list of Bug objects
            
        @property
        def tasks(self):
            return self.__tasks
        
        @property
        def bugs(self):
            return self.__bugs
        
        def add_task(self, task):
            self.__tasks.append(task)
            
        def remove_task(self, identity):
            pass
        
        def add_bug(self, bug):
            self.__bugs.append(bug)
            
        def remove_bug(self, identity):
            pass
    

    Updated on May 03, 2010

    team.py:

    from entity import Entity
    
    class Team(Entity):
    
        def __init__(self,
                identifier=None,
                name='undefined',
                comment='',
                active=True):
    
            super().__init__(identifier, name, comment, active)
            self.__developers = []  # list of Developer objects
    
        @property
        def developers(self):
            return self.__developers
    
        def add_developer(self, developer):
            self.__developers.append(developer)
    
        def remove_developer(self, identity):
            pass
    

    stakeholder.py:

    from entity import Entity
    
    #===============================================================================
    
    class Stakeholder(Entity):
    
        def __init__(self,
                identifier=None,
                name='undefined',
                comment='',
                active=True):
            super().__init__(identifier, name, comment, active)
    

    risk.py:

    from entity import Entity
    
    #===============================================================================
    
    class Risk(Entity):
    
        def __init__(self,
                identifier=None,
                name='undefined',
                comment='',
                active=True,        
                description='undefined',
                probability=1,
                impact=0,
                response_plan=False):
            super().__init__(identifier, name, comment, active)
            self.description = description
            self.probability = probability
            self.impact = impact
            self.response_plan = response_plan
    
        @property
        def score(self):
            return self.probability * self.impact
    

    requirement.py:

    from entity import Entity
    from stakeholder import Stakeholder
    
    #===============================================================================
    
    class Requirement(Entity):
    
        def __init__(self,
                description,
                identifier=None,
                name='undefined',
                comment='',
                active=True,
                requestor=None):
            super().__init__(identifier, name, comment, active)
            self.description = description
            self.__requestor = requestor  # reference to a Stakeholder object
    
        @property
        def requestor(self):
            return self.__requestor
    
        @requestor.setter
        def requestor(self, value):
            # Do we want to limit the requestor to being the product owner? If that is the case,
            # then we can remove this property completely from the Requirement class as the 
            # product owner is already tracked in the Product class.
            self.__requestor = value
    

    releasecriterion.py:

    #===============================================================================
    
    class ReleaseCriterion:
      
        def __init__(self,
                description,
                comment=''):
            self.description = description
            self.comment = comment
            self.specific = True
            self.measurable = True
            self.attainable = True
            self.relevant = True
            self.trackable = True
    
        @property
        def is_valid(self):
            return self.specific 
                and self.measurable 
                and self.attainable 
                and self.relevant 
                and self.trackable
    

    projectscope.py:

    from product import Product
    
    #===============================================================================
    
    class ProjectScope:
    
        def __init__(self, description):
            self.description = description
            self.__products = []  # list of Product objects
            self.__dependencies = []  # list of other Project objects, i.e., dependencies
            self.__release_critera = []  # list of ReleaseCriterion objects
    
        @property
        def products(self):
            return self.__products
    
        @property
        def dependencies(self):
            return self.__dependencies
    
        @property
        def release_criteria(self):
            return self.__release_criteria
    
        @property
        def is_valid(self):
            result = True
            for release_criterion in self.__release_criteria:
                if release_criterion.is_valid == False:
                    result = False
                    break
            return result
    

    project.py:

    from entity import Entity
    from projectscope import ProjectScope
    from risk import Risk
    from iteration import Iteration
    
    #===============================================================================
    
    class Project(Entity):
    
        def __init__(self,
                identifier=None,
                name='undefined',
                comment='',
                active=True,
                description='undefined'):
            super().__init__(identifier, name, comment, active)
    
            self.__scope = ProjectScope(description)
            self.__stakeholders = []  # list of Stakeholder objects
            self.__risks = []  # list of Risk objects
            self.__iterations = []  # list of Iteration/Sprint objects
    
            # What about the 'project driver' matrix? That is:
            # Priority          Rank
            # ======================
            # Release date      1
            # Feature set       2
            # Low defects       3
    
        @property
        def scope(self):
            return self.__scope
    
        @property
        def stakeholders(self):
            return self.__stakeholders
    
        @property
        def risks(self):
            return self.__risks
    
        @property
        def iterations(self):
            return self.__iterations
    
        def add_stakeholder(self, stakeholder):
            pass
    
        def remove_stakeholder(self, identity):
            pass
       
        def add_risk(self, risk):
            pass
    
        def remove_risk(self, identity):
            pass
    
        def add_iteration(self, iteration):
            pass
    
        def remove_iteration(self, identity):
            pass
    

    product.py:

    import uuid
    
    from entity import Entity
    
    #===============================================================================
    
    class Product(Entity):
    
        def __init__(self,
                identifier=None,
                name='undefined',
                comment='',
                active=True,
                description='undefined',
                product_owner=None):
            super().__init__(identifier, name, comment, active)
    
            self.description = description
            self.__requirements = []  # list of Requirement objects
            self.__product_owner = product_owner
    
        @property
        def requirements(self):
            """Return a list of the software functionality."""
            return self.__requirements
    
        @property
        def product_owner(self):
            return self.__product_owner
    
        @product_owner.setter
        def product_owner(self, value):
            self.__product_owner = value
    
        def add_requirement(self, requirement):
            self.__requirements.append(requirement)
    
        def remove_requirement(self, identity):
            pass
    

    portfolio.py:

    from entity import Entity
    from project import Project
    from team import Team
    
    #===============================================================================
    
    class Portfolio(Entity):
    
        def __init__(self,
                identifier=None,
                name='undefined',
                comment='',
                active=True):
            super().__init__(identifier, name, comment, active)
    
            self.__projects = []  # list of Project objects (sinks)
            self.__teams = []  # list of Team objects (sources)
    
        @property
        def projects(self):
            return self.__projects
    
        @property
        def teams(self):
            return self.__teams
    
        def add_project(self, project):
            self.__projects.append(project)
    
        def remove_project(self, identity):
            pass
    
        def add_team(self, team):
            self._teams.append(team)
    
        def remove_team(self, identity):
            pass
    

    iteration.py:

    from entity import Entity
    from backlogitem import BacklogItem
    
    #===============================================================================
    
    class Iteration(Entity):
    
        def __init__(self,
                start_date,
                end_date,
                identifier=None,
                name='undefined',
                comment='',
                active=True):
            super().__init__(identifier, name, comment, active)
    
            self.start_date = start_date
            self.end_date = end_date
            self.__backlog_items = []  # list of (committed) BacklogItem objects
    
        @property
        def backlog_items(self):
            return self.__backlog_items
    
        def add_backlog_item(backlog_item):
            self.__backlog_items.append(backlog_item)
    
        def remove_backlog_item(backlog_item):
            pass
    

    bug.py:

    from entity import Entity
    
    #===============================================================================
    
    class Bug(Entity):
       
        def __init__(self,
                identifier=None,
                name='undefined',
                comment='',
                active=True,
                description='undefined',
                severity='critical',
                priority='now'):
            super().__init__(identifier, name, comment, active)
    
            self.description = description
            self.__severity = severity
            self.__priority = priority
    
        @property
        def severity(self):
            return self.__severity
    
        @property
        def priority(self):
            return self.__priority
    
        @severity.setter
        def severity(self, value):
            # Severity is Technical but Absolute: an assessment of the impact of the bug
            # without regard to other work in the queue or the current schedule.
            states = {'critical', 'high', 'medium', 'low'}
            if len(states - {value.lower()}) == 4:
                # temporary code, should raise an appropriate exception, e.g., unknown severity
                pass
            else:
                self.__severity = value
    
        @priority.setter
        def priority(self, value):
            # Priority is Business, but Relative: a subjective evaluation of how important
            # an issue is, given other tasks in the queue and the current schedule.
            states = {'now', 'p1', 'p2', 'p3'}
            # Now: drop everything and take care of it as soon as you see this (usually
            # for blocking bugs) 
            # P1: fix before next build to test 
            # P2: fix before final release 
            # P3: we probably won't get to these, but we want to track them anyway 
            if len(states - {value.lower()}) == 4:
                # temporary code, should raise an appropriate exception, e.g., unknown priority
                pass  
            else:
                self.__priority = value
    

    To be continued


    Click here to be able to create pages, upload images and file attachments, and link to other users and their pages.


    blog comments powered by Disqus