agile.open.connected
Essere agili?
Metodi, strumenti e integrazioni
Python “to rule them all” Massimo Azzolini
Massimo Azzolini
Founder - Project manager
RedTurtle - Ferrara - Italy
massimo@redturtle.it
@massimoazzolini
www.redturtle.it
Massimo Azzolini
Not an agile guru
being agile...
Massimo Azzolini
The agile manifesto
➡ Individuals and interactions
over processes and tools
➡ Working software
over comprehensive
documentation
➡ Customer collaboration
over contract negotiation
➡ Responding to change
over following a plan
12 principles
of agile manifesto
1. Satisfy the customer
2. Welcome changing requirements
3. Deliver working software frequently
4. Business people and developers work together
5. Build projects around motivated individuals.
6. Face-to-face conversation
7. Working software is the primary measure of progress
8. Agile processes promote sustainable development
9. Attention to technical and design excellence
10. Simplicity is essential
11. The best architectures and requirements emerge from self-organizing teams
12. Retrospective
12 principles of agile manifesto
Kanban?
Scrum?
Scrum
Kanban
so how can I make it
USEFUL?
Massimo Azzolini
The structure
Started in 1999 in Ferrara, in 2012
we have had:
★ 14 people in house
★ most backend developers
★ 4 freelancer (not RedTurtle)
★ 3 teams
★ 77 projects
★ 347 customer requests
★ 1943 tickets solved
★ 20K+ hours worked
ISO 9001
no, I mean really
USEF UL ?!
trainings and conferences
Scrum doesn’t completely fit to us
Kanban doesn’t completely fit to us
LOL
Massimo Azzolini
What is left?
1. Teams
2. Iterations
3. Agile team to embrace the verb
4. Introduce 1-2 new tools/methods after every agile meeting
5. Multiple kanban boards (personal vs group vs project)
6. WIP limit and customers’ delays
7. Value your time
8. Write everything
9. Code review
Massimo Azzolini
Which tools?
➡ Email?!
➡ Share Documents
‣ Google Drive
‣ Dropbox
➡ Organize tasks
‣ Trello o Kanbanery
‣ Simple management
(or other plone based
solutions)
‣ Basecamp
Penelope
getpenelope.github.com
Project journey
A new
project Google
Documentation
Project Manager
Customer
requests
Trac
Reports
Dashboard
Google
Documentation
Customer
Trac - tickets
Trac - roadmap
Dashboard
Trac
Developer
Time
management
Sentry Customer
requests
Time
management
Show me
the code!!!
OAuth/Login
Google OAuth2 example
[app:penelope]
...
pyramid.includes = velruse.providers.google
provider.google.consumer_key = 441361239240193
provider.google.consumer_secret = 52ef2618a1999eeec6d9c
provider.google.scope =
https://www.googleapis.com/auth/calendar
https://www.googleapis.com/auth/drive.apps.list
wgsi.ini
GDrive integration
GDrive Integration
@documents.auth_graceful
def get_google_document(context, request, **kwargs):
    try:
       folder_entry = request.gclient['DocsClient'].get_doclist(
uri='/feeds/default/private/full/%s/contents/' % folder_id)
folder = folder_entry.entry
    except (ValueError, RequestError), e:
       status = u'''Something is not working correctly.'''
    params.update(folder=folder, status=status)
Trac integration
TicketStore - Trac Integration
class ITicketStore(Interface):
    def get_tickets_for_project(self, project, request,
query=None, limit=None):
        """ """
    def get_requests_from_tickets(self, project, ticket_ids,
request=None):
        """ """
    def get_tickets_for_request(self, customer_request,
limit=None, request=None):
        """ """
