miércoles, 23 de octubre de 2013

Redmine $%&# ChiliProject - Fork of Project Management System



This is an entry about the wonderful world of forks.

Firstly, general requirements of project management system:

  • Documentation management
  • Incidents tracking system: Incidents, queries and requirements [2] )
  • Communication: News, forum, blogs, wiki’s, IM, user guides, development guides, styles guide, and so on.
  • Community management system: User, roles, access control,
  • Code management: SCM, automatic documentation, automatic test, continuous integration), releases.
Redmine is a project management system, it is free software licensed as GNU General Public License v2.  Redmine is written using the Ruby on Rails framework.


Some of the main features of Redmine are:
  • Multiple projects support
  • Flexible role based access control
  • Flexible issue tracking system
  • Gantt chart and calendar
  • News, documents & files management
  • Feeds & email notifications
  • Per project wiki
  • Per project forums
  • Time tracking
  • Custom fields for issues, time-entries, projects and users
  • SCM integration:
    • SVN, CVS, Git, Mercurial, Bazaar and Darcs
  • Issue creation via email
  • Multiple LDAP authentication support
  • User self-registration support
  • Multilanguage support
  • Multiple databases support 
  • Plugin model for extension 
  •  REST API
ChiliProject is a web based project management system. It supports your team throughout the complete project life cycle, from setting up and discussing a project plan, over tracking issues and reporting work progress to collaboratively sharing knowledge. ChiliProject is free software licensed as GNU General Public License v2.


Some of the main features of ChiliProject are:

  • Project planning (Roadmap, Gantt diagram)
  • Issue tracking and progress reporting (activities, notification, feeds)
  • Sharing knowledge collaboratively (Wiki, SCM:Git or subversion)
  • Keep control of your expenses (Track the time, reports)
  • Interact with externals (News, forum)
  • Setup your work flows to support your business processes (Work flow, role, permissions)
  • Plugin model for extension

In February 2011 a group of developers from the Redmine community, in particular, 6 developers.     
Their reasons for creating a fork were:
-          The maintenance and evolution of Redmine has not been as predictable and responsive
-          Patches were too sporadic
-          Lacked a clear methodology.
-          The efforts of community via public and private forums to discuss the goals and future direction with the project manager of Redmine failed.
-          The current project manager did not share these priorities.

Therefore, they created this fork, called ChiliProject, which has been launched on the basis of a 1.0.4 version of Redmine, with these principles:
  • Depends upon people producing useful software and supporting documentation, contributing as a team for the benefit of the community.
  • Reflects a spirit of collaboration and fun while garnering community feedback and providing good governance that allows for businesses to confidently invest in further development.
  • Is open to the participation of anyone who can contribute value and who is willing to work with the community.
Actually, it seems that Chiliproject have not had so much successful during these years.
At least they have the same colour in their logos.
In conclusion, in a first analysis seems to be a problem of communication of the new community manager or managers, that he  did not know how to integrates these concerns.

References:
[2] Example of requirement management using issue tracker system http://www.redmine.org/projects/redmine/issues?set_filter=1&tracker_id=2





martes, 22 de octubre de 2013

Python parser for command-line options, arguments and sub-commands (argparse)

The argparse module makes it easy to write user-friendly command-line interfaces.
Right after we are going to show basic examples of use of this parser.

  • Let us start with a very simple example which does (almost) nothing:
SOURCE CODE

1
2
3
4
import argparse

parser = argparse.ArgumentParser()
parser.parse_args()

OUTPUT


1
2
3
4
5
$ ./argparse1.py -h
usage: argparse1.py [-h]

