Be agile: take back control over your work

Preview:

Citation preview

agile.open.connectedEssere agili?

Metodi, strumenti e integrazioniPython “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

U S E F U L ?

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

U S E F U L ? !

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

TracReports

DashboardGoogle

Documentation

Customer

Trac - tickets

Trac - roadmap

Dashboard

Trac

Developer

Time

management

Sentry Customer

requestsTime

management

Show me the code!!!

OAuth/Login

Google OAuth2 example

[app:penelope]...pyramid.includes = velruse.providers.googleprovider.google.consumer_key = 441361239240193provider.google.consumer_secret = 52ef2618a1999eeec6d9cprovider.google.scope = https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/drive.apps.list

wgsi.ini

GDrive integration

GDrive Integration

@documents.auth_gracefuldef 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',''))        params['project_name'] = data['project']['name']        params['project_url'] = data['project']['url']

        message.add_header('X-MC-Template', 'ticket')        message.add_header('X-MC-MergeVars', json.dumps(params))

        message.set_payload('')

        message = message.as_string()        message = fix_eol(message, CRLF)        self.log.info("Sending notification through SMTP at %s:%d to %s"                      % (self.smtp_server, self.smtp_port, recipients))

        server = smtplib.SMTP(self.smtp_server, self.smtp_port)        if self.smtp_user:            server.login(self.smtp_user.encode('utf-8'),                         self.smtp_password.encode('utf-8'))        server.sendmail(from_addr, recipients, message)        server.quit()

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 AzzoliniRedTurtle’s co-founder and project managermassimo@redturtle.it tw: @massimoazzolini