Deform/Colander
Deform/Colander
@view_config(renderer='templates/form.pt', name='myform')
def myform(self):
class Mapping(colander.Schema):
name = colander.SchemaNode(
colander.String(),
description='Content name')
date = colander.SchemaNode(
colander.Date(),
widget=deform.widget.DatePartsWidget(),
description='Content date')
class Schema(colander.Schema):
number = colander.SchemaNode(
colander.Integer())
mapping = Mapping()
schema = Schema()
form = deform.Form(schema, buttons=('submit',))
return self.render_form(form)
Deform/Colander
Mandrill
def send(self, from_addr, recipients, data):
        # Ensure the message complies with RFC2822: use CRLF line endings
        message = data['msg']
        data = data['data']
        params = {}
        changes_body = data['changes_body']
        if changes_body:
            params['changes_body'] = rest2html(changes_body)
        if data['ticket']['new']:
            params['ticket_new'] = True
        params['ticket_body_hdr'] = data['ticket_body_hdr']
        params['ticket_link'] = data['ticket']['link']
        params['ticket_reporter'] = data['ticket']['reporter']
        params['ticket_owner'] = data['ticket']['owner']
        params['ticket_description'] = self.wiki2html(data['ticket']['description'])
        params['ticket_type'] = data['ticket']['type']
        params['ticket_status'] = data['ticket']['status']
        params['ticket_priority'] = data['ticket']['priority']
        params['ticket_milestone'] = data['ticket'].get('milestone')
        params['change_author'] = data['change'].get('author','')
        params['change_comment'] = self.wiki2html(data['change'].get('comment',''))
Mandrill
Sentry
Sentry-Penelope
class PenelopePlugin(IssuePlugin):
    
    def create_issue(self, request, group, form_data, **kwargs):
        proxy = TracXmlProxy(form_data['trac'], request=request)
        try:
            opts = {'type': 'defect',
                    'issuetype': 'sistemistica',
                    'customerrequest': '',
                    'owner': ''}
            ticket = proxy.ticket.create(form_data['title'],
                                         form_data['description'],
                                         opts)
        except Exception, e:
            msg = unicode(e)
            raise forms.ValidationError(_('Error communicating: %s') %(msg,))
        try:
            data = int(ticket)
        except Exception, e:
            raise forms.ValidationError(_('Error decoding response: %s') % (e,))
        return data
Solr - trac full text search
Questions ?
Massimo Azzolini
RedTurtle’s co-founder
and project manager
massimo@redturtle.it
tw: @massimoazzolini

