Using python to analyse a Trello board

Things this is:

  • a post about using python 3 to parse JSON files
  • a love letter to code
  • a reminder for myself
  • shameless self-promotion
  • a theoretical approach to calculating story points per sprint
    • only use this power to help your team reflect: if you use it to compare teams across your organisation, something bad will happen to you
      • not really bad, obviously, but it’ll be moderately irritating, like you won’t be able to find a pound for your shopping trolley or you’ll be halfway to the shops when you remember you’ve forgotten your wallet

Things this is not:

  • how to make burndown charts from your Trello boards
    • if you want me to make a thing that does that, it will cost you a sizeable ongoing donation to #BlackLivesMatter
Continue reading

NaBloPoMo #16

I’m going to do a quick review of a piece of code with 12 commits. It implements a fairly basic example of the Game of Life. Reading the rules of that game will be helpful if you haven’t read them yet.

In this blog, I’m going to try to guess what each commit does. Tomorrow we’ll compare and learn a valuable lesson about writing good commit messages.

Continue reading

NaBloPoMo #13

A first draft recursive function to solve a problem I found on codewars. I am happy with the recursion, but I’m finding that it struggles when the initial row is up to 100,000 characters long. I think that means there’s a secret to working this out – or, at least, a more efficient way than just brute-forcing it. Comments welcome!

Problem Description

A coloured triangle is created from a row of colours, each of which is red, green or blue. Successive rows, each containing one fewer colour than the last, are generated by considering the two touching colours in the previous row. If these colours are identical, the same colour is used in the new row. If they are different, the missing colour is used in the new row. This is continued until the final row, with only a single colour, is generated.

My code

class Triangle:
    def __init__(self):
        self.colours = {'R', 'G', 'B'}

    def triangle(self, row):
        if len(row) == 1:
            return row[0]
            new_row = []
            for i in range(len(row) - 1):
                parent_colours = set(row[i:i + 2])
            return self.triangle(new_row)

    def child_colour(self, parent_colours: set) -> str:
        if len(parent_colours) == 1:
            return parent_colours.pop()
            return self.colours.difference(parent_colours).pop()

So: this class has two methods, triangle and child_colour. triangle calls itself until it reaches its exit condition. The exit condition comes when we’ve only got one letter in row – that is to say, we’re down to the bottom level of the triangle.

If we’re not at that point yet, the method works out what the next row would be and passes it to a new instance of the same method.

Before very long, it’s triangles all the way down – until at last one reaches the exit condition. At this point I imagine all the methods snapping back together in exactly the way Terry Pratchett described the Cabinet of Curiosity. A cabinet where the first shelf contains another shelf that shoots off at a right angle, and that shelf branches another, and another, and another – until the answer is found. Then the first shelf draws back, and then the second, and then the third which somehow passes through the sixth and ninth because human brains can’t even see the eighth colour of the rainbow.

And then, at last, in the only drawer in the cabinet, is the answer you were looking for.

Look, he does it much better than I do. This is really just a recommendation to read Making Money. Ignore the code entirely.

November is National Blog Posting Month, or NaBloPoMo. I’ll be endeavouring to write one blog post per day in the month of November 2019 – some short and sweet, others long and boring.

NaBloPoMo #12

I was hoping to build a library. I’m going to write about how far I got, where the barrier is, and what I plan to do next. This follows from the introduction to Munkres I wrote earlier this month.

The algorithm requires manipulation of the elements in a grid as well as it’s rows and columns. I’m going to start this series by explaining how I tried to make the rows and column manipulable[mfn]completely guessed at that, genuinely amazed it’s a real word[/mfn] by extending the pandas class Series.

This is probably going to be moderately technical. I’m going to be walking through the code so there’s no need to go and look for it, but if you’d like to you can find it on Github.

import pandas as pd

class PandasMunkresAccessor:
    def __init__(self, pandas_obj):
        self._obj = pandas_obj
        self.covered = False

    def cover(self):
        Marks all elements in this series as 'covered' and also itself
        self.covered = True

    def uncover(self):
        Marks all elements in this series as uncovered and also itself
        self.covered = False

This is the first class I’m using. It uses a decorator, which is a sort of magic thing we have in Python. I’m going to avoid explaining decorators for the moment[mfn]because I don’t fully understand them myself yet[/mfn] and just say that this is a way of extending an existing class in Pandas – the Series class. It adds two methods and an attribute that I can access through Series.munkres – you can see where the “munkres” part comes from in the second line of code.

The way I’ve modified it is by adding an attribute, covered. There are also quasi “setter” and “getter” methods – one that covers all the objects in the Series and itself, and one that does the reverse.

I’ve added this because part of the algorithm involves covering rows and columns in a grid, and I thought this would be the simplest way to implement that in code. I am not yet sure whether I’m right, so I’m writing tests to check my understanding of how a Series is actually initialised.

(This is by far the best thing about writing code: if you don’t know how a thing works, you can construct a test to check. Most things in life aren’t like that.)

You’ll notice that when the extended Series.cover method is called, it calls another method – .apply. This is a method in the base Series class, and it allows you to apply a function to every element in the series. In this case, I’m applying a method from the MunkresElement class, which is just as well because all the items in the Series are MunkresElements. We know this because when we initialised the object in which we find the Series, we fill it with MunkresElements.

November is National Blog Posting Month, or NaBloPoMo. I’ll be endeavouring to write one blog post per day in the month of November 2019 – some short and sweet, others long and boring.