How Google Ads spends your money behind your back

In recent months, Google has increased efforts to automate many aspects of Google Ads, for example the display targeting. By doing so manual controls have been hidden or removed from Google Ads or default settings have been changed. This is not always in the best interest of the advertiser. Below I will collect cases where Google has started to take decisions over the head of the advertiser, and how advertisers can take back control.

Display Native Apps Exclusion

Many advertisers see little to no return on their ad spend invested on native app placements on the Google Display Network. Meanwhile, for Google it’s a fast growing market as more and more impressions are generated in native apps, mostly free-of-charge games.

In earlier, happier days, all in-app ads could be disabled by excluding the placement . Google has phased this convenient possibility out in 2018. This article describes a workaround, but the setting has become harder to find and it’s not possible to exclude all native app placements as one anymore.

Display automatic targeting

If advertisers pay close attention they can see that by default, much of their display ad spend goes into something called Display Automated Targeting. This inflates media spend considerably which is especially misleading in Remarketing campaigns where an advertiser wants to target exclusively users that have visited the website before.

Here is how to deactivate Display automatic targeting for Remarketing (and other) campaigns.

Responsive Images and Safe Areas

Sometimes a designer or a content editor wants images, videos or more advanced content sections such as image sliders to take the full width of the browser window or even the full screen (height and width).

Full width or full screen content has many advantages from a design point of view and is often used to show off great videos or images in the header of a page. Homepages often use a full screen hero section:

These full width elements normally have a background video or image which is set to cover all the available space, given a fixed height (for example 600px height).

This means, that depending on the browser windows size, a part of the background image will be cut off.

Background images are often centered. This means, that if the browser window is wider than the background image width, an equal part on the top and bottom of the background image will be “cut off” (= not visible). If the browser window is less wide than the background image, the background image will still try to cover the given height of the full-width section. For this, it has to sacrifice space in the horizontal dimension. The background image will therefore be cut off on the left and right, like, for example on this iphone X screen:

In the real world, users will come to see your beautiful full width section on many different screen and browser sizes. Therefore, many of them will never see all of your beautiful background picture, but always just parts of it.

That’s where safe areas come in. A safe area is the part of the image that will always* show, whatever the user’s device or screen size. Here is an example of the definition of a safe area for a specific website project:

Content editors and designers should be briefed accordingly, so that they know how to resize and crop their images, so that the important part of the image is inside the safe area. Safe area briefings for different full-width sections on the website are therefore often created by project managers and web programmers.

Here you are a template that you can download and use for your own purposes:

I hope I could explain the complexities of using full-width images in this articles. If you have any questions don’t hesitate to comment or write to me.

*In practice, of course, there will always be users that wont even see the safe area, because, for example, they use a very old Blackberry phone. However in web design its best practice to set a compatibility policy and then ignore devices and browsers outside of that policy for cost and efficiency reasons.

Django<2 and sqlite3 Incompatibility

Older versions of django are currently not compatible with sqlite>3.25. More information here: – Problems occur when trying to load a fixture via `./ loaddata` or in some cases when using a database created in an old version of sqlite3 django throws a segmentation fault.

Uninstall any existing homebrew versions of sqlite3 via brew uninstall sqlite3

Then install sqlite 3.35:

brew install

Sushi Ingredients Shopping List

For 8 persons, approx 40 rolls

  • One small mango
  • 2 ripe avocados
  • 4 blocks of Sushi grade salmon (~720gr) – In Switzerland best purchased in big Coop supermarkets
  • 1 block of Sushi grade tuna (~250g) – In Switzerland best purchased in big Coop supermarkets in the fish department)
  • 1 small cucumber
  • Two bottles of rice vinegar for sushi
  • Japonica rice – in Switzerland it’s normally labelled as Sushi rice – you might get suitable Japonica rice in a local Chinese shop (1kg) – cook in 4 separate batches
  • Philadelphia Cream Cheese (small)
  • 2 jars of Sushi pickled ginger (Gari)
  • Wasabi
  • 4 Sushi mats
  • Bunch of fresh chives (goes well with cream cheese)
  • 40 Nori sheets (8 packs)
  • Roasted (black) and normal sesame
  • 1 carrot
  • 10 cooked shrimps for shrimp nigiri

Django / DjangoCMS – custom language

We had to create custom languages in Django for two different versions of the Chinese language: yue (Mandarin Chinese) and cmn (Cantonese Chinese)