Be agile: take back control over your work

  • 1.
    agile.open.connected Essere agili? Metodi, strumentie integrazioni Python “to rule them all” Massimo Azzolini
  • 2.
    Massimo Azzolini Founder -Project manager RedTurtle - Ferrara - Italy massimo@redturtle.it @massimoazzolini www.redturtle.it Massimo Azzolini Not an agile guru
  • 3.
  • 4.
    Massimo Azzolini The agilemanifesto ➡ Individuals and interactions over processes and tools ➡ Working software over comprehensive documentation ➡ Customer collaboration over contract negotiation ➡ Responding to change over following a plan
  • 5.
  • 6.
    1. Satisfy thecustomer 2. Welcome changing requirements 3. Deliver working software frequently 4. Business people and developers work together 5. Build projects around motivated individuals. 6. Face-to-face conversation 7. Working software is the primary measure of progress 8. Agile processes promote sustainable development 9. Attention to technical and design excellence 10. Simplicity is essential 11. The best architectures and requirements emerge from self-organizing teams 12. Retrospective 12 principles of agile manifesto
  • 7.
  • 8.
  • 9.
  • 10.
    so how canI make it USEFUL?
  • 11.
    Massimo Azzolini The structure Startedin 1999 in Ferrara, in 2012 we have had: ★ 14 people in house ★ most backend developers ★ 4 freelancer (not RedTurtle) ★ 3 teams ★ 77 projects ★ 347 customer requests ★ 1943 tickets solved ★ 20K+ hours worked
  • 12.
  • 13.
    no, I meanreally USEF UL ?!
  • 14.
  • 15.
    Scrum doesn’t completelyfit to us Kanban doesn’t completely fit to us LOL
  • 17.
    Massimo Azzolini What isleft? 1. Teams 2. Iterations 3. Agile team to embrace the verb 4. Introduce 1-2 new tools/methods after every agile meeting 5. Multiple kanban boards (personal vs group vs project) 6. WIP limit and customers’ delays 7. Value your time 8. Write everything 9. Code review
  • 18.
    Massimo Azzolini Which tools? ➡Email?! ➡ Share Documents ‣ Google Drive ‣ Dropbox ➡ Organize tasks ‣ Trello o Kanbanery ‣ Simple management (or other plone based solutions) ‣ Basecamp
  • 19.
  • 21.
  • 22.
    A new project Google Documentation ProjectManager Customer requests Trac Reports
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
    Google OAuth2 example [app:penelope] ... pyramid.includes= velruse.providers.google provider.google.consumer_key = 441361239240193 provider.google.consumer_secret = 52ef2618a1999eeec6d9c provider.google.scope = https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/drive.apps.list wgsi.ini
  • 28.
  • 29.
    GDrive Integration @documents.auth_graceful def get_google_document(context,request, **kwargs):     try:        folder_entry = request.gclient['DocsClient'].get_doclist( uri='/feeds/default/private/full/%s/contents/' % folder_id) folder = folder_entry.entry     except (ValueError, RequestError), e:        status = u'''Something is not working correctly.'''     params.update(folder=folder, status=status)
  • 30.
  • 31.
    TicketStore - TracIntegration class ITicketStore(Interface):     def get_tickets_for_project(self, project, request, query=None, limit=None):         """ """     def get_requests_from_tickets(self, project, ticket_ids, request=None):         """ """     def get_tickets_for_request(self, customer_request, limit=None, request=None):         """ """
  • 32.
  • 33.
    Deform/Colander @view_config(renderer='templates/form.pt', name='myform') def myform(self): classMapping(colander.Schema): name = colander.SchemaNode( colander.String(), description='Content name') date = colander.SchemaNode( colander.Date(), widget=deform.widget.DatePartsWidget(), description='Content date') class Schema(colander.Schema): number = colander.SchemaNode( colander.Integer()) mapping = Mapping() schema = Schema() form = deform.Form(schema, buttons=('submit',)) return self.render_form(form)
  • 34.
  • 35.
  • 36.
    def send(self, from_addr,recipients, data):         # Ensure the message complies with RFC2822: use CRLF line endings         message = data['msg']         data = data['data']         params = {}         changes_body = data['changes_body']         if changes_body:             params['changes_body'] = rest2html(changes_body)         if data['ticket']['new']:             params['ticket_new'] = True         params['ticket_body_hdr'] = data['ticket_body_hdr']         params['ticket_link'] = data['ticket']['link']         params['ticket_reporter'] = data['ticket']['reporter']         params['ticket_owner'] = data['ticket']['owner']         params['ticket_description'] = self.wiki2html(data['ticket']['description'])         params['ticket_type'] = data['ticket']['type']         params['ticket_status'] = data['ticket']['status']         params['ticket_priority'] = data['ticket']['priority']         params['ticket_milestone'] = data['ticket'].get('milestone')         params['change_author'] = data['change'].get('author','')         params['change_comment'] = self.wiki2html(data['change'].get('comment','')) Mandrill
  • 37.
  • 38.
    Sentry-Penelope class PenelopePlugin(IssuePlugin):          def create_issue(self,request, group, form_data, **kwargs):         proxy = TracXmlProxy(form_data['trac'], request=request)         try:             opts = {'type': 'defect',                     'issuetype': 'sistemistica',                     'customerrequest': '',                     'owner': ''}             ticket = proxy.ticket.create(form_data['title'],                                          form_data['description'],                                          opts)         except Exception, e:             msg = unicode(e)             raise forms.ValidationError(_('Error communicating: %s') %(msg,))         try:             data = int(ticket)         except Exception, e:             raise forms.ValidationError(_('Error decoding response: %s') % (e,))         return data
  • 39.
    Solr - tracfull text search
  • 40.
    Questions ? Massimo Azzolini RedTurtle’sco-founder and project manager massimo@redturtle.it tw: @massimoazzolini