optional arguments:
  -h, --help  show this help message and exit





  • Let us configure description, version and epilogue


  • SOURCE CODE

    1
    2
    3
    4
    5
    6
    7
    import argparse
    
    # prog = sys.argv[0], you could change if you want, prog="myprogram"
    parser = argparse.ArgumentParser(description='Description',
                                     version="%(prog)s 0.1",
                                    epilog="Epilogue")
    parser.parse_args()
    

    OUTPUT


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    $ ./argparse2.py -h
    usage: argparse2.py [-h] [-v]
    
    Description
    
    optional arguments:
      -h, --help     show this help message and exit
      -v, --version  show program's version number and exit
    
    Epilogue
    

    • Let us introduce a positional argument

    SOURCE CODE


    1
    2
    3
    4
    5
    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument("argument")
    parser.parse_args()
    


    OUTPUT


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    $ ./argparse3.py -h
    usage: argparse3.py [-h] argument
    
    positional arguments:
      argument    Information about argument
    
    optional arguments:
      -h, --help  show this help message and exit
      -v, --version  show program's version number and exit
    
    Epilogue
    
    • Let us introduce optional arguments
    SOURCE CODE


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    import argparse
    
    parser = argparse.ArgumentParser()
    # A optional argument A with necessary value
    parser.add_argument("-a", "--argument-A", 
                        help="Information about argument with pattern [-a ARGUMENT]")
    # A optional argument B without necessary value
    parser.add_argument("-b", "--argument-B", 
                        help="Information about b argument with pattern [-b] ",
                        action="store_true")
    parser.parse_args()
    


    OUTPUT


    1
    2
    3
    4
    5
    6
    7
    8
    $ ./argparse4.py -h
    usage: argparse4.py [-h] [-a ARGUMENT_A] [-b]
    
    optional arguments:
      -h, --help            show this help message and exit
      -a ARGUMENT_A, --argument-A ARGUMENT_A
                            Information about argument with pattern [-a ARGUMENT]
      -b, --argument-B      Information about b argument with pattern [-b]
    
    • Let us introduce subcommands
    SOURCE CODE

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    import argparse
    
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(help='sub-command help')
    # create the parser for the "a" command
    parser_a = subparsers.add_parser('a', help='a help')
    
    # create the parser for the "b" command
    parser_b = subparsers.add_parser('b', help='b help')
    
    parser.parse_args()
    


    OUTPUT


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    $ ./argparse5.py -h
    usage: argparse5.py [-h] {a,b} ...
    
    positional arguments:
      {a,b}       sub-command help
        a         a help
        b         b help
    
    optional arguments:
      -h, --help  show this help message and exit
    
    • Let us introduce mutual exclusion of arguments
    SOURCE CODE 

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    import argparse
    
    parser = argparse.ArgumentParser()
    group1 = parser.add_mutually_exclusive_group(required=True)
    group1.add_argument("-a", "--argument-A", action="store_true")
    group1.add_argument("-b", "--argument-B", action="store_false")
    
    group2 = parser.add_mutually_exclusive_group(required=True)
    group2.add_argument("-c", "--argument-C", help="c help")
    group2.add_argument("-d", "--argument-D", help="d help")
    
    parser.parse_args()
    


    OUTPUT


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    $ ./argparse6.py -h
    usage: argparse6.py [-h] (-a | -b) (-c ARGUMENT_C | -d ARGUMENT_D)
    
    optional arguments:
      -h, --help            show this help message and exit
      -a, --argument-A
      -b, --argument-B
      -c ARGUMENT_C, --argument-C ARGUMENT_C
                            c help
      -d ARGUMENT_D, --argument-D ARGUMENT_D
                            d help
    

    • Finally, let us see how to execute action of optional argument, although positional argument is empt
     SOURCE CODE (partial)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    import sys
    import argparse
    
    # Parse the target url
        parser.add_argument ("url" ,
                            nargs='?',
                            help="target URL")
    # Parse argument to show license
        parser.add_argument ("-l",
                            "--license",
                            help="show license",
                            action="store_true")
    
     # Print license
        if args.license:
            print st.WS_LICENSE
            sys.exit(0)
    

    The tricky is to use nargs='?' for positional argument and sys.exit(0)

    OUTPUT

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    $ ./argparse7.py  -h
    usage: argparse7.py [-l] [url]
    
    Description
    
    positional arguments:
      url                   target URL
    
    optional arguments:
      -h, --help            show this help message and exit
      -v, --version         show program's version number and exit
      -l, --license         show license
    


    References:
     [1] http://docs.python.org/dev/library/argparse.html 


    The Python unit testing framework

    The PyUnit is a unit testing framework, which is a Python implementation of famous JUnit, which is the de facto standard unit testing framework for its different language.

    Python implements this standard unit testing with the module unittest.

    Firstly, a piece of theory on testing

     In order to design a comprehensive testing plan we want to take advantage of the following types of tests:

    unit-testing
        Testing done on a single subroutine or module.
    functional-testing
        Testing for a given functional group of subroutines or modules, for example, testing model dynamics alone without the model physics.
    system-testing
        Testing done on the whole system.

    In particular,  proper unit-tests will do the following:
    •     Applicable requirements are checked.
    •     Exercise every line of code.
    •     Check that the full range of possible input data works.
    •     Boundary analysis - logical statements that refer to threshold states are checked to ensure they are correct.
    •     Check for bad input data.
    A test case in Python is created by subclassing unittest.TestCase.
     - a setUp() method is defined, the test runner will run that method prior to each test.
     - a tearDown() method is defined, the test runner will invoke that method after each test. I

    Main assert functions:
    1. assertEqual(a, b)     a == b    
    2. assertNotEqual(a, b)     a != b    
    3. assertTrue(x)     bool(x) is True    
    4. assertFalse(x)     bool(x) is False
    Tthere are another assert function, and in particular in the following example we are going to use to test assert raised (assertRaises)

    A suite is a group of test cases.

    Finally, we are going to show an example of an independet class (TestKelvinToFahrenheit) that performeces the unit tests

    •  File kelvinToFahrenheitFunctions.py - Function to be tested

    1
    2
    3
    def KelvinToFahrenheit(Temperature):
        assert (Temperature >= 0), "Colder than absolute zero!"
        return ((Temperature - 273) * 1.8) + 32
    

    • File kelvinToFahrenheit.py Main function that only prints different values

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    #!/usr/bin/env python
      
    import kelvinToFahrenheitFunctions as k2f
    
    if __name__ == '__main__':
        print k2f.KelvinToFahrenheit(273)
        print k2f.KelvinToFahrenheit(104)
        print k2f.KelvinToFahrenheit(454)
        print int(k2f.KelvinToFahrenheit(505.78))
        print k2f.KelvinToFahrenheit(-5)
    

    • File TestKelvinToFahrenheit.py - Independent class and file to testing function kelvinToFahrenheit

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #!/usr/bin/env python
    
    import unittest
    import math
    
    import kelvinToFahrenheitFunctions as k2f
    
    class TestKelvinToFahrenheit (unittest.TestCase):
        # Test positive value, for instance 104 and 454
        def test_positive_value (self):
            fahr_temp = k2f.KelvinToFahrenheit(104)
            self.assertEqual(fahr_temp, -272.2)
            fahr_temp = k2f.KelvinToFahrenheit(454)
            self.assertEqual(fahr_temp, 357.8)
    
        # Test boundary value 273 and 0
        def test_boundary_value (self):
            fahr_temp = k2f.KelvinToFahrenheit(273)
            self.assertEqual(fahr_temp, 32)
            fahr_temp = math.ceil(k2f.KelvinToFahrenheit(255.22))
            self.assertEqual(fahr_temp, 0)
    
        # Test assert raises, in this case colder than absolute zero
        # something impossible.
        def test_colder_abs_zero (self):
            self.assertRaises(AssertionError,
                                k2f.KelvinToFahrenheit,
                                - 1)
    
    if __name__ == '__main__':
        unittest.main()
    

    The output would be

    $ ./TestKelvinToFahrenheit.py
    
    ...
    
    ----------------------------------------------------------------------
    
    Ran 3 tests in 0.001s
    
    OK
    

    and with the --verbose option


    $ ./TestKelvinToFahrenheit.py --verbose
    
    test_boundary_value (__main__.TestKelvinToFahrenheit) ... ok
    
    test_colder_abs_zero (__main__.TestKelvinToFahrenheit) ... ok
    
    test_positive_value (__main__.TestKelvinToFahrenheit) ... ok
    
    ----------------------------------------------------------------------
    
    Ran 3 tests in 0.001s
    
    OK
    
    OK
    


    References:
    [1] http://docs.python.org/3.3/library/unittest.html
    [2] http://www.cesm.ucar.edu/working_groups/Software/dev_guide/dev_guide/node13.html
    [3] http://blog.stevensanderson.com/2009/08/24/writing-great-unit-tests-best-and-worst-practises/
    [4] http://stackoverflow.com/questions/129507/how-do-you-test-that-a-python-function-throws-an-exception


    lunes, 21 de octubre de 2013

    vi as Python IDE

    There are many editors for Python, but in this entry we are going to explain how to configure the simplest one, Vi.

    Information about Used Host Operating System

    Linux gon 3.11.0-12-generic #19-Ubuntu SMP Wed Oct 9 16:20:46 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

    $ lsb_release -a
    Distributor ID:    Ubuntu
    Description:    Ubuntu 13.04
    Release:    13.04
    Codename:    raring



    Vi information

    $ vi --version
    VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Apr  2 2013 09:17:34)
    Included patches: 1-547


    Basic configuration

    The vi configuration file is .vimrc in home directory. An this is the aspect with basic configuration















    Firstly, we are going to enable syntax coloring, automatic indentation and set line number with the command set number

    " Basic configuration 
     " Automatically enable syntax coloring and automatic indentation for 
     " Python code  
     syntax on 
     filetype indent plugin on 
     " Set line numbers 
     set number 
    














    In style guide for Python code, spaces are the preferred indentation method, then it is useful configure tag in vi equal to 4 spaces. The command for that is

      " Spaces instead of tabs  
      set expandtab  
      " 4 space for tabs  
      set tabstop=4  
    














    Indentation in Python code is essential, so using command set autoindent always set auto indenting on.

    set autoindent 
    

    To enable file type detection we can use the following command:

    filetype indent plugin on  
    

    If you use a dark background, this command may help adjust the default colours for better contrast:

    set background=dark  
    














    You can enable code folding using the following configuration

    set foldmethod=indent
    set foldlevel=99
    

    Then you will be able to be inside a method and type 'za' to open and close a fold.













    Advanced configuration and tools

    Lastly, we are going to configure advanced features only for brave coder.

    For using this advanced configuration is compulsory the configuration of reference [1].

    Tasks list
    There are some coders that set mark as #TODO or #FIXME to create a list of tasks. Using the vim module tasklist the following configuration in .vimrc

     map td TaskList
     nnoremap v TaskList
     map td TaskList
    

    you can show tasks list: hit <leader>td  ("\td")








     







    Pep8

    You can validate your code with  Style Guide for Python Code (Pep8) using pep8 vim plugin.

    I have used the command "pip install flake8" to install  flake8 pyflakes pep8 mccabe.

    Pressing F5 will run it using the "quickfix" feature.
    This way you can "navigate" through errors using :cn and other standard commands.















    Tab Completion and Documentation
    We are going to use the SuperTab plugin to check the context of the code you are working on and choose the best for the situation.

    This is the configuration in .vimrc

    " SuperTab plugin
    au FileType python set omnifunc=pythoncomplete#Complete
    let g:SuperTabDefaultCompletionType = "context"
    

    Pressing tab after the string for example "cl" complete with the template of a class














    Besides, pydoc preview to get the most useful information out of the code completion, that gives us the ability to hit <leader>pw when our cursor is on a module














    Window Splits Tool
    Sometimes code folding isn't enough; you may need to start opening up multiple windows and working on multiple files at once or different locations within the same file. To do this in vim, you can use these shortcuts:

    Vertical Split : Ctrl+w + v
    Horizontal Split: Ctrl+w + s
    Close current windows: Ctrl+w + q
    






















    References:
    [1] [http://sontek.net/blog/detail/turning-vim-into-a-modern-python-ide
    [2] http://www.vim.org/scripts/script.php?script_id=2914