This is really simple in Django with gettext, as the default languages can just be overriden:

    ## Customize this
    ('en', "English"),
    ('cmn', "Mandarin Chinese"),
    ('yue', "Cantonese Chinese"),

    ## Customize this
    1: [
            'code': 'en',
            'name': gettext('en'),
            'redirect_on_fallback': True,
            'public': True,
            'hide_untranslated': False,
            'code': 'cmn',
            'name': "Mandarin Chinese",
            'redirect_on_fallback': True,
            'public': True,
            'hide_untranslated': False,
            'code': 'yue',
            'name': "Cantonese Chinese",
            'redirect_on_fallback': True,
            'public': True,
            'hide_untranslated': False,
    'default': {
        'redirect_on_fallback': True,
        'public': True,
        'hide_untranslated': False,

updating from templates and python code:

./ makemessages -l en
./ makemessages -l yue
./ makemessages -l cmn

"Plural-Forms: nplurals=2; plural=(n != 1);\n" needs to be part of every .po file for DjangoCMS

./ compilemessages

Use Google Sheets as a database for a frontend widget

Here is an example, parts of this come from a Stackoverflow article, but I can’t find the link to it anymore. If you find the SO article, please kindly let me know so I can reference the original source here.

<!doctype html>
<html class="no-js" lang="">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">

<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css" />

<!--[if lt IE 8]>
You are using an <strong>outdated</strong> browser. Please <a href="">upgrade your browser</a> to improve your experience.

<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>

function query_spreadsheet(query_string) {
    var api = {
        baseUrl: '',
        sheet_key: "19YCYRQuuXWaQYVr-lMAeufIPFkp7lH24xwdL_Htbbu8",
        gid: '0',
        headers: '1'
    var random_id = Math.random().toString(36).substring(2);
    window.last_search_call = random_id;
    var random_callback_handler = "callback_func_" + random_id;
    let sql_query = "select A,B,C,D,E,F where A matches '" + query_string + ".*' or LOWER(B) contains '" + query_string + "'";
    let request_url = api.baseUrl + api.sheet_key + "/gviz/tq";
        url: request_url,
        // The name of the callback parameter, as specified by the YQL service
        jsonp: false,
        jsonpCallback: random_callback_handler,
        // Tell jQuery we're expecting JSONP
        dataType: "jsonp",
        data: {
            headers: api.headers,
            gid: api.gid,
            tqx: "responseHandler:" + random_callback_handler,
            tq: sql_query,
        success: function( response ) {
            if (window.last_search_call === random_id) {
                var result = []
                if (response.table.rows.length) {
                    var first_row = response.table.rows[0]
                    result = [
                            'id': 0,
                            'zip': first_row.c[0].v,
                            'city': first_row.c[1].v,
                            'canton': first_row.c[2].v,
                            'name': first_row.c[3].v,
                            'phone': first_row.c[4].v,
                            'img_url': first_row.c[5].v
                console.log( result ); // server response 
            } else {
                console.log('call cancelled!')
        error: function(XHR, textStatus, errorThrown) {
            console.log("error: " + textStatus);
            console.log("error: " + errorThrown);
jQuery(document).ready(function() {
    jQuery('body').on('click', '#sales-reps-search-button', function(ev) {
        // get query string form input field
        var searchString = jQuery('#sales-reps-search-field').val();
        var trimmedSearchString = searchString.trim();
    <input type="text" value="8000" class="form-control" id="sales-reps-search-field" placeholder="Search ...">
    <button id="sales-reps-search-button">Search!</button>

<div class="result">
<div class="error">


django language switcher

In Django, it’s not evident how to properly switch languages. Google searches result in many different approaches, but none of them is clean. In django-cms however, there is a clean and supported approach: the language_chooser template tag.

DjangoCMS and ManifestStaticFilesStorage

The expires header in HTTP works like the TTL (Time to live) for DNS caching – once cached, the browser wont request the asset anymore until its individual cache-until date expires.

If the file content changes on the server, the browser will not see those changes before the expiration date of the file’s cache.

That can cause problems, for example when jacascript or styles in the cached file dont match the html code of the document anymore.

Also, when we deploy, we want the new version to be visible right away, to everyone.

In order to have both browser caching AND full version control in the user’s browser, the idea is to add a unique hash to the filename of all files that are collected by django’s staticfiles. So that if they are changed, the filename changes. Like this, a new asset is fetched instantly by the browser as caching is done by filename.

The expiry header can then safely be set to an “infinite” date (a date far in the future) – we therefore call this “far future expiry header”.

  • You can do this by setting far future expiry header on the static directory, where django puts the collected static files. This is done in the configuration of the webserver that serves the static files (normally nginx).
  • Allows to remove hashing from the webpack setup which is convenient
  • Allows to also hash images via {% static 'path/to/asset.jpg' %}, respectively assets that are not bundled via webpack
  • Hashing is only active if debug is False
  • The collectstatic will fail if any app that includes static assets in templates is not in INSTALLED_APPS
  • On production, collectstatic will also sometimes fail, in such cases, DEBUG has to be turned on, ./ collectstatic has to be run, then DEBUG can be turned off again. See
  • If you use inlined data urls and the svg code itself uses url()s and these contain url_encoded values, then collectstatic command will fail because it will try to analyse these url()s and you have to work around this like
  • On, if you are using aldryn-django then static file hashing can be activated like this:


  • Aire de Barcelona – Wellness
  • Playa Mar Bella – Chiringuitos
  • W Hotel Rooftop – view
  • Museu Nacional d’Art de Catalunya – Sunset on the stairs
  • Bajofondo Club – Cocktails
  • The Pulitzer Terrace at Hotel Pulitzer – rooftop bar
  • B-Pool Bar at B-Hotel – Tapas, view
  • Grand Hotel Central Skybar – view, drinks

NEO python Shortcuts


  • create a python virtualenv
  • pip install neo-python


  • np-prompt in your console

Private Network Setup

  • docker pull cityofzion/neo-privatenet (see
  • np-prompt -p connects to your docker private network
  • then you can import the wallet that is provided in the documentation. It has already all the NEO and GAS.