Clean up Jenkins to save disk space

-> Manage Jenkins -> Script Console


MAX_BUILDS = 10 // max builds to keep

def jobs = Jenkins.instance.items;

for (job in jobs) {
    println "Job: " +
    def recent = job.builds.limit(MAX_BUILDS)
    println "Recent Builds: "  + recent
    println "============================="
    for (build in job.builds) {
        if (!recent.contains(build) && !build.isBuilding()) {
            println "Deleting: " + build
            println ""

replace raven with sentry-sdk

since raven is now deprecated, this is how we should integrate sentry into our django projects: via the new pip install sentry-sdk: No more messing around with LOGGING is required, the following code plugs right into django:

import sentry_sdk
import logging
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.logging import LoggingIntegration

         level=logging.INFO, # Capture info and above as breadcrumbs
         event_level=None # Send no events from log messages
   environment=env('DJANGO_ENV', 'develop'),

django translation tips: makemessages and gettext pitfalls & blocktrans best practises

Below I share some best practises and experiences I had with Django’s gettext implementations.

Fuzzy translations

Fuzzy translations are one of the bigger challenge when using gettext for translation. The following is a best practise in coping with them:

“Fuzzy” messages for not make it into the translation. They are not considered to be translated correctly, but they are not deleted, since they are likely to be “almost correct” and just need a small update.

Part of normal translator operations is to search out any “fuzzy” messages and verify that they are either still correct (just delete the fuzzy comment, but do not delete the python-format bit) or update them and then remove the “fuzzy”.

This isn’t anything special to Django, by the way. It’s the way
gettext-backed i18n support operates.

Malcolm Tredinnick at!msg/django-users/SjqjUhZwJzU/DaHmdwZeZLkJ

The Django blocktranstag

Best practice: Always use the `trimmed` option. This results in a nicely formatted block of text in the po file which makes it simpler for translators to handle it. If the translation key is not trimmed, arbitrary line breaks make it difficult to translate properly.

{% blocktrans trimmed with business_name=business_name context "password reset email" %}
You're receiving this email because you requested a password reset for your user account at {{ business_name }}.

Another line, which will be trimmed into one single block of text inside the po file so that this can be properly formatted.
{% endblocktrans %}

Translation Process

  1. After changes in the code and templates the developer updates the po files with makemessages command and review the updates in the re-generated po files and then commits them to the source, best via a Merge Request. Its not a good idea to add new translation entries from templates or code to a po file manually. django makemessages command takes care of this and makes sure everything is updated correctly. (edited)
  2. compilemessages is then normally run during deployment in the deploy script. It generates the compiled .mo files that are needed by Django to display the translations.

Django Project Dockerization


version: "2"

    build: "."
      - "8000:80"
      - ".:/app:rw"
    command: python runserver


FROM aldryn/base-project:py3-3.23

# System upgrade
RUN apt-get update && apt-get upgrade -yq

# Setup for ssh onto github
RUN mkdir -p /root/.ssh
ADD credentials/divio-deploy-key.pem /root/.ssh/id_rsa
RUN chmod 700 /root/.ssh/id_rsa
RUN echo "Host\n\tStrictHostKeyChecking no\n" >> /root/.ssh/config

# Python
# we want to keep project-specific sources in the "wee" folder
COPY requirements.txt /app/
RUN pip install -r requirements.txt

# nvm environment variables
RUN . $NVM_DIR/ && nvm install $NODE_VERSION

# add node and npm to path so the commands are available
ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
COPY package.json /app/
RUN npm install

COPY . /app

RUN npm run build

RUN DJANGO_MODE=build python collectstatic --noinput

Das isch Züri

Das muss man über Züri einfach wissen, no excuse
– So werden in der Schweiz Freunde gemacht, gilt auch für Züri: musst Du einfach gelesen haben, sonst weisst Du nicht was hier läuft, sorry.

Das muss man in Züri (mindestens) einmal gemacht haben (vielleicht):

Ab in die Berge…
– Wanderungen ab Zürich:

create ethereum keystore from private key

I havent yet found a python way of doing this but here is an npm solution:

npm install ethereumjs-wallet


var Wallet = require('ethereumjs-wallet');
var key = Buffer.from('<private key>', 'hex');
var wallet = Wallet.fromPrivateKey(key);
wallet.toV3String('<empty string or password');

Derive bitcoin addresses from HD xpub keys


we want to

  • generate Bitcoin receiving addresses from an xpub key (so that no secret information needs to be shared)
  • do this independently from a service / API, in python
  • be compatible with
  • be compatible with wallet software such as Electrum or so that the received funds can be easily managed

A note in regards to

  • allows to export the mnemonic and also the xpub key for each wallet account created
  • the xprv key for each account can be derived via by setting the account to {n}, where 0 corresponds to the first account, 1 to the second, and so on.
  • this xprv can then be imported into Electrum

Solution appears to be the continuation of Vitalik Buterin’s abandoned


from cryptos import *

words = 'word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12'
coin = Bitcoin()
private_wallet = coin.wallet(words)

xpub = private_wallet.keystore.xpub

# this is where you would start with an address calculation feature in an insecure environment like a web server
pub_keystore = keystore.from_xpub(xpub, coin, "p2pkh")
pub_wallet = wallet.HDWallet(pub_keystore)

Divio/DjangoCMS – import a DB dump

This is based on

First, get rid of the old db:

docker ps
docker stop projectname_db_1
docker rm projectname_db_1
docker-compose up db

then import

cat db-dump.sql | docker exec -i projectname_db_1 psql -U postgres db

Simple Django Admin Bulk Editing

Django Admin is awesome but it lacks an important feature: Editing multiple items in batch. There are two django add-ons this functionality but both seem a bit out of date.

Here I would like to show an example of how simple batch editing can be added to django admin with minimal amount of code, taking advantage of django admin’s own get_form().

def batch_update_view(model_admin, request, queryset, field_name):

        # removes all other fields from the django admin form for a model
    def remove_fields(form):
        for field in list(form.base_fields.keys()):
            if not field == field_name:
                del form.base_fields[field]
        return form

        # the return value is the form class, not the form class instance
    form_class = remove_fields(model_admin.get_form(request))

    if request.method == 'POST':
        form = form_class()

        # the view is already called via POST from the django admin changelist
        # here we have to distinguish between just showing the intermediary view via post
        # and actually confirming the bulk edits
        # for this there is a hidden field 'form-post' in the html template
        if 'form-post' in request.POST:
            form = form_class(request.POST)
            if form.is_valid():
                for item in queryset.all():
                    setattr(item, field_name, form.cleaned_data[field_name])
                model_admin.message_user(request, "Changed offer on {} items".format(queryset.count()))
                return HttpResponseRedirect(request.get_full_path())

        return render(
                'form': form,
                'items': queryset,

The template for the intermediary view could be made model agnostic by passing a list of (id, string) tuples to the template instead of item objects:


{% extends "admin/base_site.html" %}

{% block extrahead %}{{ block.super }}
    <script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
    {{ media }}
{% endblock %}

{% block content %}
    <form action="" method="post">
        {% csrf_token %}
            {{ form }}
            {% for item in items %}
                    {{ }} - {{ }} - {{ item.who }} - {{ item.offer }} - {{ item.offer_work_type }}
                {# this is required by Django Admin custom action framework #}
                <input type="hidden" name="_selected_action" value="{{ }}"/>
            {% endfor %}
                {# this is required by Django Admin custom action framework #}
        <input type="hidden" name="action" value="{{ request.POST.action }}"/>
        <input type="hidden" name="form-post" value="True"/>
        <input type="submit" name="apply" value="Save"/>
{% endblock %}
class YourModelAdmin(admin.ModelAdmin):
    actions = [

    def custom_batch_editing_field1_admin_action(self, request, queryset):
        return batch_update_view(
            # this is the name of the field on the YourModel model
    batch_update_offer.short_description = "batch update field1"