From 7db2d9a6cde3ca644f4041f76f41ba2304f58117 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 27 Jul 2021 16:21:10 +0200
Subject: [PATCH 1/2] misc: apply pyupgrade (#55868)

---
 combo/apps/assets/migrations/0001_initial.py  |  2 -
 .../assets/migrations/0002_key_maxlength.py   |  3 -
 combo/apps/assets/templatetags/assets.py      |  2 +-
 combo/apps/assets/views.py                    | 24 ++---
 combo/apps/calendar/forms.py                  |  4 +-
 .../apps/calendar/migrations/0001_initial.py  |  3 -
 .../0002_bookingcalendar_days_displayed.py    |  3 -
 combo/apps/calendar/models.py                 |  8 +-
 combo/apps/calendar/utils.py                  | 10 +-
 combo/apps/calendar/views.py                  |  2 +-
 .../apps/dashboard/migrations/0001_initial.py |  3 -
 .../migrations/0002_auto_20180105_0846.py     |  3 -
 combo/apps/dashboard/models.py                |  2 +-
 combo/apps/dashboard/views.py                 |  2 +-
 combo/apps/dataviz/forms.py                   |  2 +-
 combo/apps/dataviz/migrations/0001_initial.py |  3 -
 .../0002_gauge_jsonp_data_source.py           |  3 -
 .../0003_cubesbarchart_cubestable.py          |  3 -
 .../migrations/0004_auto_20160108_1048.py     |  3 -
 .../migrations/0005_auto_20160928_1152.py     |  3 -
 .../apps/dataviz/migrations/0006_chartcell.py |  3 -
 .../migrations/0007_auto_20170214_2006.py     |  3 -
 .../migrations/0008_auto_20190328_0857.py     |  2 -
 .../migrations/0009_auto_20190617_1214.py     |  2 -
 .../migrations/0010_auto_20190328_1111.py     |  2 -
 .../migrations/0011_auto_20200813_1100.py     |  2 -
 .../migrations/0012_auto_20201126_1557.py     |  4 +-
 .../migrations/0013_update_chartng_cells.py   |  2 -
 .../migrations/0014_auto_20201130_1534.py     |  2 -
 .../migrations/0015_auto_20201202_1424.py     |  2 -
 .../migrations/0016_auto_20201215_1624.py     |  2 -
 combo/apps/dataviz/models.py                  | 12 +--
 combo/apps/family/migrations/0001_initial.py  |  3 -
 ..._familyinfoscell_restricted_to_unlogged.py |  3 -
 .../0003_familyinfoscell_extra_css_class.py   |  3 -
 ...4_familyinfoscell_last_update_timestamp.py |  3 -
 combo/apps/family/views.py                    |  2 +-
 combo/apps/fargo/migrations/0001_initial.py   |  3 -
 ...002_recentdocumentscell_extra_css_class.py |  3 -
 ...centdocumentscell_last_update_timestamp.py |  3 -
 .../0004_recentdocumentscell_fargo_site.py    |  3 -
 combo/apps/fargo/models.py                    |  4 +-
 combo/apps/gallery/migrations/0001_initial.py |  3 -
 .../gallery/migrations/0002_image_title.py    |  3 -
 .../migrations/0003_gallerycell_title.py      |  3 -
 combo/apps/gallery/views.py                   |  4 +-
 combo/apps/kb/migrations/0001_initial.py      |  2 -
 .../kb/migrations/0002_auto_20200203_1137.py  |  2 -
 combo/apps/kb/models.py                       |  2 +-
 combo/apps/lingo/forms.py                     | 10 +-
 .../commands/notify_new_remote_invoices.py    |  1 -
 combo/apps/lingo/manager_views.py             |  4 +-
 combo/apps/lingo/migrations/0001_initial.py   |  3 -
 .../apps/lingo/migrations/0002_transaction.py |  3 -
 .../migrations/0003_auto_20150306_1047.py     |  3 -
 .../0004_basketitem_notification_date.py      |  3 -
 .../migrations/0005_auto_20150307_1242.py     |  3 -
 .../migrations/0006_transaction_status.py     |  3 -
 .../migrations/0007_lingobasketlinkcell.py    |  3 -
 .../migrations/0008_auto_20150908_1538.py     |  3 -
 .../migrations/0009_auto_20150917_1456.py     |  3 -
 .../0010_regie_payment_min_amount.py          |  3 -
 .../migrations/0011_auto_20151029_1535.py     |  3 -
 .../migrations/0012_auto_20151130_1047.py     |  3 -
 .../migrations/0013_transaction_amount.py     |  3 -
 .../migrations/0014_auto_20160218_1455.py     |  3 -
 .../migrations/0015_auto_20160309_2150.py     |  3 -
 .../migrations/0016_auto_20160319_2028.py     |  3 -
 .../migrations/0017_auto_20160327_0831.py     |  3 -
 .../migrations/0018_auto_20160527_0638.py     |  3 -
 .../migrations/0019_auto_20160531_1336.py     |  3 -
 .../migrations/0020_auto_20160606_1803.py     |  3 -
 .../migrations/0021_transactionoperation.py   |  3 -
 .../lingo/migrations/0022_regie_is_default.py |  3 -
 .../migrations/0023_auto_20160928_1152.py     |  3 -
 .../0024_selfdeclaredinvoicepayment.py        |  3 -
 .../migrations/0025_auto_20161206_1713.py     |  3 -
 .../migrations/0026_auto_20161215_1447.py     |  3 -
 .../migrations/0027_auto_20170214_2006.py     |  3 -
 .../migrations/0028_tipipaymentformcell.py    |  3 -
 .../migrations/0029_auto_20170528_1334.py     |  3 -
 ...030_transaction_to_be_paid_remote_items.py |  3 -
 .../0031_basketitem_waiting_date.py           |  2 -
 .../0032_basketitem_capture_date.py           |  2 -
 .../migrations/0033_auto_20181204_2241.py     |  3 -
 .../migrations/0034_auto_20190426_1159.py     |  2 -
 .../migrations/0035_auto_20190426_1124.py     |  2 -
 .../migrations/0036_auto_20190426_1202.py     |  2 -
 .../0037_regie_transaction_options.py         |  2 -
 .../migrations/0038_hide_cell_if_empty.py     |  2 -
 .../0039_transaction_transaction_date.py      |  2 -
 .../migrations/0040_auto_20200608_1222.py     |  2 -
 ...0041_regie_can_pay_only_one_basket_item.py |  2 -
 combo/apps/lingo/models.py                    | 29 +++---
 combo/apps/lingo/utils.py                     |  1 -
 combo/apps/lingo/views.py                     | 70 +++++++-------
 combo/apps/maps/forms.py                      |  6 +-
 combo/apps/maps/manager_views.py              | 22 ++---
 combo/apps/maps/migrations/0001_initial.py    |  3 -
 combo/apps/maps/migrations/0002_map.py        |  3 -
 .../migrations/0003_auto_20170603_1653.py     |  3 -
 .../maps/migrations/0004_map_initial_state.py |  3 -
 .../migrations/0005_auto_20180212_1742.py     |  3 -
 .../migrations/0006_auto_20180627_0841.py     |  3 -
 .../migrations/0007_auto_20180706_1345.py     |  2 -
 .../maps/migrations/0008_map_layer_options.py |  5 +-
 .../maps/migrations/0009_map_layer_kind.py    |  2 -
 .../maps/migrations/0010_map_layer_opacity.py |  3 -
 .../migrations/0011_map_layer_set_slug.py     |  3 -
 .../migrations/0012_map_layer_slug_unicity.py |  3 -
 .../0013_geojson_query_parameter.py           |  3 -
 .../migrations/0014_geojson_circle_param.py   |  3 -
 combo/apps/maps/models.py                     |  6 +-
 .../notifications/migrations/0001_initial.py  |  3 -
 ...notificationscell_last_update_timestamp.py |  3 -
 .../migrations/0003_notification_origin.py    |  3 -
 .../migrations/0004_auto_20180316_1026.py     |  4 +-
 .../migrations/0005_auto_20180324_0025.py     |  2 -
 combo/apps/notifications/models.py            |  4 +-
 combo/apps/pwa/forms.py                       |  2 +-
 combo/apps/pwa/manager_views.py               | 12 +--
 combo/apps/pwa/migrations/0001_initial.py     |  2 -
 combo/apps/pwa/migrations/0002_pwasettings.py |  2 -
 .../pwa/migrations/0003_pwanavigationentry.py |  2 -
 .../0004_pwasettings_application_icon.py      |  2 -
 .../0005_pwasettings_application_name.py      |  2 -
 .../pwa/migrations/0006_auto_20190628_1042.py |  2 -
 combo/apps/pwa/models.py                      |  5 +-
 combo/apps/search/engines.py                  |  2 +-
 combo/apps/search/forms.py                    |  2 +-
 combo/apps/search/migrations/0001_initial.py  |  3 -
 .../migrations/0002_auto_20180720_1511.py     |  3 -
 .../migrations/0003_create_search_services.py |  3 -
 .../0004_remove_searchcell__search_service.py |  3 -
 .../migrations/0005_searchcell_autofocus.py   |  2 -
 .../search/migrations/0006_indexedcell.py     |  2 -
 .../apps/search/migrations/0007_french_fts.py |  2 -
 .../0008_searchcell_input_placeholder.py      |  2 -
 combo/apps/search/models.py                   |  4 +-
 combo/apps/wcs/context_processors.py          |  9 +-
 combo/apps/wcs/forms.py                       | 10 +-
 combo/apps/wcs/migrations/0001_initial.py     |  3 -
 .../wcs/migrations/0002_auto_20150217_1402.py |  3 -
 .../wcs/migrations/0003_auto_20150218_1151.py |  3 -
 .../wcs/migrations/0004_auto_20150218_1154.py |  3 -
 .../wcs/migrations/0005_auto_20150401_0758.py |  3 -
 .../wcs/migrations/0006_categoriescell.py     |  3 -
 .../migrations/0007_trackingcodeinputcell.py  |  3 -
 .../0008_wcsformcell_cached_json.py           |  3 -
 .../0009_remove_wcscategorycell_link_page.py  |  3 -
 .../wcs/migrations/0010_auto_20151029_1535.py |  3 -
 .../wcs/migrations/0011_auto_20151215_1121.py |  3 -
 ...012_wcsformsofcategorycell_manual_order.py |  3 -
 .../wcs/migrations/0013_auto_20160928_1152.py |  3 -
 .../wcs/migrations/0014_auto_20170214_2006.py |  3 -
 .../0015_wcscurrentformscell_categories.py    |  2 -
 .../0016_backofficesubmissioncell.py          |  2 -
 .../wcs/migrations/0017_wcscareformscell.py   |  2 -
 .../0018_wcscurrentdraftscell_categories.py   |  3 -
 ...0019_wcscurrentformscell_include_drafts.py |  3 -
 combo/apps/wcs/migrations/0020_cards.py       |  3 -
 combo/apps/wcs/migrations/0021_card.py        |  3 -
 combo/apps/wcs/migrations/0022_cards_user.py  |  3 -
 .../apps/wcs/migrations/0023_card_info_id.py  |  3 -
 .../wcs/migrations/0024_card_info_user.py     |  3 -
 .../migrations/0025_submission_categories.py  |  3 -
 .../0028_wcscardscell_without_user.py         |  2 -
 .../wcs/migrations/0029_cards_custom_title.py |  3 -
 .../migrations/0032_forms_user_can_access.py  |  3 -
 combo/apps/wcs/models.py                      | 33 ++++---
 combo/apps/wcs/views.py                       |  6 +-
 combo/data/fields.py                          | 23 +++--
 combo/data/forms.py                           | 10 +-
 combo/data/library.py                         |  2 +-
 combo/data/management/commands/cron.py        |  1 -
 combo/data/management/commands/export_site.py |  2 +-
 combo/data/management/commands/import_site.py |  4 +-
 combo/data/migrations/0001_initial.py         |  3 -
 .../migrations/0002_auto_20150217_1327.py     |  3 -
 combo/data/migrations/0003_menucell.py        |  3 -
 combo/data/migrations/0004_menucell_depth.py  |  3 -
 .../migrations/0005_auto_20150226_0903.py     |  3 -
 combo/data/migrations/0006_linkcell.py        |  3 -
 .../migrations/0007_auto_20150529_1003.py     |  3 -
 .../migrations/0008_auto_20150529_1504.py     |  3 -
 .../migrations/0009_auto_20150529_2247.py     |  3 -
 combo/data/migrations/0010_feedcell.py        |  3 -
 .../migrations/0011_auto_20151028_1047.py     |  3 -
 .../migrations/0012_auto_20151029_1535.py     |  3 -
 combo/data/migrations/0013_parameterscell.py  |  3 -
 .../migrations/0014_menucell_initial_level.py |  3 -
 combo/data/migrations/0015_feedcell_title.py  |  3 -
 combo/data/migrations/0016_feedcell_limit.py  |  3 -
 .../migrations/0017_menucell_root_page.py     |  3 -
 .../data/migrations/0018_parentcontentcell.py |  3 -
 .../migrations/0019_create_parent_cells.py    |  3 -
 .../migrations/0020_auto_20160928_1152.py     |  3 -
 combo/data/migrations/0021_jsoncell.py        |  3 -
 .../migrations/0022_auto_20170214_2006.py     |  3 -
 .../migrations/0023_auto_20170313_1541.py     |  3 -
 combo/data/migrations/0024_configjsoncell.py  |  3 -
 .../migrations/0025_jsoncell_varnames_str.py  |  3 -
 .../migrations/0026_jsoncell_force_async.py   |  3 -
 combo/data/migrations/0027_page_picture.py    |  3 -
 .../data/migrations/0028_jsoncell_timeout.py  |  3 -
 .../migrations/0029_auto_20171022_1242.py     |  3 -
 .../migrations/0030_externallinksearchitem.py |  3 -
 combo/data/migrations/0031_remove_blurps.py   |  3 -
 .../data/migrations/0032_page_description.py  |  3 -
 .../migrations/0033_auto_20180401_1300.py     |  2 -
 combo/data/migrations/0034_redirect.py        |  2 -
 .../migrations/0035_page_related_cells.py     |  2 -
 combo/data/migrations/0036_page_sub_slug.py   |  2 -
 .../migrations/0037_auto_20190701_2118.py     |  2 -
 .../0038_increase_jsoncell_url_max_length.py  |  2 -
 combo/data/migrations/0039_link_list_cell.py  |  3 -
 .../migrations/0040_auto_20200119_1017.py     |  2 -
 .../migrations/0041_auto_20200130_1619.py     |  3 -
 .../0042_page_creation_timestamp.py           |  2 -
 .../0043_delete_externallinksearchitem.py     |  2 -
 combo/data/migrations/0044_validity_info.py   |  5 +-
 combo/data/migrations/0045_link_list_limit.py |  2 -
 combo/data/models.py                          | 64 ++++++-------
 combo/data/utils.py                           |  8 +-
 combo/manager/fields.py                       |  3 +-
 combo/manager/forms.py                        | 12 +--
 combo/manager/views.py                        | 40 ++++----
 combo/profile/__init__.py                     |  1 -
 combo/profile/migrations/0001_initial.py      |  3 -
 combo/profile/migrations/0002_profilecell.py  |  2 -
 combo/profile/models.py                       |  4 +-
 combo/profile/utils.py                        |  2 +-
 combo/public/templatetags/combo.py            |  7 +-
 combo/public/views.py                         | 12 +--
 combo/urls_utils.py                           |  4 +-
 combo/utils/forms.py                          |  8 +-
 combo/utils/requests_wrapper.py               |  2 +-
 setup.py                                      |  3 +-
 tests/settings.py                             |  2 +-
 tests/test_assets.py                          | 12 +--
 tests/test_calendar.py                        |  2 +-
 tests/test_cells.py                           | 36 +++----
 tests/test_cron.py                            |  3 +-
 tests/test_dashboard.py                       |  2 +-
 tests/test_dataviz.py                         | 78 +++++++--------
 tests/test_ensure_jsonbfields.py              |  2 -
 tests/test_fargo.py                           |  2 +-
 tests/test_gallery_cell.py                    |  1 -
 tests/test_lingo_cells.py                     |  6 +-
 tests/test_lingo_manager.py                   |  6 +-
 tests/test_lingo_payment.py                   |  4 +-
 tests/test_lingo_remote_regie.py              |  8 +-
 tests/test_manager.py                         | 25 +++--
 tests/test_maps_cells.py                      |  3 +-
 tests/test_maps_manager.py                    |  7 +-
 tests/test_notification.py                    |  4 +-
 tests/test_pages.py                           | 26 ++---
 tests/test_profile.py                         |  2 +-
 tests/test_public.py                          |  4 +-
 tests/test_publik.py                          |  2 +-
 tests/test_pwa.py                             |  2 +-
 tests/test_requests.py                        |  7 +-
 tests/test_search.py                          | 12 +--
 tests/test_spooler.py                         |  2 +-
 tests/test_utils.py                           |  2 +-
 tests/test_wcs.py                             | 96 +++++++++----------
 tests/test_wcs_templatetags.py                | 10 +-
 267 files changed, 430 insertions(+), 977 deletions(-)

diff --git a/combo/apps/assets/migrations/0001_initial.py b/combo/apps/assets/migrations/0001_initial.py
index 0744b226..402c8ef5 100644
--- a/combo/apps/assets/migrations/0001_initial.py
+++ b/combo/apps/assets/migrations/0001_initial.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2018-06-12 11:42
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/assets/migrations/0002_key_maxlength.py b/combo/apps/assets/migrations/0002_key_maxlength.py
index cfd96627..64e78ebe 100644
--- a/combo/apps/assets/migrations/0002_key_maxlength.py
+++ b/combo/apps/assets/migrations/0002_key_maxlength.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/assets/templatetags/assets.py b/combo/apps/assets/templatetags/assets.py
index 4c08efdf..e974c1f0 100644
--- a/combo/apps/assets/templatetags/assets.py
+++ b/combo/apps/assets/templatetags/assets.py
@@ -38,7 +38,7 @@ def asset_url(*args, **kwargs):
             asset = asset_object
             break
 
-        if isinstance(asset_object, six.string_types):
+        if isinstance(asset_object, str):
             try:
                 asset = Asset.objects.get(key=asset_object).asset
                 break
diff --git a/combo/apps/assets/views.py b/combo/apps/assets/views.py
index 9864b5dd..09aa60d5 100644
--- a/combo/apps/assets/views.py
+++ b/combo/apps/assets/views.py
@@ -39,7 +39,7 @@ from .forms import AssetsImportForm, AssetUploadForm
 from .models import Asset
 
 
-class CkEditorAsset(object):
+class CkEditorAsset:
     def __init__(self, filepath):
         self.filepath = filepath
         self.name = os.path.basename(filepath)
@@ -69,7 +69,7 @@ class CkEditorAsset(object):
         return ckeditor.views.is_image(self.src)
 
 
-class SlotAsset(object):
+class SlotAsset:
     def __init__(self, key=None, name=None, asset_type='image', asset=None):
         self.key = key
         self.name = name
@@ -98,7 +98,7 @@ class SlotAsset(object):
 
     @classmethod
     def get_assets(cls):
-        assets = dict([(x.key, x) for x in Asset.objects.all()])
+        assets = {x.key: x for x in Asset.objects.all()}
         uniq_slots = {}
         uniq_slots.update(settings.COMBO_ASSET_SLOTS)
         cells = CellBase.get_cells(select_related={'__all__': ['page'], 'data_linkcell': ['link_page']})
@@ -131,7 +131,7 @@ class Assets(ListView):
         return files
 
     def get_context_data(self, **kwargs):
-        context = super(Assets, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['query'] = self.request.GET.get('q') or ''
         return context
 
@@ -170,7 +170,7 @@ class AssetUpload(FormView):
         ckeditor_upload_view = ckeditor.views.ImageUploadView()
         self.request.GET = {'CKEditorFuncNum': '-'}  # hack
         ckeditor_upload_view.post(self.request)
-        return super(AssetUpload, self).form_valid(form)
+        return super().form_valid(form)
 
     def get_success_url(self):
         return Assets(request=self.request).get_anchored_url(name=self.request.FILES['upload'].name)
@@ -204,7 +204,7 @@ class AssetOverwrite(FormView):
                 self.request,
                 _('You have to upload a file with the same extension (%(ext)s).') % {'ext': ext_orig},
             )
-            return super(AssetOverwrite, self).form_valid(form)
+            return super().form_valid(form)
 
         default_storage.delete(img_orig)
         if getattr(settings, 'CKEDITOR_IMAGE_BACKEND', None):
@@ -220,7 +220,7 @@ class AssetOverwrite(FormView):
         else:
             if backend.should_create_thumbnail(saved_path):
                 backend.create_thumbnail(saved_path)
-        return super(AssetOverwrite, self).form_valid(form)
+        return super().form_valid(form)
 
     def get_success_url(self):
         img_orig = self.request.GET['img']
@@ -254,7 +254,7 @@ class SlotAssets(ListView):
 
     def get_assets(self, cell):
         asset_slots = cell.get_asset_slots()
-        assets = dict([(x.key, x) for x in Asset.objects.filter(key__in=asset_slots.keys())])
+        assets = {x.key: x for x in Asset.objects.filter(key__in=asset_slots.keys())}
         for key, value in asset_slots.items():
             yield SlotAsset(
                 key,
@@ -293,7 +293,7 @@ class SlotAssetUpload(FormView):
             self.asset = Asset(key=self.kwargs['key'])
         self.asset.asset = self.request.FILES['upload']
         self.asset.save()
-        return super(SlotAssetUpload, self).form_valid(form)
+        return super().form_valid(form)
 
     def get_success_url(self):
         if self.request.GET.get('cell_reference'):
@@ -349,9 +349,9 @@ class AssetsImport(FormView):
             import_assets(form.cleaned_data['assets_file'], overwrite)
         except tarfile.TarError:
             messages.error(self.request, _('The assets file is not valid.'))
-            return super(AssetsImport, self).form_valid(form)
+            return super().form_valid(form)
         messages.success(self.request, _('The assets file has been imported.'))
-        return super(AssetsImport, self).form_valid(form)
+        return super().form_valid(form)
 
 
 assets_import = AssetsImport.as_view()
@@ -392,7 +392,7 @@ class AssetsExportSize(TemplateView):
     template_name = 'combo/manager_assets_export_size.html'
 
     def get_context_data(self, **kwargs):
-        context = super(AssetsExportSize, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         media_prefix = default_storage.path('')
         computed_size = 0
         for basedir, dirnames, filenames in os.walk(media_prefix):
diff --git a/combo/apps/calendar/forms.py b/combo/apps/calendar/forms.py
index e17d876b..a6b87c29 100644
--- a/combo/apps/calendar/forms.py
+++ b/combo/apps/calendar/forms.py
@@ -37,7 +37,7 @@ class BookingCalendarForm(forms.ModelForm):
         )
 
     def __init__(self, *args, **kwargs):
-        super(BookingCalendarForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         agenda_references = get_agendas()
         formdef_references = get_wcs_options('/api/formdefs/')
         self.fields['agenda_reference'].widget = forms.Select(choices=agenda_references)
@@ -47,7 +47,7 @@ class BookingCalendarForm(forms.ModelForm):
 class BookingForm(forms.Form):
     def __init__(self, *args, **kwargs):
         self.cell = kwargs.pop('cell')
-        super(BookingForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.cleaned_data = {}
 
     def is_valid(self):
diff --git a/combo/apps/calendar/migrations/0001_initial.py b/combo/apps/calendar/migrations/0001_initial.py
index a7891da0..12f08f98 100644
--- a/combo/apps/calendar/migrations/0001_initial.py
+++ b/combo/apps/calendar/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import datetime
 
 from django.db import migrations, models
diff --git a/combo/apps/calendar/migrations/0002_bookingcalendar_days_displayed.py b/combo/apps/calendar/migrations/0002_bookingcalendar_days_displayed.py
index a7f8089d..4d0edc78 100644
--- a/combo/apps/calendar/migrations/0002_bookingcalendar_days_displayed.py
+++ b/combo/apps/calendar/migrations/0002_bookingcalendar_days_displayed.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/calendar/models.py b/combo/apps/calendar/models.py
index a0669df7..c737926a 100644
--- a/combo/apps/calendar/models.py
+++ b/combo/apps/calendar/models.py
@@ -59,16 +59,12 @@ class BookingCalendar(CellBase):
         return settings.BOOKING_CALENDAR_CELL_ENABLED and is_chrono_enabled() and is_wcs_enabled()
 
     def is_visible(self, **kwargs):
-        return (
-            self.agenda_reference
-            and self.formdef_reference
-            and super(BookingCalendar, self).is_visible(**kwargs)
-        )
+        return self.agenda_reference and self.formdef_reference and super().is_visible(**kwargs)
 
     def get_cell_extra_context(self, context):
         if context.get('placeholder_search_mode'):
             return {}
-        extra_context = super(BookingCalendar, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         events_data = get_chrono_events(self.agenda_reference, not (context.get('synchronous')))
         extra_context.update(
             get_calendar_context_vars(context['request'], extra_context['cell'], events_data)
diff --git a/combo/apps/calendar/utils.py b/combo/apps/calendar/utils.py
index f2431a2b..a5ec2241 100644
--- a/combo/apps/calendar/utils.py
+++ b/combo/apps/calendar/utils.py
@@ -128,7 +128,7 @@ def get_form_url_with_params(cell, data):
     return url
 
 
-class DaySlot(object):
+class DaySlot:
     def __init__(self, date_time, available, exist=True):
         self.date_time = localtime(make_aware(date_time))
         self.available = available
@@ -142,7 +142,7 @@ class DaySlot(object):
         return '%s' % self.date_time.isoformat()
 
 
-class WeekDay(object):
+class WeekDay:
     def __init__(self, date):
         self.date = date
         self.slots = []
@@ -168,7 +168,7 @@ class WeekDay(object):
         return max(self.slots, key=lambda x: x.date_time.time())
 
 
-class Calendar(object):
+class Calendar:
     def __init__(self, offset, days_displayed, min_duration):
         self.offset = offset
         self.days_displayed = days_displayed
@@ -227,7 +227,7 @@ class Calendar(object):
         return day.get_slot(slot.time())
 
     def get_minimum_slot(self):
-        return min([day.get_minimum_slot().date_time.time() for day in self.days])
+        return min(day.get_minimum_slot().date_time.time() for day in self.days)
 
     def get_maximum_slot(self):
-        return max([day.get_maximum_slot().date_time.time() for day in self.days])
+        return max(day.get_maximum_slot().date_time.time() for day in self.days)
diff --git a/combo/apps/calendar/views.py b/combo/apps/calendar/views.py
index 13fce662..2296494a 100644
--- a/combo/apps/calendar/views.py
+++ b/combo/apps/calendar/views.py
@@ -49,7 +49,7 @@ class CalendarContentAjaxView(DetailView):
     template_name = 'calendar/booking_calendar_content.html'
 
     def get_context_data(self, **kwargs):
-        context = super(CalendarContentAjaxView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['cell'] = self.object
         events_data = get_chrono_events(self.object.agenda_reference, context.get('synchronous'))
         context.update(get_calendar_context_vars(self.request, self.object, events_data))
diff --git a/combo/apps/dashboard/migrations/0001_initial.py b/combo/apps/dashboard/migrations/0001_initial.py
index a7601d44..60dfcce0 100644
--- a/combo/apps/dashboard/migrations/0001_initial.py
+++ b/combo/apps/dashboard/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.db import migrations, models
 
diff --git a/combo/apps/dashboard/migrations/0002_auto_20180105_0846.py b/combo/apps/dashboard/migrations/0002_auto_20180105_0846.py
index f9eead41..0de145d2 100644
--- a/combo/apps/dashboard/migrations/0002_auto_20180105_0846.py
+++ b/combo/apps/dashboard/migrations/0002_auto_20180105_0846.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/dashboard/models.py b/combo/apps/dashboard/models.py
index 9adcffd9..e7e71440 100644
--- a/combo/apps/dashboard/models.py
+++ b/combo/apps/dashboard/models.py
@@ -54,7 +54,7 @@ class DashboardCell(CellBase):
             for x in ValidityInfo.objects.filter(invalid_since__lt=now() - datetime.timedelta(days=2))
         }
         context['tiles'] = [x for x in tiles if (x.cell_type_id, x.cell_pk) not in validity_info_dict]
-        return super(DashboardCell, self).render(context)
+        return super().render(context)
 
 
 class Tile(models.Model):
diff --git a/combo/apps/dashboard/views.py b/combo/apps/dashboard/views.py
index ece4146f..14cf8cc2 100644
--- a/combo/apps/dashboard/views.py
+++ b/combo/apps/dashboard/views.py
@@ -158,7 +158,7 @@ def dashboard_auto_tile(request, *args, **kwargs):
 def dashboard_reorder_tiles(request, *args, **kwargs):
     dashboard = DashboardCell.objects.filter(page__snapshot__isnull=True)[0]
     new_order = request.GET['order'].split(',')
-    tiles = dict((str(x.id), x) for x in Tile.objects.filter(id__in=new_order))
+    tiles = {str(x.id): x for x in Tile.objects.filter(id__in=new_order)}
     for i, tile_id in enumerate(new_order):
         tile = tiles.get(tile_id)
         if tile.user != request.user:
diff --git a/combo/apps/dataviz/forms.py b/combo/apps/dataviz/forms.py
index e312665e..cf724a94 100644
--- a/combo/apps/dataviz/forms.py
+++ b/combo/apps/dataviz/forms.py
@@ -34,7 +34,7 @@ class ChartForm(forms.ModelForm):
         fields = ('title', 'url')
 
     def __init__(self, *args, **kwargs):
-        super(ChartForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         available_charts = []
         for site_key, site_dict in settings.KNOWN_SERVICES.get('bijoe').items():
             result = requests.get(
diff --git a/combo/apps/dataviz/migrations/0001_initial.py b/combo/apps/dataviz/migrations/0001_initial.py
index b35e7137..f4620cb0 100644
--- a/combo/apps/dataviz/migrations/0001_initial.py
+++ b/combo/apps/dataviz/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/dataviz/migrations/0002_gauge_jsonp_data_source.py b/combo/apps/dataviz/migrations/0002_gauge_jsonp_data_source.py
index 6f70bd17..c9c41e83 100644
--- a/combo/apps/dataviz/migrations/0002_gauge_jsonp_data_source.py
+++ b/combo/apps/dataviz/migrations/0002_gauge_jsonp_data_source.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/dataviz/migrations/0003_cubesbarchart_cubestable.py b/combo/apps/dataviz/migrations/0003_cubesbarchart_cubestable.py
index 9660760e..401c32ca 100644
--- a/combo/apps/dataviz/migrations/0003_cubesbarchart_cubestable.py
+++ b/combo/apps/dataviz/migrations/0003_cubesbarchart_cubestable.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/dataviz/migrations/0004_auto_20160108_1048.py b/combo/apps/dataviz/migrations/0004_auto_20160108_1048.py
index 2d3c85f5..c5a34058 100644
--- a/combo/apps/dataviz/migrations/0004_auto_20160108_1048.py
+++ b/combo/apps/dataviz/migrations/0004_auto_20160108_1048.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/dataviz/migrations/0005_auto_20160928_1152.py b/combo/apps/dataviz/migrations/0005_auto_20160928_1152.py
index c2193fab..19a19900 100644
--- a/combo/apps/dataviz/migrations/0005_auto_20160928_1152.py
+++ b/combo/apps/dataviz/migrations/0005_auto_20160928_1152.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/dataviz/migrations/0006_chartcell.py b/combo/apps/dataviz/migrations/0006_chartcell.py
index 2e6a01c9..1280b78e 100644
--- a/combo/apps/dataviz/migrations/0006_chartcell.py
+++ b/combo/apps/dataviz/migrations/0006_chartcell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/dataviz/migrations/0007_auto_20170214_2006.py b/combo/apps/dataviz/migrations/0007_auto_20170214_2006.py
index ed19c432..d31270d3 100644
--- a/combo/apps/dataviz/migrations/0007_auto_20170214_2006.py
+++ b/combo/apps/dataviz/migrations/0007_auto_20170214_2006.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import datetime
 
 from django.db import migrations, models
diff --git a/combo/apps/dataviz/migrations/0008_auto_20190328_0857.py b/combo/apps/dataviz/migrations/0008_auto_20190328_0857.py
index 51f8cb09..012f8b53 100644
--- a/combo/apps/dataviz/migrations/0008_auto_20190328_0857.py
+++ b/combo/apps/dataviz/migrations/0008_auto_20190328_0857.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2019-03-28 07:57
-from __future__ import unicode_literals
 
 from django.db import migrations
 
diff --git a/combo/apps/dataviz/migrations/0009_auto_20190617_1214.py b/combo/apps/dataviz/migrations/0009_auto_20190617_1214.py
index ef60e143..83522f57 100644
--- a/combo/apps/dataviz/migrations/0009_auto_20190617_1214.py
+++ b/combo/apps/dataviz/migrations/0009_auto_20190617_1214.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2019-06-17 10:14
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/dataviz/migrations/0010_auto_20190328_1111.py b/combo/apps/dataviz/migrations/0010_auto_20190328_1111.py
index 37440ef9..a2d41f2e 100644
--- a/combo/apps/dataviz/migrations/0010_auto_20190328_1111.py
+++ b/combo/apps/dataviz/migrations/0010_auto_20190328_1111.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2019-03-28 10:11
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.contrib.postgres.fields import JSONField
diff --git a/combo/apps/dataviz/migrations/0011_auto_20200813_1100.py b/combo/apps/dataviz/migrations/0011_auto_20200813_1100.py
index 5528e3fa..160bf9b7 100644
--- a/combo/apps/dataviz/migrations/0011_auto_20200813_1100.py
+++ b/combo/apps/dataviz/migrations/0011_auto_20200813_1100.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2020-08-13 09:00
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/dataviz/migrations/0012_auto_20201126_1557.py b/combo/apps/dataviz/migrations/0012_auto_20201126_1557.py
index 1c8067a3..47c50808 100644
--- a/combo/apps/dataviz/migrations/0012_auto_20201126_1557.py
+++ b/combo/apps/dataviz/migrations/0012_auto_20201126_1557.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.29 on 2020-11-26 14:57
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.db import migrations, models
@@ -35,7 +33,7 @@ class Migration(migrations.Migration):
         ),
         migrations.AlterUniqueTogether(
             name='statistic',
-            unique_together=set([('slug', 'site_slug', 'service_slug')]),
+            unique_together={('slug', 'site_slug', 'service_slug')},
         ),
         migrations.AddField(
             model_name='chartngcell',
diff --git a/combo/apps/dataviz/migrations/0013_update_chartng_cells.py b/combo/apps/dataviz/migrations/0013_update_chartng_cells.py
index d44f6b54..a9daf3d4 100644
--- a/combo/apps/dataviz/migrations/0013_update_chartng_cells.py
+++ b/combo/apps/dataviz/migrations/0013_update_chartng_cells.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.29 on 2020-11-30 14:26
-from __future__ import unicode_literals
 
 from django.conf import settings
 from django.db import migrations
diff --git a/combo/apps/dataviz/migrations/0014_auto_20201130_1534.py b/combo/apps/dataviz/migrations/0014_auto_20201130_1534.py
index 918b78cd..f9a7eb2a 100644
--- a/combo/apps/dataviz/migrations/0014_auto_20201130_1534.py
+++ b/combo/apps/dataviz/migrations/0014_auto_20201130_1534.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.29 on 2020-11-30 14:34
-from __future__ import unicode_literals
 
 from django.db import migrations
 
diff --git a/combo/apps/dataviz/migrations/0015_auto_20201202_1424.py b/combo/apps/dataviz/migrations/0015_auto_20201202_1424.py
index cb9c0dd5..562c9bb9 100644
--- a/combo/apps/dataviz/migrations/0015_auto_20201202_1424.py
+++ b/combo/apps/dataviz/migrations/0015_auto_20201202_1424.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.29 on 2020-12-02 13:24
-from __future__ import unicode_literals
 
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations
diff --git a/combo/apps/dataviz/migrations/0016_auto_20201215_1624.py b/combo/apps/dataviz/migrations/0016_auto_20201215_1624.py
index b0a0555f..a001e80f 100644
--- a/combo/apps/dataviz/migrations/0016_auto_20201215_1624.py
+++ b/combo/apps/dataviz/migrations/0016_auto_20201215_1624.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.29 on 2020-12-15 15:24
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/dataviz/models.py b/combo/apps/dataviz/models.py
index 68d32365..e4ba7b93 100644
--- a/combo/apps/dataviz/models.py
+++ b/combo/apps/dataviz/models.py
@@ -111,7 +111,7 @@ class ChartCell(CellBase):
         return ''
 
     def get_cell_extra_context(self, context):
-        context = super(ChartCell, self).get_cell_extra_context(context)
+        context = super().get_cell_extra_context(context)
         context['title'] = self.title
         context['url'] = self.url
         return context
@@ -271,7 +271,7 @@ class ChartNgCell(CellBase):
         )
 
     def get_cell_extra_context(self, context):
-        ctx = super(ChartNgCell, self).get_cell_extra_context(context)
+        ctx = super().get_cell_extra_context(context)
         if self.chart_type == 'table' and self.statistic and self.statistic.url:
             try:
                 chart = self.get_chart(raise_if_not_cached=not (context.get('synchronous')))
@@ -508,7 +508,7 @@ class ChartNgCell(CellBase):
             tmp_items = sorted(zip(chart.x_labels, data), key=lambda x: (x[1] or 0))
         elif self.sort_order == 'desc':
             tmp_items = sorted(zip(chart.x_labels, data), key=lambda x: (x[1] or 0), reverse=True)
-        x_labels, sorted_data = zip(*[(label, value) for label, value in tmp_items])
+        x_labels, sorted_data = zip(*((label, value) for label, value in tmp_items))
         chart.x_labels = list(x_labels)
         return list(sorted_data)
 
@@ -568,12 +568,12 @@ class ChartNgCell(CellBase):
 
             return format_duration
         elif measure == 'percent':
-            percent_formatter = lambda x: '{:.1f}%'.format(x)
+            percent_formatter = lambda x: f'{x:.1f}%'
             return percent_formatter
 
     def make_percent(self, series_data):
         for i, values in enumerate(zip(*series_data)):
-            sum_values = sum([v for v in values if v is not None])
+            sum_values = sum(v for v in values if v is not None)
             if sum_values == 0:
                 continue
 
@@ -595,7 +595,7 @@ class ChartNgCell(CellBase):
             # "W" and "o" are interpreted by Django's date filter and should be left as is. First W is
             # backslash escaped to prevent it from being interpreted, translators should refer to Django's
             # documentation in order to know if the new letter resulting of translation should be escaped or not.
-            '_week': gettext('\WW-o'),
+            '_week': gettext(r'\WW-o'),
             '_month': 'm-Y',
             '_year': 'Y',
             '_weekday': 'l',
diff --git a/combo/apps/family/migrations/0001_initial.py b/combo/apps/family/migrations/0001_initial.py
index f3acb5e9..115e60f9 100644
--- a/combo/apps/family/migrations/0001_initial.py
+++ b/combo/apps/family/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import ckeditor.fields
 from django.db import migrations, models
 
diff --git a/combo/apps/family/migrations/0002_familyinfoscell_restricted_to_unlogged.py b/combo/apps/family/migrations/0002_familyinfoscell_restricted_to_unlogged.py
index 1340d93a..645729fb 100644
--- a/combo/apps/family/migrations/0002_familyinfoscell_restricted_to_unlogged.py
+++ b/combo/apps/family/migrations/0002_familyinfoscell_restricted_to_unlogged.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/family/migrations/0003_familyinfoscell_extra_css_class.py b/combo/apps/family/migrations/0003_familyinfoscell_extra_css_class.py
index 70810e90..16301c0e 100644
--- a/combo/apps/family/migrations/0003_familyinfoscell_extra_css_class.py
+++ b/combo/apps/family/migrations/0003_familyinfoscell_extra_css_class.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/family/migrations/0004_familyinfoscell_last_update_timestamp.py b/combo/apps/family/migrations/0004_familyinfoscell_last_update_timestamp.py
index c90f450d..00ea4eae 100644
--- a/combo/apps/family/migrations/0004_familyinfoscell_last_update_timestamp.py
+++ b/combo/apps/family/migrations/0004_familyinfoscell_last_update_timestamp.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import datetime
 
 from django.db import migrations, models
diff --git a/combo/apps/family/views.py b/combo/apps/family/views.py
index 5f460b11..ad4a69ef 100644
--- a/combo/apps/family/views.py
+++ b/combo/apps/family/views.py
@@ -52,7 +52,7 @@ class FamilyLinkView(FormView):
             messages.error(self.request, error_message)
         else:
             messages.info(self.request, _('Your account was successfully linked.'))
-        return super(FamilyLinkView, self).form_valid(form)
+        return super().form_valid(form)
 
 
 class FamilyUnlinkView(TemplateView):
diff --git a/combo/apps/fargo/migrations/0001_initial.py b/combo/apps/fargo/migrations/0001_initial.py
index 7a7627d8..3d76b633 100644
--- a/combo/apps/fargo/migrations/0001_initial.py
+++ b/combo/apps/fargo/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/fargo/migrations/0002_recentdocumentscell_extra_css_class.py b/combo/apps/fargo/migrations/0002_recentdocumentscell_extra_css_class.py
index cb54004e..32675b40 100644
--- a/combo/apps/fargo/migrations/0002_recentdocumentscell_extra_css_class.py
+++ b/combo/apps/fargo/migrations/0002_recentdocumentscell_extra_css_class.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/fargo/migrations/0003_recentdocumentscell_last_update_timestamp.py b/combo/apps/fargo/migrations/0003_recentdocumentscell_last_update_timestamp.py
index e2bb6926..cf027941 100644
--- a/combo/apps/fargo/migrations/0003_recentdocumentscell_last_update_timestamp.py
+++ b/combo/apps/fargo/migrations/0003_recentdocumentscell_last_update_timestamp.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import datetime
 
 from django.db import migrations, models
diff --git a/combo/apps/fargo/migrations/0004_recentdocumentscell_fargo_site.py b/combo/apps/fargo/migrations/0004_recentdocumentscell_fargo_site.py
index baeb08f6..9b2bc467 100644
--- a/combo/apps/fargo/migrations/0004_recentdocumentscell_fargo_site.py
+++ b/combo/apps/fargo/migrations/0004_recentdocumentscell_fargo_site.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/fargo/models.py b/combo/apps/fargo/models.py
index 5578a121..b2c86e81 100644
--- a/combo/apps/fargo/models.py
+++ b/combo/apps/fargo/models.py
@@ -62,7 +62,7 @@ class RecentDocumentsCell(CellBase):
         user = kwargs.get('user')
         if not user or user.is_anonymous:
             return False
-        return super(RecentDocumentsCell, self).is_visible(**kwargs)
+        return super().is_visible(**kwargs)
 
     @classmethod
     def is_enabled(cls):
@@ -105,7 +105,7 @@ class RecentDocumentsCell(CellBase):
 
     def render(self, context):
         context.update(self.get_json('api/documents/recently-added/', context))
-        return super(RecentDocumentsCell, self).render(context)
+        return super().render(context)
 
 
 def get_fargo_services():
diff --git a/combo/apps/gallery/migrations/0001_initial.py b/combo/apps/gallery/migrations/0001_initial.py
index 1a6dfb60..3bb88467 100644
--- a/combo/apps/gallery/migrations/0001_initial.py
+++ b/combo/apps/gallery/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/gallery/migrations/0002_image_title.py b/combo/apps/gallery/migrations/0002_image_title.py
index 51e0898c..8ae59e82 100644
--- a/combo/apps/gallery/migrations/0002_image_title.py
+++ b/combo/apps/gallery/migrations/0002_image_title.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/gallery/migrations/0003_gallerycell_title.py b/combo/apps/gallery/migrations/0003_gallerycell_title.py
index 23b0be6c..7a07665e 100644
--- a/combo/apps/gallery/migrations/0003_gallerycell_title.py
+++ b/combo/apps/gallery/migrations/0003_gallerycell_title.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/gallery/views.py b/combo/apps/gallery/views.py
index 6742a958..a3f5c9a3 100644
--- a/combo/apps/gallery/views.py
+++ b/combo/apps/gallery/views.py
@@ -40,10 +40,10 @@ class ImageAddView(CreateView):
         form.instance.gallery_id = self.kwargs.get('gallery_pk')
         other_images = form.instance.gallery.image_set.all()
         if other_images:
-            form.instance.order = max([x.order for x in other_images]) + 1
+            form.instance.order = max(x.order for x in other_images) + 1
         else:
             form.instance.order = 0
-        return super(ImageAddView, self).form_valid(form)
+        return super().form_valid(form)
 
     def get_success_url(self):
         return (
diff --git a/combo/apps/kb/migrations/0001_initial.py b/combo/apps/kb/migrations/0001_initial.py
index 92dfab97..ebeade57 100644
--- a/combo/apps/kb/migrations/0001_initial.py
+++ b/combo/apps/kb/migrations/0001_initial.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2020-01-31 15:40
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.db import migrations, models
diff --git a/combo/apps/kb/migrations/0002_auto_20200203_1137.py b/combo/apps/kb/migrations/0002_auto_20200203_1137.py
index bb767910..ff12ea30 100644
--- a/combo/apps/kb/migrations/0002_auto_20200203_1137.py
+++ b/combo/apps/kb/migrations/0002_auto_20200203_1137.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.17 on 2020-02-03 10:37
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/kb/models.py b/combo/apps/kb/models.py
index 2827a86c..b503ae15 100644
--- a/combo/apps/kb/models.py
+++ b/combo/apps/kb/models.py
@@ -44,7 +44,7 @@ class LatestPageUpdatesCell(CellBase):
         verbose_name = _('Latest Page Updates')
 
     def get_cell_extra_context(self, context):
-        extra_context = super(LatestPageUpdatesCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         if self.root_page:
             pages = self.root_page.get_descendants_and_me()
         else:
diff --git a/combo/apps/lingo/forms.py b/combo/apps/lingo/forms.py
index dc279de0..51d6f17d 100644
--- a/combo/apps/lingo/forms.py
+++ b/combo/apps/lingo/forms.py
@@ -98,7 +98,7 @@ class RegieForm(forms.ModelForm):
         ]
 
     def __init__(self, *args, **kwargs):
-        super(RegieForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         fields, initial = create_form_fields(
             self.instance.eopayment.get_parameters(scope='transaction'),
             self.instance.transaction_options,
@@ -107,7 +107,7 @@ class RegieForm(forms.ModelForm):
         self.initial.update(initial)
 
     def save(self):
-        instance = super(RegieForm, self).save()
+        instance = super().save()
         instance.transaction_options = compute_json_field(
             self.instance.eopayment.get_parameters(scope='transaction'), self.cleaned_data
         )
@@ -121,7 +121,7 @@ class PaymentBackendForm(forms.ModelForm):
         fields = ['label', 'slug', 'service']
 
     def __init__(self, *args, **kwargs):
-        super(PaymentBackendForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         fields, initial = create_form_fields(
             self.instance.eopayment.get_parameters(scope='global'), self.instance.service_options
         )
@@ -131,7 +131,7 @@ class PaymentBackendForm(forms.ModelForm):
             self.fields['service'].disabled = True
 
     def save(self):
-        instance = super(PaymentBackendForm, self).save()
+        instance = super().save()
         instance.service_options = compute_json_field(
             self.instance.eopayment.get_parameters(scope='global'), self.cleaned_data
         )
@@ -148,7 +148,7 @@ class TransactionExportForm(forms.Form):
     )
 
     def __init__(self, *args, **kwargs):
-        super(TransactionExportForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         today = datetime.date.today()
         self.initial['start_date'] = today - datetime.timedelta(days=30)
         self.initial['end_date'] = today
diff --git a/combo/apps/lingo/management/commands/notify_new_remote_invoices.py b/combo/apps/lingo/management/commands/notify_new_remote_invoices.py
index 5b0d969f..17d77ad4 100644
--- a/combo/apps/lingo/management/commands/notify_new_remote_invoices.py
+++ b/combo/apps/lingo/management/commands/notify_new_remote_invoices.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 #
 # lingo - basket and payment system
 # Copyright (C) 2018  Entr'ouvert
diff --git a/combo/apps/lingo/manager_views.py b/combo/apps/lingo/manager_views.py
index f6271290..a6771d12 100644
--- a/combo/apps/lingo/manager_views.py
+++ b/combo/apps/lingo/manager_views.py
@@ -95,7 +95,7 @@ class TransactionListView(ListView):
     paginate_by = 10
 
     def get_context_data(self, **kwargs):
-        context = super(TransactionListView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['query'] = self.request.GET.get('q') or ''
         return context
 
@@ -126,7 +126,7 @@ class BasketItemErrorListView(ListView):
     paginate_by = 10
 
     def get_context_data(self, **kwargs):
-        context = super(BasketItemErrorListView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['query'] = self.request.GET.get('q') or ''
         return context
 
diff --git a/combo/apps/lingo/migrations/0001_initial.py b/combo/apps/lingo/migrations/0001_initial.py
index 01f77ab9..8d154e5f 100644
--- a/combo/apps/lingo/migrations/0001_initial.py
+++ b/combo/apps/lingo/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
diff --git a/combo/apps/lingo/migrations/0002_transaction.py b/combo/apps/lingo/migrations/0002_transaction.py
index 25f74677..9a6fccf5 100644
--- a/combo/apps/lingo/migrations/0002_transaction.py
+++ b/combo/apps/lingo/migrations/0002_transaction.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0003_auto_20150306_1047.py b/combo/apps/lingo/migrations/0003_auto_20150306_1047.py
index 10d5bf94..d1ae1fe2 100644
--- a/combo/apps/lingo/migrations/0003_auto_20150306_1047.py
+++ b/combo/apps/lingo/migrations/0003_auto_20150306_1047.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0004_basketitem_notification_date.py b/combo/apps/lingo/migrations/0004_basketitem_notification_date.py
index d29e21d5..19a46aad 100644
--- a/combo/apps/lingo/migrations/0004_basketitem_notification_date.py
+++ b/combo/apps/lingo/migrations/0004_basketitem_notification_date.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0005_auto_20150307_1242.py b/combo/apps/lingo/migrations/0005_auto_20150307_1242.py
index ba83873a..498669b5 100644
--- a/combo/apps/lingo/migrations/0005_auto_20150307_1242.py
+++ b/combo/apps/lingo/migrations/0005_auto_20150307_1242.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0006_transaction_status.py b/combo/apps/lingo/migrations/0006_transaction_status.py
index 81b2c392..43749dee 100644
--- a/combo/apps/lingo/migrations/0006_transaction_status.py
+++ b/combo/apps/lingo/migrations/0006_transaction_status.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0007_lingobasketlinkcell.py b/combo/apps/lingo/migrations/0007_lingobasketlinkcell.py
index 68819d2f..c4c3b2d2 100644
--- a/combo/apps/lingo/migrations/0007_lingobasketlinkcell.py
+++ b/combo/apps/lingo/migrations/0007_lingobasketlinkcell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0008_auto_20150908_1538.py b/combo/apps/lingo/migrations/0008_auto_20150908_1538.py
index 7fefb93d..aab7cdf3 100644
--- a/combo/apps/lingo/migrations/0008_auto_20150908_1538.py
+++ b/combo/apps/lingo/migrations/0008_auto_20150908_1538.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import ckeditor.fields
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0009_auto_20150917_1456.py b/combo/apps/lingo/migrations/0009_auto_20150917_1456.py
index 4772b836..9af893b4 100644
--- a/combo/apps/lingo/migrations/0009_auto_20150917_1456.py
+++ b/combo/apps/lingo/migrations/0009_auto_20150917_1456.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0010_regie_payment_min_amount.py b/combo/apps/lingo/migrations/0010_regie_payment_min_amount.py
index 3522dd76..fa682bb6 100644
--- a/combo/apps/lingo/migrations/0010_regie_payment_min_amount.py
+++ b/combo/apps/lingo/migrations/0010_regie_payment_min_amount.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0011_auto_20151029_1535.py b/combo/apps/lingo/migrations/0011_auto_20151029_1535.py
index 7a29a6fc..de95d54e 100644
--- a/combo/apps/lingo/migrations/0011_auto_20151029_1535.py
+++ b/combo/apps/lingo/migrations/0011_auto_20151029_1535.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0012_auto_20151130_1047.py b/combo/apps/lingo/migrations/0012_auto_20151130_1047.py
index c9a8efd0..403f9d85 100644
--- a/combo/apps/lingo/migrations/0012_auto_20151130_1047.py
+++ b/combo/apps/lingo/migrations/0012_auto_20151130_1047.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0013_transaction_amount.py b/combo/apps/lingo/migrations/0013_transaction_amount.py
index 02e653fa..7ca3687c 100644
--- a/combo/apps/lingo/migrations/0013_transaction_amount.py
+++ b/combo/apps/lingo/migrations/0013_transaction_amount.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0014_auto_20160218_1455.py b/combo/apps/lingo/migrations/0014_auto_20160218_1455.py
index b1c9154a..3242dbe6 100644
--- a/combo/apps/lingo/migrations/0014_auto_20160218_1455.py
+++ b/combo/apps/lingo/migrations/0014_auto_20160218_1455.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0015_auto_20160309_2150.py b/combo/apps/lingo/migrations/0015_auto_20160309_2150.py
index 1ea74382..ea28e368 100644
--- a/combo/apps/lingo/migrations/0015_auto_20160309_2150.py
+++ b/combo/apps/lingo/migrations/0015_auto_20160309_2150.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0016_auto_20160319_2028.py b/combo/apps/lingo/migrations/0016_auto_20160319_2028.py
index e6d618c5..4d702a94 100644
--- a/combo/apps/lingo/migrations/0016_auto_20160319_2028.py
+++ b/combo/apps/lingo/migrations/0016_auto_20160319_2028.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0017_auto_20160327_0831.py b/combo/apps/lingo/migrations/0017_auto_20160327_0831.py
index b0cbaa9c..2353e346 100644
--- a/combo/apps/lingo/migrations/0017_auto_20160327_0831.py
+++ b/combo/apps/lingo/migrations/0017_auto_20160327_0831.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import ckeditor.fields
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0018_auto_20160527_0638.py b/combo/apps/lingo/migrations/0018_auto_20160527_0638.py
index 8e86eafd..cbeb174f 100644
--- a/combo/apps/lingo/migrations/0018_auto_20160527_0638.py
+++ b/combo/apps/lingo/migrations/0018_auto_20160527_0638.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0019_auto_20160531_1336.py b/combo/apps/lingo/migrations/0019_auto_20160531_1336.py
index b22a166d..639b234e 100644
--- a/combo/apps/lingo/migrations/0019_auto_20160531_1336.py
+++ b/combo/apps/lingo/migrations/0019_auto_20160531_1336.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0020_auto_20160606_1803.py b/combo/apps/lingo/migrations/0020_auto_20160606_1803.py
index 395111f0..72703818 100644
--- a/combo/apps/lingo/migrations/0020_auto_20160606_1803.py
+++ b/combo/apps/lingo/migrations/0020_auto_20160606_1803.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0021_transactionoperation.py b/combo/apps/lingo/migrations/0021_transactionoperation.py
index d4447368..e3ac5594 100644
--- a/combo/apps/lingo/migrations/0021_transactionoperation.py
+++ b/combo/apps/lingo/migrations/0021_transactionoperation.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0022_regie_is_default.py b/combo/apps/lingo/migrations/0022_regie_is_default.py
index 8a7e1208..27d08ab8 100644
--- a/combo/apps/lingo/migrations/0022_regie_is_default.py
+++ b/combo/apps/lingo/migrations/0022_regie_is_default.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0023_auto_20160928_1152.py b/combo/apps/lingo/migrations/0023_auto_20160928_1152.py
index 4b7b4050..c650eddd 100644
--- a/combo/apps/lingo/migrations/0023_auto_20160928_1152.py
+++ b/combo/apps/lingo/migrations/0023_auto_20160928_1152.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0024_selfdeclaredinvoicepayment.py b/combo/apps/lingo/migrations/0024_selfdeclaredinvoicepayment.py
index 29376662..b1d58616 100644
--- a/combo/apps/lingo/migrations/0024_selfdeclaredinvoicepayment.py
+++ b/combo/apps/lingo/migrations/0024_selfdeclaredinvoicepayment.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import ckeditor.fields
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0025_auto_20161206_1713.py b/combo/apps/lingo/migrations/0025_auto_20161206_1713.py
index 502de149..9fc31e1a 100644
--- a/combo/apps/lingo/migrations/0025_auto_20161206_1713.py
+++ b/combo/apps/lingo/migrations/0025_auto_20161206_1713.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0026_auto_20161215_1447.py b/combo/apps/lingo/migrations/0026_auto_20161215_1447.py
index 4596b1f9..9b5bbd1b 100644
--- a/combo/apps/lingo/migrations/0026_auto_20161215_1447.py
+++ b/combo/apps/lingo/migrations/0026_auto_20161215_1447.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0027_auto_20170214_2006.py b/combo/apps/lingo/migrations/0027_auto_20170214_2006.py
index d5151966..f557b3f1 100644
--- a/combo/apps/lingo/migrations/0027_auto_20170214_2006.py
+++ b/combo/apps/lingo/migrations/0027_auto_20170214_2006.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import datetime
 
 from django.db import migrations, models
diff --git a/combo/apps/lingo/migrations/0028_tipipaymentformcell.py b/combo/apps/lingo/migrations/0028_tipipaymentformcell.py
index ab4eeb0b..76673858 100644
--- a/combo/apps/lingo/migrations/0028_tipipaymentformcell.py
+++ b/combo/apps/lingo/migrations/0028_tipipaymentformcell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 import combo.data.fields
diff --git a/combo/apps/lingo/migrations/0029_auto_20170528_1334.py b/combo/apps/lingo/migrations/0029_auto_20170528_1334.py
index 5b15cadf..9c97ecc9 100644
--- a/combo/apps/lingo/migrations/0029_auto_20170528_1334.py
+++ b/combo/apps/lingo/migrations/0029_auto_20170528_1334.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0030_transaction_to_be_paid_remote_items.py b/combo/apps/lingo/migrations/0030_transaction_to_be_paid_remote_items.py
index 8e733a84..f0499541 100644
--- a/combo/apps/lingo/migrations/0030_transaction_to_be_paid_remote_items.py
+++ b/combo/apps/lingo/migrations/0030_transaction_to_be_paid_remote_items.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0031_basketitem_waiting_date.py b/combo/apps/lingo/migrations/0031_basketitem_waiting_date.py
index f9573aed..abb63f65 100644
--- a/combo/apps/lingo/migrations/0031_basketitem_waiting_date.py
+++ b/combo/apps/lingo/migrations/0031_basketitem_waiting_date.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.8 on 2018-01-31 14:24
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0032_basketitem_capture_date.py b/combo/apps/lingo/migrations/0032_basketitem_capture_date.py
index 285f8f76..80e844bc 100644
--- a/combo/apps/lingo/migrations/0032_basketitem_capture_date.py
+++ b/combo/apps/lingo/migrations/0032_basketitem_capture_date.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.15 on 2018-10-05 15:33
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0033_auto_20181204_2241.py b/combo/apps/lingo/migrations/0033_auto_20181204_2241.py
index c91630a0..46fd9f3e 100644
--- a/combo/apps/lingo/migrations/0033_auto_20181204_2241.py
+++ b/combo/apps/lingo/migrations/0033_auto_20181204_2241.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/lingo/migrations/0034_auto_20190426_1159.py b/combo/apps/lingo/migrations/0034_auto_20190426_1159.py
index d50fc053..0b05603f 100644
--- a/combo/apps/lingo/migrations/0034_auto_20190426_1159.py
+++ b/combo/apps/lingo/migrations/0034_auto_20190426_1159.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2019-04-26 09:59
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.contrib.postgres.fields import JSONField
diff --git a/combo/apps/lingo/migrations/0035_auto_20190426_1124.py b/combo/apps/lingo/migrations/0035_auto_20190426_1124.py
index 47fed104..89c417d2 100644
--- a/combo/apps/lingo/migrations/0035_auto_20190426_1124.py
+++ b/combo/apps/lingo/migrations/0035_auto_20190426_1124.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2019-04-26 09:24
-from __future__ import unicode_literals
 
 from django.db import migrations
 
diff --git a/combo/apps/lingo/migrations/0036_auto_20190426_1202.py b/combo/apps/lingo/migrations/0036_auto_20190426_1202.py
index 68e68c21..cb58b602 100644
--- a/combo/apps/lingo/migrations/0036_auto_20190426_1202.py
+++ b/combo/apps/lingo/migrations/0036_auto_20190426_1202.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2019-04-26 10:02
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.db import migrations, models
diff --git a/combo/apps/lingo/migrations/0037_regie_transaction_options.py b/combo/apps/lingo/migrations/0037_regie_transaction_options.py
index c0cf568a..9b0f7869 100644
--- a/combo/apps/lingo/migrations/0037_regie_transaction_options.py
+++ b/combo/apps/lingo/migrations/0037_regie_transaction_options.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2019-05-14 12:02
-from __future__ import unicode_literals
 
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
diff --git a/combo/apps/lingo/migrations/0038_hide_cell_if_empty.py b/combo/apps/lingo/migrations/0038_hide_cell_if_empty.py
index 46db6a6c..bcbb5200 100644
--- a/combo/apps/lingo/migrations/0038_hide_cell_if_empty.py
+++ b/combo/apps/lingo/migrations/0038_hide_cell_if_empty.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2020-03-31 13:12
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0039_transaction_transaction_date.py b/combo/apps/lingo/migrations/0039_transaction_transaction_date.py
index 1e1313b5..1a394218 100644
--- a/combo/apps/lingo/migrations/0039_transaction_transaction_date.py
+++ b/combo/apps/lingo/migrations/0039_transaction_transaction_date.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.29 on 2020-04-03 20:19
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0040_auto_20200608_1222.py b/combo/apps/lingo/migrations/0040_auto_20200608_1222.py
index dd152747..1e16468c 100644
--- a/combo/apps/lingo/migrations/0040_auto_20200608_1222.py
+++ b/combo/apps/lingo/migrations/0040_auto_20200608_1222.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2020-06-08 10:22
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/migrations/0041_regie_can_pay_only_one_basket_item.py b/combo/apps/lingo/migrations/0041_regie_can_pay_only_one_basket_item.py
index 1c681941..d2f98b2f 100644
--- a/combo/apps/lingo/migrations/0041_regie_can_pay_only_one_basket_item.py
+++ b/combo/apps/lingo/migrations/0041_regie_can_pay_only_one_basket_item.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2020-10-01 11:53
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/lingo/models.py b/combo/apps/lingo/models.py
index 0c83279d..9d7eb647 100644
--- a/combo/apps/lingo/models.py
+++ b/combo/apps/lingo/models.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 #
 # lingo - basket and payment system
 # Copyright (C) 2015  Entr'ouvert
@@ -68,7 +67,7 @@ class PaymentException(Exception):
 
 class UnsignedPaymentException(PaymentException):
     def __init__(self, transaction, *args, **kwargs):
-        super(UnsignedPaymentException, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.transaction = transaction
 
 
@@ -163,7 +162,7 @@ class PaymentBackend(models.Model):
 
     def make_eopayment(self, *, request=None, automatic_return_url=None, normal_return_url=None, **kwargs):
         options = self.service_options or {}
-        if isinstance(options, six.string_types):
+        if isinstance(options, str):
             # backward compatibility when used againt postgresql < 9.4 and
             # service_options is received as a string.
             try:
@@ -329,7 +328,7 @@ class Regie(models.Model):
             qs.update(is_default=False)
         elif not self.__class__.objects.filter(is_default=True).exists():
             self.is_default = True
-        super(Regie, self).save(*args, **kwargs)
+        super().save(*args, **kwargs)
 
     def natural_key(self):
         return (self.slug,)
@@ -688,7 +687,7 @@ class BasketItem(models.Model):
         return reverse('basket-item-pay-view', kwargs={'item_signature': signature})
 
 
-class RemoteItem(object):
+class RemoteItem:
     payment_date = None
 
     def __init__(
@@ -745,7 +744,7 @@ class RemoteItem(object):
 
     @classmethod
     def transactions_for_remote_items(cls, queryset, remote_items):
-        remote_item_ids = set(remote_item.id for remote_item in remote_items if not remote_item.paid)
+        remote_item_ids = {remote_item.id for remote_item in remote_items if not remote_item.paid}
         if not remote_item_ids:
             return Transaction.objects.none()
 
@@ -878,7 +877,7 @@ class Transaction(models.Model):
                     'unable to notify payment for remote item %s from transaction %s', item_id, self
                 )
             else:
-                logger.info(u'notified payment for remote item %s from transaction %s', item_id, self)
+                logger.info('notified payment for remote item %s from transaction %s', item_id, self)
                 subject = _('Invoice #%s') % remote_item.display_id
                 local_item = BasketItem.objects.create(
                     user=self.user,
@@ -1066,10 +1065,10 @@ class LingoBasketCell(CellBase):
         items = BasketItem.get_items_to_be_paid(context['request'].user)
         if not items:
             return
-        total = sum([x.amount for x in items])
+        total = sum(x.amount for x in items)
         if total == int(total):
             total = int(total)
-        return {'badge': _(u'%s€') % localize(total)}
+        return {'badge': _('%s€') % localize(total)}
 
     def render(self, context):
         basket_template = template.loader.get_template('lingo/combo/basket.html')
@@ -1081,7 +1080,7 @@ class LingoBasketCell(CellBase):
             regies[item.regie_id]['items'].append(item)
 
         for items in regies.values():
-            items['total'] = sum([x.amount for x in items['items']])
+            items['total'] = sum(x.amount for x in items['items'])
 
         context['regies'] = sorted(regies.values(), key=lambda x: x['regie'].label)
         return basket_template.render(context)
@@ -1147,7 +1146,7 @@ class LingoBasketLinkCell(CellBase):
             return ''
         basket_template = template.loader.get_template('lingo/combo/basket_link.html')
         context['items'] = BasketItem.get_items_to_be_paid(user=context['request'].user)
-        context['total'] = sum([x.amount for x in context['items']])
+        context['total'] = sum(x.amount for x in context['items'])
         return basket_template.render(context)
 
 
@@ -1198,7 +1197,7 @@ class Items(CellBase):
         return [], []
 
     def get_cell_extra_context(self, context):
-        ctx = super(Items, self).get_cell_extra_context(context)
+        ctx = super().get_cell_extra_context(context)
         if context.get('placeholder_search_mode'):
             # don't call webservices when we're just looking for placeholders
             return ctx
@@ -1220,7 +1219,7 @@ class Items(CellBase):
         self.context = context
         if not context.get('synchronous'):
             raise NothingInCacheException()
-        return super(Items, self).render(context)
+        return super().render(context)
 
 
 @register_cell_class
@@ -1273,7 +1272,7 @@ class SelfDeclaredInvoicePayment(Items):
     def render(self, context):
         context['synchronous'] = True
         context['page_path'] = context['request'].path
-        return super(SelfDeclaredInvoicePayment, self).render(context)
+        return super().render(context)
 
 
 TIPI_CONTROL_PROCOTOLS = (
@@ -1326,7 +1325,7 @@ class TipiPaymentFormCell(CellBase):
         return getattr(settings, 'LINGO_TIPI_CELL_PAYMENT_URL', 'https://www.payfip.gouv.fr/tpa/paiement.web')
 
     def get_cell_extra_context(self, context):
-        extra_context = super(TipiPaymentFormCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         form_fields = self.get_default_form_class().base_fields
         field_definitions = (
             {'protocol': 'any', 'fields': ['exer']},
diff --git a/combo/apps/lingo/utils.py b/combo/apps/lingo/utils.py
index 91bc85ea..7c8394aa 100644
--- a/combo/apps/lingo/utils.py
+++ b/combo/apps/lingo/utils.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 # lingo - basket and payment system
 # Copyright (C) 2020  Entr'ouvert
 #
diff --git a/combo/apps/lingo/views.py b/combo/apps/lingo/views.py
index bd9e26c5..970ce781 100644
--- a/combo/apps/lingo/views.py
+++ b/combo/apps/lingo/views.py
@@ -98,9 +98,9 @@ def lingo_check_request_signature(request):
 class LocaleDecimal(Decimal):
     # accept , instead of . for French users comfort
     def __new__(cls, value="0", *args, **kwargs):
-        if isinstance(value, six.string_types) and settings.LANGUAGE_CODE.startswith('fr-'):
+        if isinstance(value, str) and settings.LANGUAGE_CODE.startswith('fr-'):
             value = value.replace(',', '.')
-        return super(LocaleDecimal, cls).__new__(cls, value, *args, **kwargs)
+        return super().__new__(cls, value, *args, **kwargs)
 
 
 class RegiesApiView(ListView):
@@ -121,11 +121,11 @@ class AddBasketItemApiView(View):
 
     @csrf_exempt
     def dispatch(self, *args, **kwargs):
-        return super(AddBasketItemApiView, self).dispatch(*args, **kwargs)
+        return super().dispatch(*args, **kwargs)
 
     def get_amount(self, amount):
         if isinstance(amount, list):
-            d = Decimal(sum([LocaleDecimal(a) for a in amount]))
+            d = Decimal(sum(LocaleDecimal(a) for a in amount))
         else:
             d = LocaleDecimal(amount)
         return d.quantize(Decimal('0.01'), ROUND_HALF_UP)
@@ -237,7 +237,7 @@ class RemoveBasketItemApiView(View):
 
     @csrf_exempt
     def dispatch(self, *args, **kwargs):
-        return super(RemoveBasketItemApiView, self).dispatch(*args, **kwargs)
+        return super().dispatch(*args, **kwargs)
 
     def post(self, request, *args, **kwargs):
         if not lingo_check_request_signature(request):
@@ -285,7 +285,7 @@ class ValidateTransactionApiView(View):
 
     @csrf_exempt
     def dispatch(self, *args, **kwargs):
-        return super(ValidateTransactionApiView, self).dispatch(*args, **kwargs)
+        return super().dispatch(*args, **kwargs)
 
     def post(self, request, *args, **kwargs):
         if not lingo_check_request_signature(request):
@@ -295,22 +295,22 @@ class ValidateTransactionApiView(View):
             transaction = Transaction.objects.get(id=request.GET['transaction_id'])
         except Transaction.DoesNotExist:
             logger.warning(
-                u'received validate request for unknown transaction %s', request.GET['transaction_id']
+                'received validate request for unknown transaction %s', request.GET['transaction_id']
             )
             raise Http404
 
         amount = LocaleDecimal(request.GET['amount'])
 
-        logger.info(u'validating amount %s for transaction %s', amount, smart_text(transaction.id))
+        logger.info('validating amount %s for transaction %s', amount, smart_text(transaction.id))
         try:
             result = transaction.make_eopayment(request=request).backend.validate(
                 amount, transaction.bank_data
             )
         except eopayment.ResponseError as e:
-            logger.error(u'failed in validation operation: %s', e)
+            logger.error('failed in validation operation: %s', e)
             return JsonResponse({'err': 1, 'e': force_text(e)})
 
-        logger.info(u'bank validation result: %r', result)
+        logger.info('bank validation result: %r', result)
         operation = TransactionOperation(
             transaction=transaction, kind='validation', amount=amount, bank_result=result
         )
@@ -324,7 +324,7 @@ class CancelTransactionApiView(View):
 
     @csrf_exempt
     def dispatch(self, *args, **kwargs):
-        return super(CancelTransactionApiView, self).dispatch(*args, **kwargs)
+        return super().dispatch(*args, **kwargs)
 
     def post(self, request, *args, **kwargs):
         if not lingo_check_request_signature(request):
@@ -334,20 +334,20 @@ class CancelTransactionApiView(View):
             transaction = Transaction.objects.get(id=request.GET['transaction_id'])
         except Transaction.DoesNotExist:
             logger.warning(
-                u'received validate request for unknown transaction %s', request.GET['transaction_id']
+                'received validate request for unknown transaction %s', request.GET['transaction_id']
             )
             raise Http404
 
         amount = LocaleDecimal(request.GET['amount'])
 
-        logger.info(u'cancelling amount %s for transaction %s', amount, smart_text(transaction.id))
+        logger.info('cancelling amount %s for transaction %s', amount, smart_text(transaction.id))
         try:
             result = transaction.make_eopayment(request=request).backend.cancel(amount, transaction.bank_data)
         except eopayment.ResponseError as e:
-            logger.error(u'failed in cancel operation: %s', e)
+            logger.error('failed in cancel operation: %s', e)
             return JsonResponse({'err': 1, 'e': force_text(e)})
 
-        logger.info(u'bank cancellation result: %r', result)
+        logger.info('bank cancellation result: %r', result)
         operation = TransactionOperation(
             transaction=transaction, kind='cancellation', amount=amount, bank_result=result
         )
@@ -356,7 +356,7 @@ class CancelTransactionApiView(View):
         return JsonResponse({'err': 0, 'extra': result})
 
 
-class PayMixin(object):
+class PayMixin:
     @atomic
     def handle_payment(
         self, request, regie, items, remote_items, next_url='/', email='', firstname='', lastname=''
@@ -381,17 +381,17 @@ class PayMixin(object):
             messages.error(request, _('Some items are already paid.'))
             return HttpResponseRedirect(next_url)
 
-        total_amount = sum([x.amount for x in remote_items or items])
+        total_amount = sum(x.amount for x in remote_items or items)
 
         if total_amount < regie.payment_min_amount:
-            messages.warning(request, _(u'Minimal payment amount is %s €.') % regie.payment_min_amount)
+            messages.warning(request, _('Minimal payment amount is %s €.') % regie.payment_min_amount)
             return HttpResponseRedirect(
                 get_payment_status_view(next_url=next_url if remote_items else items[0].source_url)
             )
 
         for item in items:
             if item.regie != regie:
-                messages.error(request, _(u'Invalid grouping for basket items.'))
+                messages.error(request, _('Invalid grouping for basket items.'))
                 return HttpResponseRedirect(next_url)
         user = request.user if request.user.is_authenticated else None
         transaction = Transaction()
@@ -454,7 +454,7 @@ class PayMixin(object):
             messages.error(request, _('Failed to initiate payment request'))
             return HttpResponseRedirect(get_payment_status_view(next_url=next_url))
         logger.info(
-            u'emitted payment request with id %s',
+            'emitted payment request with id %s',
             smart_text(order_id),
             extra={'eopayment_order_id': smart_text(order_id), 'eopayment_data': repr(data)},
         )
@@ -505,14 +505,14 @@ class PayView(PayMixin, View):
                 for item_id in request.POST.getlist('item'):
                     remote_items.append(regie.get_invoice(user, item_id, update_paid=True))
             except (requests.exceptions.RequestException, RemoteInvoiceException):
-                messages.error(request, _(u'Technical error: impossible to retrieve invoices.'))
+                messages.error(request, _('Technical error: impossible to retrieve invoices.'))
                 return HttpResponseRedirect(next_url)
             except ObjectDoesNotExist:
-                messages.error(request, _(u'No invoice was found.'))
+                messages.error(request, _('No invoice was found.'))
                 return HttpResponseRedirect(next_url)
         else:
             if user is None:
-                messages.error(request, _(u'Payment requires to be logged in.'))
+                messages.error(request, _('Payment requires to be logged in.'))
                 return HttpResponseRedirect(next_url)
 
             if not regie_id:
@@ -521,7 +521,7 @@ class PayView(PayMixin, View):
                 regie_id = items[0].regie_id
                 for item in items:
                     if item.regie_id != regie_id:
-                        messages.error(request, _(u'Invalid grouping for basket items.'))
+                        messages.error(request, _('Invalid grouping for basket items.'))
                         return HttpResponseRedirect(next_url)
 
             regie = Regie.objects.get(id=regie_id)
@@ -540,7 +540,7 @@ class PayView(PayMixin, View):
             capture_date = items[0].capture_date
             for item in items:
                 if item.capture_date != capture_date:
-                    messages.error(request, _(u'Invalid grouping for basket items: different capture dates.'))
+                    messages.error(request, _('Invalid grouping for basket items: different capture dates.'))
                     return HttpResponseRedirect(next_url)
 
         if user:
@@ -549,7 +549,7 @@ class PayView(PayMixin, View):
             # user is not authenticated, it comes from ItemCell where an email
             # can be given in the payment form.
             if not request.POST.get('email'):
-                messages.warning(request, _(u'You must give an email address.'))
+                messages.warning(request, _('You must give an email address.'))
                 return HttpResponseRedirect(request.POST.get('item_url'))
             email = request.POST.get('email')
 
@@ -634,7 +634,7 @@ class PaymentView(View):
             raise Http404("A payment backend or regie primary key or slug must be specified")
 
         payment = payment_backend.make_eopayment(request=request)
-        logger.info(u'received payment response: %r', backend_response)
+        logger.info('received payment response: %r', backend_response)
         try:
             eopayment_response_kwargs = {'redirect': not callback}
             if transaction is not None:
@@ -647,7 +647,7 @@ class PaymentView(View):
             payment_response = payment.response(backend_response, **eopayment_response_kwargs)
         except eopayment.PaymentException as e:
             logger.error(
-                u'failed to process payment response: %s',
+                'failed to process payment response: %s',
                 e,
                 extra={'eopayment_raw_response': repr(backend_response)},
             )
@@ -674,13 +674,13 @@ class CallbackView(PaymentView):
 
     @csrf_exempt
     def dispatch(self, *args, **kwargs):
-        return super(CallbackView, self).dispatch(*args, **kwargs)
+        return super().dispatch(*args, **kwargs)
 
 
 class ReturnView(PaymentView):
     @csrf_exempt
     def dispatch(self, *args, **kwargs):
-        return super(ReturnView, self).dispatch(*args, **kwargs)
+        return super().dispatch(*args, **kwargs)
 
     def get(self, request, *args, **kwargs):
         return self.handle_return(request, request.environ['QUERY_STRING'], **kwargs)
@@ -735,7 +735,7 @@ class ReturnView(PaymentView):
 
 
 class ItemDownloadView(View):
-    http_method_names = [u'get']
+    http_method_names = ['get']
 
     def get(self, request, *args, **kwargs):
         try:
@@ -768,7 +768,7 @@ class ItemDownloadView(View):
 
 
 class ItemView(TemplateView):
-    http_method_names = [u'get']
+    http_method_names = ['get']
 
     def get_context_data(self, **kwargs):
         ret = {'item_url': self.request.get_full_path()}
@@ -804,7 +804,7 @@ class CancelItemView(DetailView):
     template_name = 'lingo/combo/cancel-item.html'
 
     def get_context_data(self, **kwargs):
-        context = super(CancelItemView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['basket_url'] = get_basket_url()
         return context
 
@@ -831,7 +831,7 @@ class SelfInvoiceView(View):
 
     @csrf_exempt
     def dispatch(self, *args, **kwargs):
-        return super(SelfInvoiceView, self).dispatch(*args, **kwargs)
+        return super().dispatch(*args, **kwargs)
 
     def get(self, request, *args, **kwargs):
         try:
@@ -902,7 +902,7 @@ class PaymentStatusView(View):
         next_url = request.session.get('lingo_next_url', {}).get(str(transaction_id))
         if not next_url:
             next_url = get_basket_url()
-            if len(set([item.source_url for item in transaction.items.all()])) == 1:
+            if len({item.source_url for item in transaction.items.all()}) == 1:
                 next_url = transaction.items.first().source_url
         next_url = request.build_absolute_uri(next_url)
 
diff --git a/combo/apps/maps/forms.py b/combo/apps/maps/forms.py
index 15b08e39..dfeebb83 100644
--- a/combo/apps/maps/forms.py
+++ b/combo/apps/maps/forms.py
@@ -40,7 +40,7 @@ class MapLayerForm(forms.ModelForm):
         }
 
     def __init__(self, *args, **kwargs):
-        super(MapLayerForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         if self.instance.pk is None:
             # new instance, delete some fields
             del self.fields['slug']
@@ -71,7 +71,7 @@ class MapLayerForm(forms.ModelForm):
                 del self.fields[field]
 
     def clean(self):
-        cleaned_data = super(MapLayerForm, self).clean()
+        cleaned_data = super().clean()
 
         if self.instance.kind == 'tiles' and cleaned_data.get('tiles_default') is True:
             if MapLayer.objects.filter(kind='tiles', tiles_default=True).exclude(pk=self.instance.pk):
@@ -88,7 +88,7 @@ class MapLayerOptionsForm(forms.ModelForm):
 
     def __init__(self, *args, **kwargs):
         self.kind = kwargs.pop('kind')
-        super(MapLayerOptionsForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         # if edition, no possibility to change the layer
         if self.instance.pk:
             del self.fields['map_layer']
diff --git a/combo/apps/maps/manager_views.py b/combo/apps/maps/manager_views.py
index b6165519..624e717c 100644
--- a/combo/apps/maps/manager_views.py
+++ b/combo/apps/maps/manager_views.py
@@ -26,7 +26,7 @@ from .forms import MapLayerForm, MapLayerOptionsForm
 from .models import Map, MapLayer, MapLayerOptions
 
 
-class MapLayerMixin(object):
+class MapLayerMixin:
     model = MapLayer
     success_url = reverse_lazy('maps-manager-homepage')
 
@@ -36,7 +36,7 @@ class ManagerHomeView(MapLayerMixin, ListView):
     ordering = ['kind', 'label']
 
     def get_context_data(self, **kwargs):
-        context = super(ManagerHomeView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['map_list'] = Map.objects.all()
         return context
 
@@ -46,7 +46,7 @@ class LayerAddView(MapLayerMixin, CreateView):
     template_name = 'maps/map_layer_form.html'
 
     def get_form_kwargs(self):
-        kwargs = super(LayerAddView, self).get_form_kwargs()
+        kwargs = super().get_form_kwargs()
         kwargs['instance'] = self.model(kind=self.kwargs['kind'])
         return kwargs
 
@@ -69,10 +69,10 @@ class MapCellAddLayer(CreateView):
             self.cell = CellBase.get_cell(kwargs['cell_reference'], page=kwargs['page_pk'])
         except Map.DoesNotExist:
             raise Http404
-        return super(MapCellAddLayer, self).dispatch(request, *args, **kwargs)
+        return super().dispatch(request, *args, **kwargs)
 
     def get_form_kwargs(self):
-        kwargs = super(MapCellAddLayer, self).get_form_kwargs()
+        kwargs = super().get_form_kwargs()
         kwargs['instance'] = MapLayerOptions(map_cell=self.cell)
         kwargs['kind'] = self.kwargs['kind']
         return kwargs
@@ -84,7 +84,7 @@ class MapCellAddLayer(CreateView):
             comment=_('added layer "%(layer)s" to cell "%(cell)s"')
             % {'layer': form.instance.map_layer, 'cell': self.cell},
         )
-        return super(MapCellAddLayer, self).form_valid(form)
+        return super().form_valid(form)
 
     def get_success_url(self):
         return '%s#cell-%s' % (
@@ -106,13 +106,13 @@ class MapCellEditLayer(UpdateView):
         except Map.DoesNotExist:
             raise Http404
         self.object = get_object_or_404(MapLayerOptions, pk=kwargs['layeroptions_pk'], map_cell=self.cell)
-        return super(MapCellEditLayer, self).dispatch(request, *args, **kwargs)
+        return super().dispatch(request, *args, **kwargs)
 
     def get_object(self, *args, **kwargs):
         return self.object
 
     def get_form_kwargs(self):
-        kwargs = super(MapCellEditLayer, self).get_form_kwargs()
+        kwargs = super().get_form_kwargs()
         kwargs['kind'] = self.object.map_layer.kind
         return kwargs
 
@@ -123,7 +123,7 @@ class MapCellEditLayer(UpdateView):
             comment=_('changed options of layer "%(layer)s" in cell "%(cell)s"')
             % {'layer': form.instance.map_layer, 'cell': self.cell},
         )
-        return super(MapCellEditLayer, self).form_valid(form)
+        return super().form_valid(form)
 
     def get_success_url(self):
         return '%s#cell-%s' % (
@@ -144,13 +144,13 @@ class MapCellDeleteLayer(DeleteView):
         except Map.DoesNotExist:
             raise Http404
         self.object = get_object_or_404(MapLayerOptions, pk=kwargs['layeroptions_pk'], map_cell=self.cell)
-        return super(MapCellDeleteLayer, self).dispatch(request, *args, **kwargs)
+        return super().dispatch(request, *args, **kwargs)
 
     def get_object(self, *args, **kwargs):
         return self.object
 
     def delete(self, request, *args, **kwargs):
-        response = super(MapCellDeleteLayer, self).delete(request, *args, **kwargs)
+        response = super().delete(request, *args, **kwargs)
         PageSnapshot.take(
             self.cell.page,
             request=self.request,
diff --git a/combo/apps/maps/migrations/0001_initial.py b/combo/apps/maps/migrations/0001_initial.py
index 2a55f0db..9118b597 100644
--- a/combo/apps/maps/migrations/0001_initial.py
+++ b/combo/apps/maps/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 from combo.apps.maps.models import ICONS
diff --git a/combo/apps/maps/migrations/0002_map.py b/combo/apps/maps/migrations/0002_map.py
index 0f0d608f..8fde6162 100644
--- a/combo/apps/maps/migrations/0002_map.py
+++ b/combo/apps/maps/migrations/0002_map.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/maps/migrations/0003_auto_20170603_1653.py b/combo/apps/maps/migrations/0003_auto_20170603_1653.py
index b1a58621..4953e118 100644
--- a/combo/apps/maps/migrations/0003_auto_20170603_1653.py
+++ b/combo/apps/maps/migrations/0003_auto_20170603_1653.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/maps/migrations/0004_map_initial_state.py b/combo/apps/maps/migrations/0004_map_initial_state.py
index 144c125d..ecfbb4aa 100644
--- a/combo/apps/maps/migrations/0004_map_initial_state.py
+++ b/combo/apps/maps/migrations/0004_map_initial_state.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/maps/migrations/0005_auto_20180212_1742.py b/combo/apps/maps/migrations/0005_auto_20180212_1742.py
index 8970ebdc..783405e3 100644
--- a/combo/apps/maps/migrations/0005_auto_20180212_1742.py
+++ b/combo/apps/maps/migrations/0005_auto_20180212_1742.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/maps/migrations/0006_auto_20180627_0841.py b/combo/apps/maps/migrations/0006_auto_20180627_0841.py
index f7c68d63..42633c26 100644
--- a/combo/apps/maps/migrations/0006_auto_20180627_0841.py
+++ b/combo/apps/maps/migrations/0006_auto_20180627_0841.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/maps/migrations/0007_auto_20180706_1345.py b/combo/apps/maps/migrations/0007_auto_20180706_1345.py
index bdce235e..e0b58db4 100644
--- a/combo/apps/maps/migrations/0007_auto_20180706_1345.py
+++ b/combo/apps/maps/migrations/0007_auto_20180706_1345.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2018-07-06 11:45
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/maps/migrations/0008_map_layer_options.py b/combo/apps/maps/migrations/0008_map_layer_options.py
index 854f51af..3c8a012e 100644
--- a/combo/apps/maps/migrations/0008_map_layer_options.py
+++ b/combo/apps/maps/migrations/0008_map_layer_options.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.db.models.deletion
 from django.db import migrations, models
 
@@ -54,7 +51,7 @@ class Migration(migrations.Migration):
                 ),
                 migrations.AlterUniqueTogether(
                     name='maplayeroptions',
-                    unique_together=set([('map_cell', 'map_layer')]),
+                    unique_together={('map_cell', 'map_layer')},
                 ),
             ],
             database_operations=[],
diff --git a/combo/apps/maps/migrations/0009_map_layer_kind.py b/combo/apps/maps/migrations/0009_map_layer_kind.py
index a9594228..035e9d1d 100644
--- a/combo/apps/maps/migrations/0009_map_layer_kind.py
+++ b/combo/apps/maps/migrations/0009_map_layer_kind.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2020-02-07 09:00
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/maps/migrations/0010_map_layer_opacity.py b/combo/apps/maps/migrations/0010_map_layer_opacity.py
index 13853dcd..808d9d7e 100644
--- a/combo/apps/maps/migrations/0010_map_layer_opacity.py
+++ b/combo/apps/maps/migrations/0010_map_layer_opacity.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.core.validators
 from django.db import migrations, models
 
diff --git a/combo/apps/maps/migrations/0011_map_layer_set_slug.py b/combo/apps/maps/migrations/0011_map_layer_set_slug.py
index fe900a83..27ef5d35 100644
--- a/combo/apps/maps/migrations/0011_map_layer_set_slug.py
+++ b/combo/apps/maps/migrations/0011_map_layer_set_slug.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations
 from django.utils.text import slugify
 
diff --git a/combo/apps/maps/migrations/0012_map_layer_slug_unicity.py b/combo/apps/maps/migrations/0012_map_layer_slug_unicity.py
index 036422ca..c33eb12d 100644
--- a/combo/apps/maps/migrations/0012_map_layer_slug_unicity.py
+++ b/combo/apps/maps/migrations/0012_map_layer_slug_unicity.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/maps/migrations/0013_geojson_query_parameter.py b/combo/apps/maps/migrations/0013_geojson_query_parameter.py
index c13782b7..a7dfd0f2 100644
--- a/combo/apps/maps/migrations/0013_geojson_query_parameter.py
+++ b/combo/apps/maps/migrations/0013_geojson_query_parameter.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/maps/migrations/0014_geojson_circle_param.py b/combo/apps/maps/migrations/0014_geojson_circle_param.py
index 83b20ba9..3df54324 100644
--- a/combo/apps/maps/migrations/0014_geojson_circle_param.py
+++ b/combo/apps/maps/migrations/0014_geojson_circle_param.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/maps/models.py b/combo/apps/maps/models.py
index ea2e3dc3..7ca1b05f 100644
--- a/combo/apps/maps/models.py
+++ b/combo/apps/maps/models.py
@@ -158,7 +158,7 @@ class MapLayer(models.Model):
                 slug = '%s-%s' % (base_slug, i)
                 i += 1
             self.slug = slug
-        super(MapLayer, self).save(*args, **kwargs)
+        super().save(*args, **kwargs)
 
     def __str__(self):
         return self.label
@@ -287,7 +287,7 @@ class MapLayer(models.Model):
 
                 def get_feature_words(properties):
                     for property in properties.values():
-                        if isinstance(property, six.string_types):
+                        if isinstance(property, str):
                             feature_words.append(property)
                         elif isinstance(property, dict):
                             get_feature_words(property)
@@ -465,7 +465,7 @@ class Map(CellBase):
         ]
 
     def get_cell_extra_context(self, context):
-        ctx = super(Map, self).get_cell_extra_context(context)
+        ctx = super().get_cell_extra_context(context)
         ctx['title'] = self.title
         default_position = self.get_default_position()
         ctx['init_lat'] = default_position['lat']
diff --git a/combo/apps/notifications/migrations/0001_initial.py b/combo/apps/notifications/migrations/0001_initial.py
index ee2a2ee3..a1ae0f14 100644
--- a/combo/apps/notifications/migrations/0001_initial.py
+++ b/combo/apps/notifications/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.db import migrations, models
 
diff --git a/combo/apps/notifications/migrations/0002_notificationscell_last_update_timestamp.py b/combo/apps/notifications/migrations/0002_notificationscell_last_update_timestamp.py
index 243161db..8b9a6277 100644
--- a/combo/apps/notifications/migrations/0002_notificationscell_last_update_timestamp.py
+++ b/combo/apps/notifications/migrations/0002_notificationscell_last_update_timestamp.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import datetime
 
 from django.db import migrations, models
diff --git a/combo/apps/notifications/migrations/0003_notification_origin.py b/combo/apps/notifications/migrations/0003_notification_origin.py
index be7dcad8..b92ea0a1 100644
--- a/combo/apps/notifications/migrations/0003_notification_origin.py
+++ b/combo/apps/notifications/migrations/0003_notification_origin.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/notifications/migrations/0004_auto_20180316_1026.py b/combo/apps/notifications/migrations/0004_auto_20180316_1026.py
index 6f4bf649..552c0bf9 100644
--- a/combo/apps/notifications/migrations/0004_auto_20180316_1026.py
+++ b/combo/apps/notifications/migrations/0004_auto_20180316_1026.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.11 on 2018-03-16 10:26
-from __future__ import unicode_literals
 
 from django.conf import settings
 from django.db import migrations
@@ -16,6 +14,6 @@ class Migration(migrations.Migration):
     operations = [
         migrations.AlterUniqueTogether(
             name='notification',
-            unique_together=set([('user', 'external_id')]),
+            unique_together={('user', 'external_id')},
         ),
     ]
diff --git a/combo/apps/notifications/migrations/0005_auto_20180324_0025.py b/combo/apps/notifications/migrations/0005_auto_20180324_0025.py
index 445f8613..ea7ceaed 100644
--- a/combo/apps/notifications/migrations/0005_auto_20180324_0025.py
+++ b/combo/apps/notifications/migrations/0005_auto_20180324_0025.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.11 on 2018-03-24 00:25
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/notifications/models.py b/combo/apps/notifications/models.py
index cfc866db..d12abb23 100644
--- a/combo/apps/notifications/models.py
+++ b/combo/apps/notifications/models.py
@@ -181,10 +181,10 @@ class NotificationsCell(CellBase):
         user = kwargs.get('user')
         if user is None or not user.is_authenticated:
             return False
-        return super(NotificationsCell, self).is_visible(**kwargs)
+        return super().is_visible(**kwargs)
 
     def get_cell_extra_context(self, context):
-        extra_context = super(NotificationsCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         user = getattr(context.get('request'), 'user', None)
         if user and user.is_authenticated:
             qs = Notification.objects.visible(user)
diff --git a/combo/apps/pwa/forms.py b/combo/apps/pwa/forms.py
index 1f9948d4..f2a8507b 100644
--- a/combo/apps/pwa/forms.py
+++ b/combo/apps/pwa/forms.py
@@ -27,7 +27,7 @@ class PwaSettingsForm(forms.ModelForm):
         exclude = ('push_notifications_infos',)
 
     def __init__(self, *args, **kwargs):
-        super(PwaSettingsForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.fields['application_icon'].widget.attrs = {'accept': 'image/jpeg,image/png'}
 
     def clean_application_icon(self):
diff --git a/combo/apps/pwa/manager_views.py b/combo/apps/pwa/manager_views.py
index 706beb33..b6fb1e98 100644
--- a/combo/apps/pwa/manager_views.py
+++ b/combo/apps/pwa/manager_views.py
@@ -34,25 +34,25 @@ class ManagerHomeView(UpdateView):
     success_url = reverse_lazy('pwa-manager-homepage')
 
     def get_initial(self):
-        initial = super(ManagerHomeView, self).get_initial()
+        initial = super().get_initial()
         initial['application_name'] = self.get_object().get_application_name()
         return initial
 
     def form_valid(self, form):
         if form.instance.application_name == PwaSettings.get_default_application_name():
             form.instance.application_name = ''
-        return super(ManagerHomeView, self).form_valid(form)
+        return super().form_valid(form)
 
     def get_object(self):
         return PwaSettings.singleton()
 
     def get_context_data(self, **kwargs):
-        context = super(ManagerHomeView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['navigation_entries'] = PwaNavigationEntry.objects.all()
         return context
 
 
-class ManagerNavigationEntryMixin(object):
+class ManagerNavigationEntryMixin:
     model = PwaNavigationEntry
     fields = [
         'label',
@@ -83,8 +83,8 @@ class ManagerNavigationEntryMixin(object):
         elif form.instance.url:
             form.add_error('url', _('An URL cannot be specified when a page is selected.'))
         if form.errors:
-            return super(ManagerNavigationEntryMixin, self).form_invalid(form)
-        return super(ManagerNavigationEntryMixin, self).form_valid(form)
+            return super().form_invalid(form)
+        return super().form_valid(form)
 
 
 class ManagerAddNavigationEntry(ManagerNavigationEntryMixin, CreateView):
diff --git a/combo/apps/pwa/migrations/0001_initial.py b/combo/apps/pwa/migrations/0001_initial.py
index 328f9af4..78645bfa 100644
--- a/combo/apps/pwa/migrations/0001_initial.py
+++ b/combo/apps/pwa/migrations/0001_initial.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2018-11-29 09:45
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.conf import settings
diff --git a/combo/apps/pwa/migrations/0002_pwasettings.py b/combo/apps/pwa/migrations/0002_pwasettings.py
index 77de5200..1bd2a4a5 100644
--- a/combo/apps/pwa/migrations/0002_pwasettings.py
+++ b/combo/apps/pwa/migrations/0002_pwasettings.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2018-12-27 08:44
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/pwa/migrations/0003_pwanavigationentry.py b/combo/apps/pwa/migrations/0003_pwanavigationentry.py
index 900fd592..2158b212 100644
--- a/combo/apps/pwa/migrations/0003_pwanavigationentry.py
+++ b/combo/apps/pwa/migrations/0003_pwanavigationentry.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2018-12-27 14:27
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.db import migrations, models
diff --git a/combo/apps/pwa/migrations/0004_pwasettings_application_icon.py b/combo/apps/pwa/migrations/0004_pwasettings_application_icon.py
index 44f2ba42..9190ad3d 100644
--- a/combo/apps/pwa/migrations/0004_pwasettings_application_icon.py
+++ b/combo/apps/pwa/migrations/0004_pwasettings_application_icon.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2019-01-04 19:36
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/pwa/migrations/0005_pwasettings_application_name.py b/combo/apps/pwa/migrations/0005_pwasettings_application_name.py
index db848619..01e41336 100644
--- a/combo/apps/pwa/migrations/0005_pwasettings_application_name.py
+++ b/combo/apps/pwa/migrations/0005_pwasettings_application_name.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.20 on 2019-04-16 14:08
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/pwa/migrations/0006_auto_20190628_1042.py b/combo/apps/pwa/migrations/0006_auto_20190628_1042.py
index d3aecb28..345c6571 100644
--- a/combo/apps/pwa/migrations/0006_auto_20190628_1042.py
+++ b/combo/apps/pwa/migrations/0006_auto_20190628_1042.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2019-06-28 08:42
-from __future__ import unicode_literals
 
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
diff --git a/combo/apps/pwa/models.py b/combo/apps/pwa/models.py
index 5658e19c..ba450982 100644
--- a/combo/apps/pwa/models.py
+++ b/combo/apps/pwa/models.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*
 #
 # combo - content management system
 # Copyright (C) 2015-2018  Entr'ouvert
@@ -42,7 +41,7 @@ class PwaSettings(models.Model):
     application_icon = models.FileField(
         verbose_name=_('Application Icon'),
         help_text=_(
-            u'Icon file must be in JPEG or PNG format, and should be a square of at least 512×512 pixels.'
+            'Icon file must be in JPEG or PNG format, and should be a square of at least 512×512 pixels.'
         ),
         upload_to='pwa',
         blank=True,
@@ -70,7 +69,7 @@ class PwaSettings(models.Model):
             }
         elif not self.push_notifications:
             self.push_notifications_infos = {}
-        return super(PwaSettings, self).save(**kwargs)
+        return super().save(**kwargs)
 
     @classmethod
     def singleton(cls):
diff --git a/combo/apps/search/engines.py b/combo/apps/search/engines.py
index 8dd40e74..960668ab 100644
--- a/combo/apps/search/engines.py
+++ b/combo/apps/search/engines.py
@@ -17,7 +17,7 @@
 from django.conf import settings
 
 
-class Engines(object):
+class Engines:
     """Singleton object that serves as a registry of search engines."""
 
     def __init__(self):
diff --git a/combo/apps/search/forms.py b/combo/apps/search/forms.py
index 67737c98..44735c67 100644
--- a/combo/apps/search/forms.py
+++ b/combo/apps/search/forms.py
@@ -86,7 +86,7 @@ class TextEngineSettingsForm(TextEngineSettingsUpdateForm):
         pages_queryset = Page.objects.filter(snapshot__isnull=True, sub_slug='').order_by('title')
         pages = Page.get_as_reordered_flat_hierarchy(pages_queryset)
         pages_choices = [('', '---------')] + [
-            (x.id, {'label': '%s %s' % (u'\u00a0' * x.level * 2, x.title), 'disabled': x.slug in used_slugs})
+            (x.id, {'label': '%s %s' % ('\u00a0' * x.level * 2, x.title), 'disabled': x.slug in used_slugs})
             for x in pages
         ]
         # if '_text' without page is already selected, page is required
diff --git a/combo/apps/search/migrations/0001_initial.py b/combo/apps/search/migrations/0001_initial.py
index 7ba218f0..07d2cffd 100644
--- a/combo/apps/search/migrations/0001_initial.py
+++ b/combo/apps/search/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/search/migrations/0002_auto_20180720_1511.py b/combo/apps/search/migrations/0002_auto_20180720_1511.py
index c9be4d99..1f89aa1c 100644
--- a/combo/apps/search/migrations/0002_auto_20180720_1511.py
+++ b/combo/apps/search/migrations/0002_auto_20180720_1511.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations
 
diff --git a/combo/apps/search/migrations/0003_create_search_services.py b/combo/apps/search/migrations/0003_create_search_services.py
index 66eb545b..b58c13f1 100644
--- a/combo/apps/search/migrations/0003_create_search_services.py
+++ b/combo/apps/search/migrations/0003_create_search_services.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations
 
 
diff --git a/combo/apps/search/migrations/0004_remove_searchcell__search_service.py b/combo/apps/search/migrations/0004_remove_searchcell__search_service.py
index 22852d59..64baa5f8 100644
--- a/combo/apps/search/migrations/0004_remove_searchcell__search_service.py
+++ b/combo/apps/search/migrations/0004_remove_searchcell__search_service.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/search/migrations/0005_searchcell_autofocus.py b/combo/apps/search/migrations/0005_searchcell_autofocus.py
index 2980904e..48b9d5f7 100644
--- a/combo/apps/search/migrations/0005_searchcell_autofocus.py
+++ b/combo/apps/search/migrations/0005_searchcell_autofocus.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2019-11-20 13:06
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/search/migrations/0006_indexedcell.py b/combo/apps/search/migrations/0006_indexedcell.py
index 39f73e11..9f9e07be 100644
--- a/combo/apps/search/migrations/0006_indexedcell.py
+++ b/combo/apps/search/migrations/0006_indexedcell.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.17 on 2020-01-20 15:30
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.db import migrations, models
diff --git a/combo/apps/search/migrations/0007_french_fts.py b/combo/apps/search/migrations/0007_french_fts.py
index 57860735..0d1b0cee 100644
--- a/combo/apps/search/migrations/0007_french_fts.py
+++ b/combo/apps/search/migrations/0007_french_fts.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.17 on 2020-02-19 08:20
-from __future__ import unicode_literals
 
 from django.db import migrations, transaction
 from django.db.migrations.operations.base import Operation
diff --git a/combo/apps/search/migrations/0008_searchcell_input_placeholder.py b/combo/apps/search/migrations/0008_searchcell_input_placeholder.py
index f03aca13..fb98679a 100644
--- a/combo/apps/search/migrations/0008_searchcell_input_placeholder.py
+++ b/combo/apps/search/migrations/0008_searchcell_input_placeholder.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2020-04-08 17:22
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/apps/search/models.py b/combo/apps/search/models.py
index 0542d5d7..0fae2505 100644
--- a/combo/apps/search/models.py
+++ b/combo/apps/search/models.py
@@ -62,7 +62,7 @@ class SearchCell(CellBase):
     def is_visible(self, **kwargs):
         if not self._search_services.get('data'):
             return False
-        return super(SearchCell, self).is_visible(**kwargs)
+        return super().is_visible(**kwargs)
 
     def get_default_form_class(self):
         from .forms import SearchCellForm
@@ -156,7 +156,7 @@ class SearchCell(CellBase):
             context[self.varname] = request.GET.get(self.varname)
 
     def get_cell_extra_context(self, context):
-        extra_context = super(SearchCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         # if there is a q_<slug> in query_string, send it to the template (to be
         # used as an initial query) and remove it from query_string
         initial_q = None
diff --git a/combo/apps/wcs/context_processors.py b/combo/apps/wcs/context_processors.py
index ac979994..b4457ae1 100644
--- a/combo/apps/wcs/context_processors.py
+++ b/combo/apps/wcs/context_processors.py
@@ -32,7 +32,7 @@ def get_default_wcs_service_key():
     return None
 
 
-class LazyCardDefObjectsManager(object):
+class LazyCardDefObjectsManager:
     def __init__(self, service_key, card_id, custom_view_id=None, filters=None, user=Ellipsis):
         self._service_key = service_key
         self._card_id = card_id
@@ -147,14 +147,13 @@ class LazyCardDefObjectsManager(object):
 
     def __iter__(self):
         self._populate_cache()
-        for data in self._cached_resultset:
-            yield data
+        yield from self._cached_resultset
 
     def __nonzero__(self):
         return any(self)
 
 
-class LazyCardDef(object):
+class LazyCardDef:
     def __init__(self, slug):
         if ':' in slug:
             self.service_key, self.card_id = slug.split(':')[:2]
@@ -167,7 +166,7 @@ class LazyCardDef(object):
         return LazyCardDefObjectsManager(self.service_key, self.card_id)
 
 
-class Cards(object):
+class Cards:
     def __getattr__(self, attr):
         try:
             return LazyCardDef(attr)
diff --git a/combo/apps/wcs/forms.py b/combo/apps/wcs/forms.py
index cbf37210..6479f880 100644
--- a/combo/apps/wcs/forms.py
+++ b/combo/apps/wcs/forms.py
@@ -42,7 +42,7 @@ class WcsFormCellForm(forms.ModelForm):
         fields = ('formdef_reference',)
 
     def __init__(self, *args, **kwargs):
-        super(WcsFormCellForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         formdef_references = get_wcs_options('/api/formdefs/')
         self.fields['formdef_reference'].widget = forms.Select(choices=formdef_references)
 
@@ -108,7 +108,7 @@ class WcsCategoryCellForm(forms.ModelForm):
         fields = ('category_reference',)
 
     def __init__(self, *args, **kwargs):
-        super(WcsCategoryCellForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         references = get_wcs_options('/api/categories/')
         self.fields['category_reference'].widget = forms.Select(choices=references)
 
@@ -119,7 +119,7 @@ class WcsFormsOfCategoryCellForm(forms.ModelForm):
         fields = ('category_reference', 'ordering', 'manual_order', 'limit')
 
     def __init__(self, *args, **kwargs):
-        super(WcsFormsOfCategoryCellForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         references = get_wcs_options('/api/categories/')
         formdef_references = get_wcs_options('/api/formdefs/', include_category_slug=True)
         self.fields['ordering'].widget = forms.Select(
@@ -134,7 +134,7 @@ class WcsFormsOfCategoryCellForm(forms.ModelForm):
 class CategoriesSelectMultiple(forms.SelectMultiple):
     def format_value(self, value):
         value = json.loads(value)
-        return super(CategoriesSelectMultiple, self).format_value(value.get('data') or [])
+        return super().format_value(value.get('data') or [])
 
     def value_from_datadict(self, data, files, name):
         if isinstance(data, MultiValueDict):
@@ -142,7 +142,7 @@ class CategoriesSelectMultiple(forms.SelectMultiple):
         return data.get(name, None)
 
 
-class WcsFormsMixin(object):
+class WcsFormsMixin:
     def _init_wcs_site(self):
         if len(get_wcs_services()) == 1:
             self.fields['wcs_site'].widget = forms.HiddenInput()
diff --git a/combo/apps/wcs/migrations/0001_initial.py b/combo/apps/wcs/migrations/0001_initial.py
index e87623b1..5e0a9716 100644
--- a/combo/apps/wcs/migrations/0001_initial.py
+++ b/combo/apps/wcs/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 import combo.apps.wcs.models
diff --git a/combo/apps/wcs/migrations/0002_auto_20150217_1402.py b/combo/apps/wcs/migrations/0002_auto_20150217_1402.py
index 5705de54..d85af87a 100644
--- a/combo/apps/wcs/migrations/0002_auto_20150217_1402.py
+++ b/combo/apps/wcs/migrations/0002_auto_20150217_1402.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0003_auto_20150218_1151.py b/combo/apps/wcs/migrations/0003_auto_20150218_1151.py
index 4180a8ea..ea506a28 100644
--- a/combo/apps/wcs/migrations/0003_auto_20150218_1151.py
+++ b/combo/apps/wcs/migrations/0003_auto_20150218_1151.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0004_auto_20150218_1154.py b/combo/apps/wcs/migrations/0004_auto_20150218_1154.py
index b57337bf..8f787a07 100644
--- a/combo/apps/wcs/migrations/0004_auto_20150218_1154.py
+++ b/combo/apps/wcs/migrations/0004_auto_20150218_1154.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0005_auto_20150401_0758.py b/combo/apps/wcs/migrations/0005_auto_20150401_0758.py
index b558fb69..a9d0411a 100644
--- a/combo/apps/wcs/migrations/0005_auto_20150401_0758.py
+++ b/combo/apps/wcs/migrations/0005_auto_20150401_0758.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0006_categoriescell.py b/combo/apps/wcs/migrations/0006_categoriescell.py
index 5b3d98a8..791c0845 100644
--- a/combo/apps/wcs/migrations/0006_categoriescell.py
+++ b/combo/apps/wcs/migrations/0006_categoriescell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 import combo.apps.wcs.models
diff --git a/combo/apps/wcs/migrations/0007_trackingcodeinputcell.py b/combo/apps/wcs/migrations/0007_trackingcodeinputcell.py
index 8331c304..cd17ca35 100644
--- a/combo/apps/wcs/migrations/0007_trackingcodeinputcell.py
+++ b/combo/apps/wcs/migrations/0007_trackingcodeinputcell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0008_wcsformcell_cached_json.py b/combo/apps/wcs/migrations/0008_wcsformcell_cached_json.py
index dc97fcf7..8c15badb 100644
--- a/combo/apps/wcs/migrations/0008_wcsformcell_cached_json.py
+++ b/combo/apps/wcs/migrations/0008_wcsformcell_cached_json.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
 
diff --git a/combo/apps/wcs/migrations/0009_remove_wcscategorycell_link_page.py b/combo/apps/wcs/migrations/0009_remove_wcscategorycell_link_page.py
index 65b4b1b9..71c64973 100644
--- a/combo/apps/wcs/migrations/0009_remove_wcscategorycell_link_page.py
+++ b/combo/apps/wcs/migrations/0009_remove_wcscategorycell_link_page.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0010_auto_20151029_1535.py b/combo/apps/wcs/migrations/0010_auto_20151029_1535.py
index 79db827e..c6cc70ef 100644
--- a/combo/apps/wcs/migrations/0010_auto_20151029_1535.py
+++ b/combo/apps/wcs/migrations/0010_auto_20151029_1535.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0011_auto_20151215_1121.py b/combo/apps/wcs/migrations/0011_auto_20151215_1121.py
index e2cafcca..a78fb8a1 100644
--- a/combo/apps/wcs/migrations/0011_auto_20151215_1121.py
+++ b/combo/apps/wcs/migrations/0011_auto_20151215_1121.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0012_wcsformsofcategorycell_manual_order.py b/combo/apps/wcs/migrations/0012_wcsformsofcategorycell_manual_order.py
index 6c539535..7e976009 100644
--- a/combo/apps/wcs/migrations/0012_wcsformsofcategorycell_manual_order.py
+++ b/combo/apps/wcs/migrations/0012_wcsformsofcategorycell_manual_order.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
 
diff --git a/combo/apps/wcs/migrations/0013_auto_20160928_1152.py b/combo/apps/wcs/migrations/0013_auto_20160928_1152.py
index f6a3d1ce..2e339fac 100644
--- a/combo/apps/wcs/migrations/0013_auto_20160928_1152.py
+++ b/combo/apps/wcs/migrations/0013_auto_20160928_1152.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0014_auto_20170214_2006.py b/combo/apps/wcs/migrations/0014_auto_20170214_2006.py
index 7b351f02..82bbc2b6 100644
--- a/combo/apps/wcs/migrations/0014_auto_20170214_2006.py
+++ b/combo/apps/wcs/migrations/0014_auto_20170214_2006.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import datetime
 
 from django.db import migrations, models
diff --git a/combo/apps/wcs/migrations/0015_wcscurrentformscell_categories.py b/combo/apps/wcs/migrations/0015_wcscurrentformscell_categories.py
index 818631e8..2c1d9429 100644
--- a/combo/apps/wcs/migrations/0015_wcscurrentformscell_categories.py
+++ b/combo/apps/wcs/migrations/0015_wcscurrentformscell_categories.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.8 on 2018-01-21 12:50
-from __future__ import unicode_literals
 
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations
diff --git a/combo/apps/wcs/migrations/0016_backofficesubmissioncell.py b/combo/apps/wcs/migrations/0016_backofficesubmissioncell.py
index 66c5c32b..d6a5e445 100644
--- a/combo/apps/wcs/migrations/0016_backofficesubmissioncell.py
+++ b/combo/apps/wcs/migrations/0016_backofficesubmissioncell.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2018-08-10 08:05
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.db import migrations, models
diff --git a/combo/apps/wcs/migrations/0017_wcscareformscell.py b/combo/apps/wcs/migrations/0017_wcscareformscell.py
index 3be36c0d..357e1dd5 100644
--- a/combo/apps/wcs/migrations/0017_wcscareformscell.py
+++ b/combo/apps/wcs/migrations/0017_wcscareformscell.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2019-10-18 13:48
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.db import migrations, models
diff --git a/combo/apps/wcs/migrations/0018_wcscurrentdraftscell_categories.py b/combo/apps/wcs/migrations/0018_wcscurrentdraftscell_categories.py
index 9ebd2376..ca27dcb2 100644
--- a/combo/apps/wcs/migrations/0018_wcscurrentdraftscell_categories.py
+++ b/combo/apps/wcs/migrations/0018_wcscurrentdraftscell_categories.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations
 
diff --git a/combo/apps/wcs/migrations/0019_wcscurrentformscell_include_drafts.py b/combo/apps/wcs/migrations/0019_wcscurrentformscell_include_drafts.py
index cc1ad161..dfa90918 100644
--- a/combo/apps/wcs/migrations/0019_wcscurrentformscell_include_drafts.py
+++ b/combo/apps/wcs/migrations/0019_wcscurrentformscell_include_drafts.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0020_cards.py b/combo/apps/wcs/migrations/0020_cards.py
index d88340d2..7a74be6b 100644
--- a/combo/apps/wcs/migrations/0020_cards.py
+++ b/combo/apps/wcs/migrations/0020_cards.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.db.models.deletion
 from django.db import migrations, models
 
diff --git a/combo/apps/wcs/migrations/0021_card.py b/combo/apps/wcs/migrations/0021_card.py
index 73773be6..a57d66c9 100644
--- a/combo/apps/wcs/migrations/0021_card.py
+++ b/combo/apps/wcs/migrations/0021_card.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.db.models.deletion
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
diff --git a/combo/apps/wcs/migrations/0022_cards_user.py b/combo/apps/wcs/migrations/0022_cards_user.py
index 1ad150d0..a6e04976 100644
--- a/combo/apps/wcs/migrations/0022_cards_user.py
+++ b/combo/apps/wcs/migrations/0022_cards_user.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0023_card_info_id.py b/combo/apps/wcs/migrations/0023_card_info_id.py
index 146373a0..53d5ae66 100644
--- a/combo/apps/wcs/migrations/0023_card_info_id.py
+++ b/combo/apps/wcs/migrations/0023_card_info_id.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0024_card_info_user.py b/combo/apps/wcs/migrations/0024_card_info_user.py
index a7330ff0..5b9afa42 100644
--- a/combo/apps/wcs/migrations/0024_card_info_user.py
+++ b/combo/apps/wcs/migrations/0024_card_info_user.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0025_submission_categories.py b/combo/apps/wcs/migrations/0025_submission_categories.py
index ebc46d4e..16675856 100644
--- a/combo/apps/wcs/migrations/0025_submission_categories.py
+++ b/combo/apps/wcs/migrations/0025_submission_categories.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations
 
diff --git a/combo/apps/wcs/migrations/0028_wcscardscell_without_user.py b/combo/apps/wcs/migrations/0028_wcscardscell_without_user.py
index 554099a7..ec31d49a 100644
--- a/combo/apps/wcs/migrations/0028_wcscardscell_without_user.py
+++ b/combo/apps/wcs/migrations/0028_wcscardscell_without_user.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0029_cards_custom_title.py b/combo/apps/wcs/migrations/0029_cards_custom_title.py
index e3ca7798..8cb18825 100644
--- a/combo/apps/wcs/migrations/0029_cards_custom_title.py
+++ b/combo/apps/wcs/migrations/0029_cards_custom_title.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/apps/wcs/migrations/0032_forms_user_can_access.py b/combo/apps/wcs/migrations/0032_forms_user_can_access.py
index f1e2ccca..b6ad4aa9 100644
--- a/combo/apps/wcs/migrations/0032_forms_user_can_access.py
+++ b/combo/apps/wcs/migrations/0032_forms_user_can_access.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.contrib.postgres.fields.jsonb
 import django.db.models.deletion
 from django.db import migrations, models
diff --git a/combo/apps/wcs/models.py b/combo/apps/wcs/models.py
index a7877d5c..9bd312e6 100644
--- a/combo/apps/wcs/models.py
+++ b/combo/apps/wcs/models.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 #
 # combo - content management system
 # Copyright (C) 2014-2015  Entr'ouvert
@@ -124,7 +123,7 @@ class WcsFormCell(CellBase):
         populate_cache()
 
     def get_cell_extra_context(self, context):
-        context = super(WcsFormCell, self).get_cell_extra_context(context)
+        context = super().get_cell_extra_context(context)
         context['slug'] = self.formdef_reference.split(':')[-1]
         context['title'] = self.cached_title
         context['url'] = self.cached_url + 'tryauth'
@@ -262,7 +261,7 @@ class WcsCategoryCell(WcsCommonCategoryCell):
         return WcsCategoryCellForm
 
     def get_cell_extra_context(self, context):
-        context = super(WcsCategoryCell, self).get_cell_extra_context(context)
+        context = super().get_cell_extra_context(context)
         if not self.category_reference:
             return context
         context['slug'] = self.category_reference.split(':')[-1]
@@ -272,7 +271,7 @@ class WcsCategoryCell(WcsCommonCategoryCell):
         return context
 
 
-class WcsBlurpMixin(object):
+class WcsBlurpMixin:
     is_enabled = classmethod(is_wcs_enabled)
     cache_duration = 5
     api_url = None
@@ -299,7 +298,7 @@ class WcsBlurpMixin(object):
             wcs_sites = get_wcs_services()
 
         result = {}
-        returns = set([])
+        returns = set()
         for wcs_slug, _wcs_site in wcs_sites.items():
             api_url = self.get_api_url_for_site(context, wcs_slug)
             if not api_url:
@@ -363,7 +362,7 @@ class WcsDataBaseCell(CellBase, WcsBlurpMixin):
         abstract = True
 
     def get_additional_label(self):
-        wcs_sites = dict([(x, y.get('title')) for x, y in get_wcs_services().items()])
+        wcs_sites = {x: y.get('title') for x, y in get_wcs_services().items()}
         if len(wcs_sites.keys()) < 2:
             return ''
         if self.wcs_site in wcs_sites:
@@ -390,7 +389,7 @@ class WcsDataBaseCell(CellBase, WcsBlurpMixin):
         )
 
     def get_cell_extra_context(self, context):
-        extra_context = super(WcsDataBaseCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         extra_context.update(WcsBlurpMixin.get_cell_extra_context(self, context))
         return extra_context
 
@@ -406,10 +405,10 @@ class WcsUserDataBaseCell(WcsDataBaseCell):
         user = kwargs.get('user')
         if not user or user.is_anonymous:
             return False
-        return super(WcsUserDataBaseCell, self).is_visible(**kwargs)
+        return super().is_visible(**kwargs)
 
 
-class CategoriesValidityMixin(object):
+class CategoriesValidityMixin:
     def check_validity(self):
         categories = self.categories.get('data', [])
 
@@ -435,7 +434,7 @@ class CategoriesValidityMixin(object):
         self.mark_as_valid()
 
 
-class CategoriesFilteringMixin(object):
+class CategoriesFilteringMixin:
     def get_api_url_for_site(self, context, wcs_slug):
         url = self.get_api_url(context)
 
@@ -491,7 +490,7 @@ class WcsCurrentFormsCell(CategoriesValidityMixin, CategoriesFilteringMixin, Wcs
         return 'combo/wcs/current_forms.html'
 
     def get_additional_label(self):
-        initial_label = super(WcsCurrentFormsCell, self).get_additional_label()
+        initial_label = super().get_additional_label()
         if self.include_drafts:
             if self.current_forms and self.done_forms:
                 label = _('All Forms and Drafts')
@@ -511,7 +510,7 @@ class WcsCurrentFormsCell(CategoriesValidityMixin, CategoriesFilteringMixin, Wcs
         return label
 
     def get_cell_extra_context(self, context):
-        context = super(WcsCurrentFormsCell, self).get_cell_extra_context(context)
+        context = super().get_cell_extra_context(context)
 
         for wcs_site in context['user_forms']:
             if not context['user_forms'].get(wcs_site):
@@ -568,7 +567,7 @@ class WcsCurrentDraftsCell(CategoriesValidityMixin, CategoriesFilteringMixin, Wc
         return '/api/user/drafts'
 
     def get_cell_extra_context(self, context):
-        context = super(WcsCurrentDraftsCell, self).get_cell_extra_context(context)
+        context = super().get_cell_extra_context(context)
 
         for wcs_site in context['current_drafts']:
             if not context['current_drafts'].get(wcs_site):
@@ -658,7 +657,7 @@ class WcsFormsOfCategoryCell(WcsCommonCategoryCell, WcsBlurpMixin):
         self.mark_as_valid()
 
     def get_cell_extra_context(self, context):
-        extra_context = super(WcsFormsOfCategoryCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         extra_context.update(WcsBlurpMixin.get_cell_extra_context(self, context))
         if not self.category_reference:
             return extra_context
@@ -791,7 +790,7 @@ class CategoriesCell(WcsDataBaseCell):
         verbose_name = _('Form Categories')
 
 
-class CardMixin(object):
+class CardMixin:
     invalid_reason_codes = invalid_reason_codes
 
     def is_relevant(self, context):
@@ -1062,7 +1061,7 @@ class TrackingCodeInputCell(CellBase):
         )
 
     def get_cell_extra_context(self, context):
-        extra_context = super(TrackingCodeInputCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         if not self.wcs_site:
             self.wcs_site = list(get_wcs_services().keys())[0]
         extra_context['url'] = get_wcs_services().get(self.wcs_site).get('url')
@@ -1094,7 +1093,7 @@ class BackofficeSubmissionCell(CategoriesValidityMixin, CategoriesFilteringMixin
         return BackofficeSubmissionCellForm
 
     def get_cell_extra_context(self, context):
-        context = super(BackofficeSubmissionCell, self).get_cell_extra_context(context)
+        context = super().get_cell_extra_context(context)
         all_formdefs = context.pop('all_formdefs')
         formdefs = {}
 
diff --git a/combo/apps/wcs/views.py b/combo/apps/wcs/views.py
index 2b062547..570bf1d3 100644
--- a/combo/apps/wcs/views.py
+++ b/combo/apps/wcs/views.py
@@ -42,7 +42,7 @@ class TrackingCodeView(View):
     def dispatch(self, *args, **kwargs):
         # CSRF check must be disabled as the cell may be distributed to other
         # sites in a skeleton.
-        return super(TrackingCodeView, self).dispatch(*args, **kwargs)
+        return super().dispatch(*args, **kwargs)
 
     @classmethod
     def search(self, code, request, wcs_site=None):
@@ -92,7 +92,7 @@ class TrackingCodeView(View):
             if redirect_to_other_domain:
                 raise
             else:
-                messages.error(self.request, _(u'Looking up tracking code is currently rate limited.'))
+                messages.error(self.request, _('Looking up tracking code is currently rate limited.'))
         else:
             if url:
                 return HttpResponseRedirect(url)
@@ -103,7 +103,7 @@ class TrackingCodeView(View):
                     next_url += '?'
                 next_url += 'unknown-tracking-code'
             else:
-                messages.error(self.request, _(u'The tracking code could not been found.'))
+                messages.error(self.request, _('The tracking code could not been found.'))
 
         return HttpResponseRedirect(next_url)
 
diff --git a/combo/data/fields.py b/combo/data/fields.py
index 05dd5c46..7c3330a1 100644
--- a/combo/data/fields.py
+++ b/combo/data/fields.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 #
 # combo - content management system
 # Copyright (C) 2015-2017  Entr'ouvert
@@ -33,22 +32,22 @@ class RichTextField(ckeditor.fields.RichTextField):
             'external_plugin_resources': self.external_plugin_resources,
         }
         defaults.update(kwargs)
-        return super(RichTextField, self).formfield(**defaults)
+        return super().formfield(**defaults)
 
 
 class RichTextFormField(ckeditor.fields.RichTextFormField):
     def clean(self, value):
-        value = super(RichTextFormField, self).clean(value)
+        value = super().clean(value)
         if settings.LANGUAGE_CODE.startswith('fr-'):
             # apply some typographic rules
-            value = value.replace(u'&laquo; ', u'«\u202f')
-            value = value.replace(u'« ', u'«\u202f')
-            value = value.replace(u' &raquo;', u'\u202f»')
-            value = value.replace(u' »', u'\u202f»')
-            value = value.replace(u' :', u'\u00a0:')
-            value = value.replace(u' ;', u'\u202f;')
-            value = value.replace(u' !', u'\u202f!')
-            value = value.replace(u' ?', u'\u202f?')
+            value = value.replace('&laquo; ', '«\u202f')
+            value = value.replace('« ', '«\u202f')
+            value = value.replace(' &raquo;', '\u202f»')
+            value = value.replace(' »', '\u202f»')
+            value = value.replace(' :', '\u00a0:')
+            value = value.replace(' ;', '\u202f;')
+            value = value.replace(' !', '\u202f!')
+            value = value.replace(' ?', '\u202f?')
         return value
 
 
@@ -68,4 +67,4 @@ class TemplatableURLField(forms.URLField):
         value = super(forms.URLField, self).to_python(value)
         if '{{' in value or '{%' in value:
             return value
-        return super(TemplatableURLField, self).to_python(value)
+        return super().to_python(value)
diff --git a/combo/data/forms.py b/combo/data/forms.py
index a8d6336f..833ffdd5 100644
--- a/combo/data/forms.py
+++ b/combo/data/forms.py
@@ -26,7 +26,7 @@ from .models import ConfigJsonCell, LinkCell, LinkListCell, MenuCell, Page
 @cache_during_request
 def get_page_choices():
     pages = Page.get_as_reordered_flat_hierarchy(Page.objects.all())
-    return [(x.id, '%s %s' % (u'\u00a0' * x.level * 2, x.title)) for x in pages]
+    return [(x.id, '%s %s' % ('\u00a0' * x.level * 2, x.title)) for x in pages]
 
 
 class MenuCellForm(forms.ModelForm):
@@ -35,7 +35,7 @@ class MenuCellForm(forms.ModelForm):
         fields = ('depth', 'initial_level', 'root_page')
 
     def __init__(self, *args, **kwargs):
-        super(MenuCellForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.fields['root_page'].widget = forms.Select(choices=get_page_choices())
 
 
@@ -45,7 +45,7 @@ class LinkCellForm(forms.ModelForm):
         fields = ('title', 'url', 'link_page', 'anchor')
 
     def __init__(self, *args, **kwargs):
-        super(LinkCellForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.fields['link_page'].widget = forms.Select(choices=[(None, '-----')] + get_page_choices())
 
 
@@ -55,7 +55,7 @@ class LinkListCellForm(forms.ModelForm):
         fields = ['title', 'limit']
 
     def __init__(self, *args, **kwargs):
-        super(LinkListCellForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
 
 class ConfigJsonForm(forms.ModelForm):
@@ -71,7 +71,7 @@ class ConfigJsonForm(forms.ModelForm):
         # reset parameters in instance as the value is actually created from
         # additional fields.
         kwargs['instance'].parameters = None
-        super(ConfigJsonForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
         field_classes = {
             'string': forms.CharField,
diff --git a/combo/data/library.py b/combo/data/library.py
index b8caa18a..c43c985b 100644
--- a/combo/data/library.py
+++ b/combo/data/library.py
@@ -17,7 +17,7 @@
 from collections import OrderedDict
 
 
-class Library(object):
+class Library:
     """Singleton object that serves as a registry of the classes
     providing page cells."""
 
diff --git a/combo/data/management/commands/cron.py b/combo/data/management/commands/cron.py
index c13c38fd..de2c827f 100644
--- a/combo/data/management/commands/cron.py
+++ b/combo/data/management/commands/cron.py
@@ -14,7 +14,6 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from __future__ import print_function
 
 import sys
 import traceback
diff --git a/combo/data/management/commands/export_site.py b/combo/data/management/commands/export_site.py
index 14d78516..e7ac236f 100644
--- a/combo/data/management/commands/export_site.py
+++ b/combo/data/management/commands/export_site.py
@@ -45,7 +45,7 @@ class Command(BaseCommand):
             if options['output'] and options['output'] != '-':
                 try:
                     output = open(options['output'], 'wb')
-                except IOError as e:
+                except OSError as e:
                     raise CommandError(e)
                 export_site_tar(output)
                 output.close()
diff --git a/combo/data/management/commands/import_site.py b/combo/data/management/commands/import_site.py
index ffce55e6..0603277d 100644
--- a/combo/data/management/commands/import_site.py
+++ b/combo/data/management/commands/import_site.py
@@ -43,13 +43,13 @@ class Command(BaseCommand):
             else:
                 try:
                     fd = stack.enter_context(open(filename, 'rb'))
-                except IOError as e:
+                except OSError as e:
                     raise CommandError(e)
                 try:
                     tarfile.open(mode='r', fileobj=fd)
                 except tarfile.TarError as e:
                     format = 'json'
-                    fd = open(filename, 'r')
+                    fd = open(filename)
                 else:
                     format = 'tar'
                     fd = open(filename, 'rb')
diff --git a/combo/data/migrations/0001_initial.py b/combo/data/migrations/0001_initial.py
index 1a68034b..57374b61 100644
--- a/combo/data/migrations/0001_initial.py
+++ b/combo/data/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import ckeditor.fields
 from django.db import migrations, models
 
diff --git a/combo/data/migrations/0002_auto_20150217_1327.py b/combo/data/migrations/0002_auto_20150217_1327.py
index a8c60aef..7b5fde82 100644
--- a/combo/data/migrations/0002_auto_20150217_1327.py
+++ b/combo/data/migrations/0002_auto_20150217_1327.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0003_menucell.py b/combo/data/migrations/0003_menucell.py
index a110d2b1..426c8ce7 100644
--- a/combo/data/migrations/0003_menucell.py
+++ b/combo/data/migrations/0003_menucell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0004_menucell_depth.py b/combo/data/migrations/0004_menucell_depth.py
index e68d9a3a..fb92d0c6 100644
--- a/combo/data/migrations/0004_menucell_depth.py
+++ b/combo/data/migrations/0004_menucell_depth.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0005_auto_20150226_0903.py b/combo/data/migrations/0005_auto_20150226_0903.py
index d60a8467..c73dbf9c 100644
--- a/combo/data/migrations/0005_auto_20150226_0903.py
+++ b/combo/data/migrations/0005_auto_20150226_0903.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import ckeditor.fields
 from django.db import migrations, models
 
diff --git a/combo/data/migrations/0006_linkcell.py b/combo/data/migrations/0006_linkcell.py
index 3769ac43..83fc008c 100644
--- a/combo/data/migrations/0006_linkcell.py
+++ b/combo/data/migrations/0006_linkcell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0007_auto_20150529_1003.py b/combo/data/migrations/0007_auto_20150529_1003.py
index 3f34d355..a9328e58 100644
--- a/combo/data/migrations/0007_auto_20150529_1003.py
+++ b/combo/data/migrations/0007_auto_20150529_1003.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0008_auto_20150529_1504.py b/combo/data/migrations/0008_auto_20150529_1504.py
index 691cef40..f2ad0996 100644
--- a/combo/data/migrations/0008_auto_20150529_1504.py
+++ b/combo/data/migrations/0008_auto_20150529_1504.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0009_auto_20150529_2247.py b/combo/data/migrations/0009_auto_20150529_2247.py
index 7f93411b..cd8f3019 100644
--- a/combo/data/migrations/0009_auto_20150529_2247.py
+++ b/combo/data/migrations/0009_auto_20150529_2247.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0010_feedcell.py b/combo/data/migrations/0010_feedcell.py
index 93dac910..48830be6 100644
--- a/combo/data/migrations/0010_feedcell.py
+++ b/combo/data/migrations/0010_feedcell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0011_auto_20151028_1047.py b/combo/data/migrations/0011_auto_20151028_1047.py
index 4ac7fde8..fa2be9e3 100644
--- a/combo/data/migrations/0011_auto_20151028_1047.py
+++ b/combo/data/migrations/0011_auto_20151028_1047.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0012_auto_20151029_1535.py b/combo/data/migrations/0012_auto_20151029_1535.py
index d6bbf189..2fcd2a2d 100644
--- a/combo/data/migrations/0012_auto_20151029_1535.py
+++ b/combo/data/migrations/0012_auto_20151029_1535.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0013_parameterscell.py b/combo/data/migrations/0013_parameterscell.py
index 6832c68f..d7c5e2f2 100644
--- a/combo/data/migrations/0013_parameterscell.py
+++ b/combo/data/migrations/0013_parameterscell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
 
diff --git a/combo/data/migrations/0014_menucell_initial_level.py b/combo/data/migrations/0014_menucell_initial_level.py
index 292f2a84..1f9b329c 100644
--- a/combo/data/migrations/0014_menucell_initial_level.py
+++ b/combo/data/migrations/0014_menucell_initial_level.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0015_feedcell_title.py b/combo/data/migrations/0015_feedcell_title.py
index 8e3cab94..6b804224 100644
--- a/combo/data/migrations/0015_feedcell_title.py
+++ b/combo/data/migrations/0015_feedcell_title.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0016_feedcell_limit.py b/combo/data/migrations/0016_feedcell_limit.py
index 9d224763..86cddbc6 100644
--- a/combo/data/migrations/0016_feedcell_limit.py
+++ b/combo/data/migrations/0016_feedcell_limit.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0017_menucell_root_page.py b/combo/data/migrations/0017_menucell_root_page.py
index af00ab6d..55bfec93 100644
--- a/combo/data/migrations/0017_menucell_root_page.py
+++ b/combo/data/migrations/0017_menucell_root_page.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0018_parentcontentcell.py b/combo/data/migrations/0018_parentcontentcell.py
index c1167905..59579594 100644
--- a/combo/data/migrations/0018_parentcontentcell.py
+++ b/combo/data/migrations/0018_parentcontentcell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0019_create_parent_cells.py b/combo/data/migrations/0019_create_parent_cells.py
index 0cf1d944..d104ebcd 100644
--- a/combo/data/migrations/0019_create_parent_cells.py
+++ b/combo/data/migrations/0019_create_parent_cells.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.db import migrations, models
 
diff --git a/combo/data/migrations/0020_auto_20160928_1152.py b/combo/data/migrations/0020_auto_20160928_1152.py
index 8da9b10b..6363f15e 100644
--- a/combo/data/migrations/0020_auto_20160928_1152.py
+++ b/combo/data/migrations/0020_auto_20160928_1152.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0021_jsoncell.py b/combo/data/migrations/0021_jsoncell.py
index 8a9ee194..f19c2f6e 100644
--- a/combo/data/migrations/0021_jsoncell.py
+++ b/combo/data/migrations/0021_jsoncell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 import combo.data.models
diff --git a/combo/data/migrations/0022_auto_20170214_2006.py b/combo/data/migrations/0022_auto_20170214_2006.py
index eef46909..1090607b 100644
--- a/combo/data/migrations/0022_auto_20170214_2006.py
+++ b/combo/data/migrations/0022_auto_20170214_2006.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import datetime
 
 from django.db import migrations, models
diff --git a/combo/data/migrations/0023_auto_20170313_1541.py b/combo/data/migrations/0023_auto_20170313_1541.py
index c7a7f078..607b77f6 100644
--- a/combo/data/migrations/0023_auto_20170313_1541.py
+++ b/combo/data/migrations/0023_auto_20170313_1541.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0024_configjsoncell.py b/combo/data/migrations/0024_configjsoncell.py
index 330f6132..a7847d4e 100644
--- a/combo/data/migrations/0024_configjsoncell.py
+++ b/combo/data/migrations/0024_configjsoncell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations, models
 
diff --git a/combo/data/migrations/0025_jsoncell_varnames_str.py b/combo/data/migrations/0025_jsoncell_varnames_str.py
index e916e801..8daf24ee 100644
--- a/combo/data/migrations/0025_jsoncell_varnames_str.py
+++ b/combo/data/migrations/0025_jsoncell_varnames_str.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 import combo.data.fields
diff --git a/combo/data/migrations/0026_jsoncell_force_async.py b/combo/data/migrations/0026_jsoncell_force_async.py
index c2868147..95560ba4 100644
--- a/combo/data/migrations/0026_jsoncell_force_async.py
+++ b/combo/data/migrations/0026_jsoncell_force_async.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0027_page_picture.py b/combo/data/migrations/0027_page_picture.py
index 18cf2d85..d79f627f 100644
--- a/combo/data/migrations/0027_page_picture.py
+++ b/combo/data/migrations/0027_page_picture.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0028_jsoncell_timeout.py b/combo/data/migrations/0028_jsoncell_timeout.py
index 6e3435d2..f0a16d3e 100644
--- a/combo/data/migrations/0028_jsoncell_timeout.py
+++ b/combo/data/migrations/0028_jsoncell_timeout.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0029_auto_20171022_1242.py b/combo/data/migrations/0029_auto_20171022_1242.py
index bcb7dd07..52920228 100644
--- a/combo/data/migrations/0029_auto_20171022_1242.py
+++ b/combo/data/migrations/0029_auto_20171022_1242.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 import combo.data.models
diff --git a/combo/data/migrations/0030_externallinksearchitem.py b/combo/data/migrations/0030_externallinksearchitem.py
index ba9707ee..11108c06 100644
--- a/combo/data/migrations/0030_externallinksearchitem.py
+++ b/combo/data/migrations/0030_externallinksearchitem.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0031_remove_blurps.py b/combo/data/migrations/0031_remove_blurps.py
index d718f7ba..342537fd 100644
--- a/combo/data/migrations/0031_remove_blurps.py
+++ b/combo/data/migrations/0031_remove_blurps.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0032_page_description.py b/combo/data/migrations/0032_page_description.py
index a02cf083..7648d2d8 100644
--- a/combo/data/migrations/0032_page_description.py
+++ b/combo/data/migrations/0032_page_description.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0033_auto_20180401_1300.py b/combo/data/migrations/0033_auto_20180401_1300.py
index b74ce64d..2a5cbfc6 100644
--- a/combo/data/migrations/0033_auto_20180401_1300.py
+++ b/combo/data/migrations/0033_auto_20180401_1300.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.8 on 2018-04-01 13:00
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.conf import settings
diff --git a/combo/data/migrations/0034_redirect.py b/combo/data/migrations/0034_redirect.py
index e4dd394f..bf30ca48 100644
--- a/combo/data/migrations/0034_redirect.py
+++ b/combo/data/migrations/0034_redirect.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2018-07-13 06:40
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.db import migrations, models
diff --git a/combo/data/migrations/0035_page_related_cells.py b/combo/data/migrations/0035_page_related_cells.py
index 530ff26d..5cd9a558 100644
--- a/combo/data/migrations/0035_page_related_cells.py
+++ b/combo/data/migrations/0035_page_related_cells.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2018-06-02 11:03
-from __future__ import unicode_literals
 
 from django.contrib.postgres.fields import JSONField
 from django.db import migrations
diff --git a/combo/data/migrations/0036_page_sub_slug.py b/combo/data/migrations/0036_page_sub_slug.py
index 9b0ee25a..eb35da1e 100644
--- a/combo/data/migrations/0036_page_sub_slug.py
+++ b/combo/data/migrations/0036_page_sub_slug.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2018-08-08 18:30
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/data/migrations/0037_auto_20190701_2118.py b/combo/data/migrations/0037_auto_20190701_2118.py
index 3892788e..dc7e3e35 100644
--- a/combo/data/migrations/0037_auto_20190701_2118.py
+++ b/combo/data/migrations/0037_auto_20190701_2118.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2019-07-01 19:18
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/data/migrations/0038_increase_jsoncell_url_max_length.py b/combo/data/migrations/0038_increase_jsoncell_url_max_length.py
index 0d73241b..0c577de7 100644
--- a/combo/data/migrations/0038_increase_jsoncell_url_max_length.py
+++ b/combo/data/migrations/0038_increase_jsoncell_url_max_length.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.27 on 2020-01-08 10:14
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/data/migrations/0039_link_list_cell.py b/combo/data/migrations/0039_link_list_cell.py
index 51aa9f7b..ffc0068c 100644
--- a/combo/data/migrations/0039_link_list_cell.py
+++ b/combo/data/migrations/0039_link_list_cell.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.db.models.deletion
 from django.db import migrations, models
 
diff --git a/combo/data/migrations/0040_auto_20200119_1017.py b/combo/data/migrations/0040_auto_20200119_1017.py
index b1cf7492..fe2cdb74 100644
--- a/combo/data/migrations/0040_auto_20200119_1017.py
+++ b/combo/data/migrations/0040_auto_20200119_1017.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.17 on 2020-01-19 09:17
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/data/migrations/0041_auto_20200130_1619.py b/combo/data/migrations/0041_auto_20200130_1619.py
index 39a8d1a0..be13ca5c 100644
--- a/combo/data/migrations/0041_auto_20200130_1619.py
+++ b/combo/data/migrations/0041_auto_20200130_1619.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
diff --git a/combo/data/migrations/0042_page_creation_timestamp.py b/combo/data/migrations/0042_page_creation_timestamp.py
index d4f254a8..ee496e71 100644
--- a/combo/data/migrations/0042_page_creation_timestamp.py
+++ b/combo/data/migrations/0042_page_creation_timestamp.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2020-01-31 15:40
-from __future__ import unicode_literals
 
 import django.utils.timezone
 from django.db import migrations, models
diff --git a/combo/data/migrations/0043_delete_externallinksearchitem.py b/combo/data/migrations/0043_delete_externallinksearchitem.py
index 2f6c7718..20c3453c 100644
--- a/combo/data/migrations/0043_delete_externallinksearchitem.py
+++ b/combo/data/migrations/0043_delete_externallinksearchitem.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.17 on 2020-01-20 15:30
-from __future__ import unicode_literals
 
 from django.db import migrations
 
diff --git a/combo/data/migrations/0044_validity_info.py b/combo/data/migrations/0044_validity_info.py
index 6ee50906..d3b18506 100644
--- a/combo/data/migrations/0044_validity_info.py
+++ b/combo/data/migrations/0044_validity_info.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 import django.db.models.deletion
 from django.db import migrations, models
 
@@ -36,6 +33,6 @@ class Migration(migrations.Migration):
         ),
         migrations.AlterUniqueTogether(
             name='validityinfo',
-            unique_together=set([('content_type', 'object_id')]),
+            unique_together={('content_type', 'object_id')},
         ),
     ]
diff --git a/combo/data/migrations/0045_link_list_limit.py b/combo/data/migrations/0045_link_list_limit.py
index 67ffa69a..3031954a 100644
--- a/combo/data/migrations/0045_link_list_limit.py
+++ b/combo/data/migrations/0045_link_list_limit.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.18 on 2020-03-17 09:34
-from __future__ import unicode_literals
 
 from django.db import migrations, models
 
diff --git a/combo/data/models.py b/combo/data/models.py
index cd943961..7f451561 100644
--- a/combo/data/models.py
+++ b/combo/data/models.py
@@ -134,7 +134,7 @@ def extract_context_from_sub_slug(sub_slug, sub_url):
     return context
 
 
-class Placeholder(object):
+class Placeholder:
     def __init__(
         self, key, name=None, acquired=False, optional=False, render=True, cell=None, force_synchronous=False
     ):
@@ -157,14 +157,14 @@ class PageManager(models.Manager):
 
     def __init__(self, *args, **kwargs):
         self.snapshots = kwargs.pop('snapshots', False)
-        super(PageManager, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
     def get_by_natural_key(self, path):
         parts = [x for x in path.strip('/').split('/') if x] or ['index']
         return self.get(slug=parts[-1])
 
     def get_queryset(self):
-        queryset = super(PageManager, self).get_queryset()
+        queryset = super().get_queryset()
         if self.snapshots:
             return queryset.filter(snapshot__isnull=False)
         else:
@@ -251,7 +251,7 @@ class Page(models.Model):
             self.slug = slug
         if not self.template_name:
             self.template_name = settings.COMBO_DEFAULT_PUBLIC_TEMPLATE
-        return super(Page, self).save(*args, **kwargs)
+        return super().save(*args, **kwargs)
 
     def get_parents_and_self(self):
         pages = [self]
@@ -477,9 +477,7 @@ class Page(models.Model):
     @classmethod
     def load_serialized_page(cls, json_page, page=None, snapshot=None, request=None):
         json_page['model'] = 'data.page'
-        json_page['fields']['groups'] = [
-            [x] for x in json_page['fields']['groups'] if isinstance(x, six.string_types)
-        ]
+        json_page['fields']['groups'] = [[x] for x in json_page['fields']['groups'] if isinstance(x, str)]
         created = None
         if page is None:
             page, created = Page.objects.get_or_create(slug=json_page['fields']['slug'], snapshot=snapshot)
@@ -501,9 +499,7 @@ class Page(models.Model):
         page.object.snapshot = snapshot
         page.save()
         for cell in json_page.get('cells'):
-            cell['fields']['groups'] = [
-                [x] for x in cell['fields']['groups'] if isinstance(x, six.string_types)
-            ]
+            cell['fields']['groups'] = [[x] for x in cell['fields']['groups'] if isinstance(x, str)]
             cell['fields']['page'] = page.object.id
 
         # if there were cells, remove them
@@ -699,7 +695,7 @@ class CellMeta(MediaDefiningClass, ModelBase):
 
 
 @python_2_unicode_compatible
-class CellBase(six.with_metaclass(CellMeta, models.Model)):
+class CellBase(models.Model, metaclass=CellMeta):
 
     page = models.ForeignKey(Page, on_delete=models.CASCADE)
     placeholder = models.CharField(max_length=20)
@@ -748,7 +744,7 @@ class CellBase(six.with_metaclass(CellMeta, models.Model)):
         label = self.get_verbose_name()
         additional_label = self.get_additional_label()
         if label and additional_label:
-            return u'%s (%s)' % (label, re.sub(r'\r?\n', u' ', force_text(additional_label)))
+            return '%s (%s)' % (label, re.sub(r'\r?\n', ' ', force_text(additional_label)))
         else:
             return force_text(label)
 
@@ -794,7 +790,7 @@ class CellBase(six.with_metaclass(CellMeta, models.Model)):
             return ''
 
         # add has-asset-<slug> for each asset found
-        css = sorted(['has-asset-{}'.format(v) for k, v in self._asset_keys.items() if k in self._assets])
+        css = sorted(f'has-asset-{v}' for k, v in self._asset_keys.items() if k in self._assets)
         # add has-any-asset if at least one asset defined
         if self._assets:
             css.append('has-any-asset')
@@ -811,7 +807,7 @@ class CellBase(six.with_metaclass(CellMeta, models.Model)):
         return self.slug
 
     def get_label_for_asset(self):
-        return _(u'%(cell_label)s on page %(page_name)s (%(page_slug)s)') % {
+        return _('%(cell_label)s on page %(page_name)s (%(page_slug)s)') % {
             'cell_label': str(self),
             'page_name': str(self.page),
             'page_slug': self.page.slug,
@@ -840,12 +836,12 @@ class CellBase(six.with_metaclass(CellMeta, models.Model)):
             if slot_template_data.get('suffix'):
                 suffix = ' (%s)' % slot_template_data['suffix']
             slot_key = self.get_asset_slot_key(slot_template_key)
-            label = u'%(prefix)s — %(label)s%(suffix)s' % {
+            label = '%(prefix)s — %(label)s%(suffix)s' % {
                 'prefix': slot_template_data['prefix'],
                 'label': self.get_label_for_asset(),
                 'suffix': suffix,
             }
-            short_label = u'%(prefix)s%(suffix)s' % {'prefix': slot_template_data['prefix'], 'suffix': suffix}
+            short_label = '%(prefix)s%(suffix)s' % {'prefix': slot_template_data['prefix'], 'suffix': suffix}
             slots[slot_key] = {
                 'label': label,
                 'short_label': short_label,
@@ -1237,12 +1233,12 @@ class TextCell(CellBase):
 
     @classmethod
     def get_cell_types(cls):
-        d = super(TextCell, cls).get_cell_types()
+        d = super().get_cell_types()
         d[0]['order'] = -1
         return d
 
     def get_cell_extra_context(self, context):
-        extra_context = super(TextCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         text = self.text or ''
         force_absolute_url = context.get('force_absolute_url') or context.get('render_skeleton')
 
@@ -1346,7 +1342,7 @@ class MenuCell(CellBase):
     def get_cell_extra_context(self, context):
         from combo.public.menu import render_menu
 
-        ctx = super(MenuCell, self).get_cell_extra_context(context)
+        ctx = super().get_cell_extra_context(context)
         ctx['menu'] = render_menu(
             context,
             level=self.initial_level,
@@ -1389,9 +1385,9 @@ class LinkCell(CellBase):
     def save(self, *args, **kwargs):
         if 'update_fields' in kwargs:
             # don't check validity
-            return super(LinkCell, self).save(*args, **kwargs)
+            return super().save(*args, **kwargs)
 
-        result = super(LinkCell, self).save(*args, **kwargs)
+        result = super().save(*args, **kwargs)
         # check validity
         self.check_validity()
         return result
@@ -1429,7 +1425,7 @@ class LinkCell(CellBase):
     def get_cell_extra_context(self, context):
         force_absolute_url = context.get('force_absolute_url') or context.get('render_skeleton')
         request = context.get('request')
-        extra_context = super(LinkCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         if self.link_page:
             extra_context['title'] = self.title or self.link_page.title
             extra_context['description'] = self.link_page.description
@@ -1523,7 +1519,7 @@ class LinkListCell(CellBase):
         return utils.ellipsize(title)
 
     def get_cell_extra_context(self, context):
-        extra_context = super(LinkListCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         links = []
         for cell in context.get('page_cells', []):
             if not hasattr(cell, 'add_as_link_label'):
@@ -1604,7 +1600,7 @@ class FeedCell(CellBase):
         verbose_name = _('RSS/Atom Feed')
 
     def is_visible(self, **kwargs):
-        return bool(self.url) and super(FeedCell, self).is_visible(**kwargs)
+        return bool(self.url) and super().is_visible(**kwargs)
 
     def save(self, *args, **kwargs):
         result = super().save(*args, **kwargs)
@@ -1615,7 +1611,7 @@ class FeedCell(CellBase):
         return result
 
     def get_cell_extra_context(self, context):
-        extra_context = super(FeedCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
 
         if not self.url:
             self.mark_as_invalid('data_url_not_defined')
@@ -1657,7 +1653,7 @@ class FeedCell(CellBase):
         feed_content = cache.get(cache_key)
         if not context.get('synchronous') and feed_content is None:
             raise NothingInCacheException()
-        return super(FeedCell, self).render(context)
+        return super().render(context)
 
 
 @register_cell_class
@@ -1741,7 +1737,7 @@ class JsonCellBase(CellBase):
         return result
 
     def is_visible(self, **kwargs):
-        return bool(self.url) and super(JsonCellBase, self).is_visible(**kwargs)
+        return bool(self.url) and super().is_visible(**kwargs)
 
     def is_user_dependant(self, context=None):
         urls = [self.url] + [x['url'] for x in self.additional_data or []]
@@ -1754,7 +1750,7 @@ class JsonCellBase(CellBase):
         return {}
 
     def get_cell_extra_context(self, context, invalidate_cache=False):
-        extra_context = super(JsonCellBase, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         if context.get('placeholder_search_mode'):
             # don't call webservices when we're just looking for placeholders
             return extra_context
@@ -1813,9 +1809,9 @@ class JsonCellBase(CellBase):
                 extra_context[data_key + '_exception'] = e
                 logger = logging.getLogger(__name__)
                 if log_errors:
-                    logger.warning(u'error on request %r: %s', url, force_text(e))
+                    logger.warning('error on request %r: %s', url, force_text(e))
                 else:
-                    logger.debug(u'error on request %r: %s', url, force_text(e))
+                    logger.debug('error on request %r: %s', url, force_text(e))
                 continue
             extra_context[data_key + '_status'] = json_response.status_code
             if json_response.status_code // 100 == 2:
@@ -1847,7 +1843,7 @@ class JsonCellBase(CellBase):
                     # ignore returns of url with more than one variable
                     continue
                 returns.append(extra_context.get(data_url['key'] + '_status'))
-            returns = set([s for s in returns if s is not None])
+            returns = {s for s in returns if s is not None}
             if returns and 200 not in returns:  # not a single valid answer
                 if 404 in returns:
                     self.mark_as_invalid('data_url_not_found')
@@ -1955,7 +1951,7 @@ class JsonCellBase(CellBase):
             tmpl = engines['django'].from_string(self.template_string)
             context.update(self.get_cell_extra_context(context))
             return tmpl.render(context, context.get('request'))
-        return super(JsonCellBase, self).render(context)
+        return super().render(context)
 
 
 @register_cell_class
@@ -1989,7 +1985,7 @@ class JsonCell(JsonCellBase):
 
 class ConfigJsonCellManager(models.Manager):
     def get_queryset(self):
-        queryset = super(ConfigJsonCellManager, self).get_queryset()
+        queryset = super().get_queryset()
         return queryset.filter(key__in=settings.JSON_CELL_TYPES.keys())
 
 
@@ -2026,7 +2022,7 @@ class ConfigJsonCell(JsonCellBase):
 
     @property
     def css_class_names(self):
-        return super(ConfigJsonCell, self).css_class_names + ' ' + self.key
+        return super().css_class_names + ' ' + self.key
 
     @property
     def ajax_refresh(self):
diff --git a/combo/data/utils.py b/combo/data/utils.py
index cca3a8a1..2ee6aa99 100644
--- a/combo/data/utils.py
+++ b/combo/data/utils.py
@@ -94,15 +94,15 @@ def import_site(data, if_empty=False, clean=False, request=None):
     groups = set()
     for page in data.get('pages') or []:
         for group in page['fields']['groups']:
-            groups.add(group if isinstance(group, six.string_types) else group[0])
+            groups.add(group if isinstance(group, str) else group[0])
         for cell in page['cells']:
             for group in cell['fields']['groups']:
-                groups.add(group if isinstance(group, six.string_types) else group[0])
+                groups.add(group if isinstance(group, str) else group[0])
 
-    existing_groups = set([x.name for x in Group.objects.filter(name__in=groups)])
+    existing_groups = {x.name for x in Group.objects.filter(name__in=groups)}
     missing_groups = groups - existing_groups
     if missing_groups:
-        raise MissingGroups(names=sorted([x for x in missing_groups]))
+        raise MissingGroups(names=sorted(x for x in missing_groups))
 
     with transaction.atomic():
         if clean:
diff --git a/combo/manager/fields.py b/combo/manager/fields.py
index 98340021..c11e38b9 100644
--- a/combo/manager/fields.py
+++ b/combo/manager/fields.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 #
 # combo - content management system
 # Copyright (C) 2015-2019  Entr'ouvert
@@ -37,4 +36,4 @@ class ImageIncludingSvgField(forms.ImageField):
         if data.name and data.name.endswith('.svg'):
             # bypass image field Pillow check
             return super(forms.ImageField, self).to_python(data)
-        return super(ImageIncludingSvgField, self).to_python(data)
+        return super().to_python(data)
diff --git a/combo/manager/forms.py b/combo/manager/forms.py
index aa23f6c5..9b8e931c 100644
--- a/combo/manager/forms.py
+++ b/combo/manager/forms.py
@@ -157,7 +157,7 @@ class PageVisibilityForm(forms.ModelForm):
         fields = ('public', 'groups')
 
     def __init__(self, *args, **kwargs):
-        super(PageVisibilityForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.fields['groups'].queryset = Group.objects.order_by('name')
 
 
@@ -167,7 +167,7 @@ class PageSelectTemplateForm(forms.ModelForm):
         fields = ('template_name',)
 
     def __init__(self, *args, **kwargs):
-        super(PageSelectTemplateForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.fields['template_name'].widget = forms.Select(choices=get_template_name_choices())
 
 
@@ -186,7 +186,7 @@ class PageEditRedirectionForm(forms.ModelForm):
         return value
 
     def save(self, *args, **kwargs):
-        page = super(PageEditRedirectionForm, self).save(*args, **kwargs)
+        page = super().save(*args, **kwargs)
         page.redirect_url = page.redirect_url.strip()
         page.save()
         return page
@@ -203,10 +203,10 @@ class PageEditIncludeInNavigationForm(forms.ModelForm):
         instance = kwargs['instance']
         initial = kwargs.pop('initial', {})
         initial['include_in_navigation'] = not instance.exclude_from_navigation
-        super(PageEditIncludeInNavigationForm, self).__init__(initial=initial, *args, **kwargs)
+        super().__init__(initial=initial, *args, **kwargs)
 
     def save(self, *args, **kwargs):
-        super(PageEditIncludeInNavigationForm, self).save(*args, **kwargs)
+        super().save(*args, **kwargs)
         self.instance.exclude_from_navigation = not self.cleaned_data['include_in_navigation']
         self.instance.save()
         return self.instance
@@ -241,7 +241,7 @@ class CellVisibilityForm(forms.Form):
                     initial['visibility'] = 'groups-none'
                 else:
                     initial['visibility'] = 'groups-any'
-        super(CellVisibilityForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
     def save(self):
         if self.cleaned_data['visibility'] == 'all':
diff --git a/combo/manager/views.py b/combo/manager/views.py
index 5ae494da..6c029b49 100644
--- a/combo/manager/views.py
+++ b/combo/manager/views.py
@@ -72,7 +72,7 @@ class HomepageView(ListView):
 
     def get_context_data(self, **kwargs):
         self.object_list = Page.get_as_reordered_flat_hierarchy(self.object_list)
-        context = super(HomepageView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['extra_actions'] = plugins.get_extra_manager_actions()
         context['collapse_pages'] = settings.COMBO_MANAGE_HOME_COLLAPSE_PAGES
         return context
@@ -138,7 +138,7 @@ class SiteImportView(FormView):
             for page in pages:
                 PageSnapshot.take(page, request=self.request, comment=_('imported'))
 
-        return super(SiteImportView, self).form_valid(form)
+        return super().form_valid(form)
 
 
 site_import = SiteImportView.as_view()
@@ -176,7 +176,7 @@ class PageAddView(CreateView):
         return kwargs
 
     def get_initial(self):
-        initial = super(PageAddView, self).get_initial()
+        initial = super().get_initial()
         if not Page.objects.exists():  # first page
             initial['title'] = _('Home')
         return initial
@@ -208,7 +208,7 @@ class PageEditView(UpdateView):
     comment = None
 
     def form_valid(self, form):
-        result = super(PageEditView, self).form_valid(form)
+        result = super().form_valid(form)
         PageSnapshot.take(self.get_object(), request=self.request, comment=self.comment)
         return result
 
@@ -228,7 +228,7 @@ class PageSelectTemplateView(PageEditView):
     def form_valid(self, form):
         old_template_name = self.get_object().template_name
         new_template_name = self.object.template_name
-        response = super(PageSelectTemplateView, self).form_valid(form)
+        response = super().form_valid(form)
         if old_template_name != new_template_name:
             old_placeholder_keys = [x.key for x in self.get_object().get_placeholders(request=self.request)]
             page_cells = CellBase.get_cells(page_id=self.object.id)
@@ -324,7 +324,7 @@ class PageView(DetailView):
     template_name = 'combo/page_view.html'
 
     def get_context_data(self, **kwargs):
-        context = super(PageView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         cell_type_groups = {}
         for cell_type in CellBase.get_all_cell_types():
             if not cell_type['group'] in cell_type_groups:
@@ -415,7 +415,7 @@ class PageDeleteView(DeleteView):
 
     def get_context_data(self, **kwargs):
         subpages = Page.objects.filter(parent=self.get_object().id)
-        context = super(PageDeleteView, self).get_context_data()
+        context = super().get_context_data()
         context['display_choice'] = True if subpages else False
         return context
 
@@ -445,7 +445,7 @@ class PageExportView(FormView):
         if with_children:
             pages = instance.get_descendants_and_me()
             ordered_pages = Page.get_as_reordered_flat_hierarchy(pages, root_page=instance)
-            filename_part = '{}_and_subpages'.format(instance.slug)
+            filename_part = f'{instance.slug}_and_subpages'
         else:
             ordered_pages = [instance]
             filename_part = instance.slug
@@ -489,7 +489,7 @@ class PageDuplicateView(FormView):
         return super().form_valid(form)
 
     def get_context_data(self, **kwargs):
-        context = super(PageDuplicateView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['page'] = Page.objects.get(id=self.kwargs['pk'])
         return context
 
@@ -516,7 +516,7 @@ class SnapshotRestoreView(DetailView):
     template_name = 'combo/snapshot_restore.html'
 
     def get_context_data(self, **kwargs):
-        context = super(SnapshotRestoreView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context['page'] = Page.objects.get(id=self.kwargs['page_pk'])
         return context
 
@@ -557,7 +557,7 @@ class PageEditCellView(UpdateView):
         return [self.template_name or self.object.manager_form_template]
 
     def get_context_data(self, **kwargs):
-        context = super(PageEditCellView, self).get_context_data(**kwargs)
+        context = super().get_context_data(**kwargs)
         context.update(self.get_object().get_extra_manager_context())
         context['cell'] = self.get_object()
         context['page'] = context['cell'].page
@@ -590,7 +590,7 @@ class PageEditCellView(UpdateView):
             form = self.get_form_class()(instance=self.object, prefix=self.get_prefix())
             response = self.form_invalid(form)  # avoid redirection
         else:
-            response = super(PageEditCellView, self).form_valid(form)
+            response = super().form_valid(form)
         PageSnapshot.take(
             self.object.page, request=self.request, comment=_('changed cell "%s"') % self.object
         )
@@ -615,7 +615,7 @@ class PageDeleteCellView(DeleteView):
         cell = self.get_object()
         comment = _('removed cell "%s"') % cell
         page = cell.page
-        response = super(PageDeleteCellView, self).delete(request, *args, **kwargs)
+        response = super().delete(request, *args, **kwargs)
         PageSnapshot.take(page, request=self.request, comment=comment)
         return response
 
@@ -787,13 +787,13 @@ class PageListCellAddLinkView(CreateView):
                 break
         if self.model is None:
             raise Http404
-        return super(PageListCellAddLinkView, self).dispatch(request, *args, **kwargs)
+        return super().dispatch(request, *args, **kwargs)
 
     def get_form_class(self):
         return self.model().get_default_form_class()
 
     def get_form_kwargs(self):
-        kwargs = super(PageListCellAddLinkView, self).get_form_kwargs()
+        kwargs = super().get_form_kwargs()
         kwargs['instance'] = self.model(page=self.cell.page, placeholder=self.cell.link_placeholder)
         return kwargs
 
@@ -804,7 +804,7 @@ class PageListCellAddLinkView(CreateView):
         else:
             form.instance.order = 1
         PageSnapshot.take(self.cell.page, request=self.request, comment=_('changed cell "%s"') % self.cell)
-        response = super(PageListCellAddLinkView, self).form_valid(form)
+        response = super().form_valid(form)
         self.cell.check_validity()
         return response
 
@@ -833,7 +833,7 @@ class PageListCellEditLinkView(UpdateView):
         if self.object.placeholder != self.cell.link_placeholder:
             raise Http404
         self.model = self.object.__class__
-        return super(PageListCellEditLinkView, self).dispatch(request, *args, **kwargs)
+        return super().dispatch(request, *args, **kwargs)
 
     def get_object(self, *args, **kwargs):
         return self.object
@@ -846,7 +846,7 @@ class PageListCellEditLinkView(UpdateView):
             self.object = form.save()
             response = self.form_invalid(form)  # avoid redirection
         else:
-            response = super(PageListCellEditLinkView, self).form_valid(form)
+            response = super().form_valid(form)
         PageSnapshot.take(self.cell.page, request=self.request, comment=_('changed cell "%s"') % self.cell)
         self.cell.check_validity()
         return response
@@ -876,13 +876,13 @@ class PageListCellDeleteLinkView(DeleteView):
         if self.object.placeholder != self.cell.link_placeholder:
             raise Http404
         self.model = self.object.__class__
-        return super(PageListCellDeleteLinkView, self).dispatch(request, *args, **kwargs)
+        return super().dispatch(request, *args, **kwargs)
 
     def get_object(self, *args, **kwargs):
         return self.object
 
     def delete(self, request, *args, **kwargs):
-        response = super(PageListCellDeleteLinkView, self).delete(request, *args, **kwargs)
+        response = super().delete(request, *args, **kwargs)
         PageSnapshot.take(self.cell.page, request=self.request, comment=_('changed cell "%s"') % self.cell)
         self.cell.check_validity()
         return response
diff --git a/combo/profile/__init__.py b/combo/profile/__init__.py
index 168afad3..1f546a8d 100644
--- a/combo/profile/__init__.py
+++ b/combo/profile/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 #
 # combo - content management system
 # Copyright (C) 2014-2018  Entr'ouvert
diff --git a/combo/profile/migrations/0001_initial.py b/combo/profile/migrations/0001_initial.py
index 3c0ee90c..0a70031a 100644
--- a/combo/profile/migrations/0001_initial.py
+++ b/combo/profile/migrations/0001_initial.py
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.conf import settings
 from django.db import migrations, models
 
diff --git a/combo/profile/migrations/0002_profilecell.py b/combo/profile/migrations/0002_profilecell.py
index 122049ba..0384aef9 100644
--- a/combo/profile/migrations/0002_profilecell.py
+++ b/combo/profile/migrations/0002_profilecell.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
 # Generated by Django 1.11.12 on 2018-08-10 13:52
-from __future__ import unicode_literals
 
 import django.db.models.deletion
 from django.db import migrations, models
diff --git a/combo/profile/models.py b/combo/profile/models.py
index 3159cddf..29cae2ed 100644
--- a/combo/profile/models.py
+++ b/combo/profile/models.py
@@ -49,10 +49,10 @@ class ProfileCell(JsonCellBase):
         user = kwargs.get('user')
         if not user or user.is_anonymous:
             return False
-        return super(ProfileCell, self).is_visible(**kwargs)
+        return super().is_visible(**kwargs)
 
     def get_cell_extra_context(self, context):
-        extra_context = super(ProfileCell, self).get_cell_extra_context(context)
+        extra_context = super().get_cell_extra_context(context)
         extra_context['profile_fields'] = OrderedDict()
         if extra_context.get('profile') is not None:
             for attribute in settings.USER_PROFILE_CONFIG.get('fields'):
diff --git a/combo/profile/utils.py b/combo/profile/utils.py
index 932d7de1..ebe24122 100644
--- a/combo/profile/utils.py
+++ b/combo/profile/utils.py
@@ -23,7 +23,7 @@ else:
     UserSAMLIdentifier = None
 
 
-class ProxiedUser(object):
+class ProxiedUser:
     is_authenticated = True
     is_anonymous = False
 
diff --git a/combo/public/templatetags/combo.py b/combo/public/templatetags/combo.py
index c492ee69..96dd0c7f 100644
--- a/combo/public/templatetags/combo.py
+++ b/combo/public/templatetags/combo.py
@@ -14,7 +14,6 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from __future__ import absolute_import
 
 import copy
 import datetime
@@ -448,7 +447,7 @@ def endswith(string, substring):
 
 
 def parse_float(value):
-    if isinstance(value, six.string_types):
+    if isinstance(value, str):
         # replace , by . for French users comfort
         value = value.replace(',', '.')
     try:
@@ -590,7 +589,7 @@ def iterate_days_until(value, until):
 
 
 def parse_decimal(value, default=Decimal(0)):
-    if isinstance(value, six.string_types):
+    if isinstance(value, str):
         # replace , by . for French users comfort
         value = value.replace(',', '.')
     try:
@@ -689,7 +688,7 @@ def json_script(value, element_id):
 def phonenumber_fr(value, separator=' '):
     DROMS = ('262', '508', '590', '594', '596')
 
-    if not value or not isinstance(value, six.string_types):
+    if not value or not isinstance(value, str):
         return value
     number = value.strip()
     if not number:
diff --git a/combo/public/views.py b/combo/public/views.py
index 12031b57..8be4fd1c 100644
--- a/combo/public/views.py
+++ b/combo/public/views.py
@@ -364,12 +364,12 @@ def skeleton(request):
     response = render(request, template_name, ctx)
     response['X-Combo-Page-Id'] = str(selected_page.id)
     response['X-Combo-Skeleton-Pages'] = json.dumps(
-        dict([(x.id, utils.get_templated_url(x.redirect_url)) for x in same_domain_pages])
+        {x.id: utils.get_templated_url(x.redirect_url) for x in same_domain_pages}
     )
     return response
 
 
-class StyleDemoPage(object):
+class StyleDemoPage:
     def __init__(self, request):
         self.request = request
         self.template_name = request.GET.get('template') or 'standard'
@@ -413,8 +413,8 @@ class StyleDemoPage(object):
                     attr = 'value="%s"' % template_key
                     if template_key == self.page.template_name:
                         attr += ' selected="selected"'
-                    options_html.append(u'<option %s>%s</option>' % (attr, template_dict['name']))
-                cell.text = u'''<form><select name="template">%s</select>
+                    options_html.append('<option %s>%s</option>' % (attr, template_dict['name']))
+                cell.text = '''<form><select name="template">%s</select>
                                 <button>%s</button></form>''' % (
                     '\n'.join(options_html),
                     _('Select'),
@@ -584,7 +584,7 @@ def publish_page(request, page, status=200, template_name=None):
     # load assets
     for cell in cells:
         cell._asset_keys = cell.get_asset_slot_keys()
-    asset_keys = list(set(chain(*[c._asset_keys.keys() for c in cells])))
+    asset_keys = list(set(chain(*(c._asset_keys.keys() for c in cells))))
     assets = list(Asset.objects.filter(key__in=asset_keys))
     for cell in cells:
         cell._assets = {a.key: a for a in assets if a.key in cell._asset_keys.keys()}
@@ -620,7 +620,7 @@ def error404(request, *args, **kwargs):
         return page_not_found(request, *args, **kwargs)
 
     if Page.objects.exists() and all(
-        (not x.is_visible(request.user) for x in Page.objects.filter(parent_id__isnull=True))
+        not x.is_visible(request.user) for x in Page.objects.filter(parent_id__isnull=True)
     ):
         # if none of the first-level pages can be viewed by the user, display
         # native django error page.
diff --git a/combo/urls_utils.py b/combo/urls_utils.py
index b7b1bb14..02d3a8d3 100644
--- a/combo/urls_utils.py
+++ b/combo/urls_utils.py
@@ -29,7 +29,7 @@ else:
 
 class DecoratedURLPattern(URLPattern):
     def resolve(self, *args, **kwargs):
-        result = super(DecoratedURLPattern, self).resolve(*args, **kwargs)
+        result = super().resolve(*args, **kwargs)
         if result:
             result.func = self._decorate_with(result.func)
         return result
@@ -37,7 +37,7 @@ class DecoratedURLPattern(URLPattern):
 
 class DecoratedURLResolver(URLResolver):
     def resolve(self, *args, **kwargs):
-        result = super(DecoratedURLResolver, self).resolve(*args, **kwargs)
+        result = super().resolve(*args, **kwargs)
         if result:
             result.func = self._decorate_with(result.func)
         return result
diff --git a/combo/utils/forms.py b/combo/utils/forms.py
index ea4d4fb9..0e96a1ef 100644
--- a/combo/utils/forms.py
+++ b/combo/utils/forms.py
@@ -27,7 +27,7 @@ class MultiSortWidget(forms.SelectMultiple):
             self.with_checkboxes = kwargs.pop('with_checkboxes')
         else:
             self.with_checkboxes = False
-        super(MultiSortWidget, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
     def render(self, name, value, attrs=None, choices=(), renderer=None):
         # reorder choices to get them in the current value order
@@ -43,7 +43,7 @@ class MultiSortWidget(forms.SelectMultiple):
                 self.choices.insert(0, option_tuple)
 
         # render the <select multiple>
-        rendered = super(MultiSortWidget, self).render(name, value, attrs=attrs)
+        rendered = super().render(name, value, attrs=attrs)
 
         # include it in a <div> that will be turned into an appropriate widget
         # in javascript
@@ -61,7 +61,7 @@ class MultiSortWidget(forms.SelectMultiple):
 
     def render_options(self, choices, value):
         value = value.get('data') or []
-        return super(MultiSortWidget, self).render_options(choices, value)
+        return super().render_options(choices, value)
 
     def value_from_datadict(self, data, files, name):
         if isinstance(data, MultiValueDict):
@@ -70,4 +70,4 @@ class MultiSortWidget(forms.SelectMultiple):
 
     def format_value(self, value):
         value = value.get('data') or []
-        return super(MultiSortWidget, self).format_value(value)
+        return super().format_value(value)
diff --git a/combo/utils/requests_wrapper.py b/combo/utils/requests_wrapper.py
index b5ffffbd..ce064ab5 100644
--- a/combo/utils/requests_wrapper.py
+++ b/combo/utils/requests_wrapper.py
@@ -134,7 +134,7 @@ class Requests(RequestsSession):
 
         kwargs['timeout'] = kwargs.get('timeout') or settings.REQUESTS_TIMEOUT
 
-        response = super(Requests, self).request(method, url, **kwargs)
+        response = super().request(method, url, **kwargs)
         if log_errors and (response.status_code // 100 != 2):
             extra = {}
             if django_request:
diff --git a/setup.py b/setup.py
index 4567331f..46dc0eec 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,4 @@
 #! /usr/bin/env python
-# -*- coding: utf-8 -*-
 
 import glob
 import itertools
@@ -35,7 +34,7 @@ def get_version():
     tag exists, take 0.0- and add the length of the commit log.
     """
     if os.path.exists('VERSION'):
-        with open('VERSION', 'r') as v:
+        with open('VERSION') as v:
             return v.read()
     if os.path.exists('.git'):
         p = subprocess.Popen(
diff --git a/tests/settings.py b/tests/settings.py
index c8796142..344ed228 100644
--- a/tests/settings.py
+++ b/tests/settings.py
@@ -68,7 +68,7 @@ MEDIA_ROOT = tempfile.mkdtemp('combo-test')
 
 if 'DISABLE_MIGRATIONS' in os.environ:
 
-    class DisableMigrations(object):
+    class DisableMigrations:
         def __contains__(self, item):
             return True
 
diff --git a/tests/test_assets.py b/tests/test_assets.py
index a95062bc..7484ba33 100644
--- a/tests/test_assets.py
+++ b/tests/test_assets.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
 import base64
 import os
 import tarfile
@@ -66,7 +64,7 @@ def test_asset_set_api(app, john_doe):
     resp = app.post_json(reverse('api-assets-set', kwargs={'key': 'plop'}), params={}, status=400)
     assert resp.json.get('err') == 1
 
-    for invalid_value in (None, 'not base 64', u'éléphant'):
+    for invalid_value in (None, 'not base 64', 'éléphant'):
         resp = app.post_json(
             reverse('api-assets-set', kwargs={'key': 'plop'}),
             params={
@@ -121,8 +119,8 @@ def test_tar_untar_assets(some_assets):
     data = untar_assets_files(tar)
     assert [x['fields']['key'] for x in data['assets']] == ['banner', 'favicon']
     assert count_asset_files() == 2
-    assert open('%s/assets/test.png' % path, 'r').read() == 'test'
-    assert open('%s/assets/test2.png' % path, 'r').read() == 'foo'
+    assert open('%s/assets/test.png' % path).read() == 'test'
+    assert open('%s/assets/test2.png' % path).read() == 'foo'
     clean_assets_files()
 
 
@@ -143,8 +141,8 @@ def test_import_export_assets(some_assets, tmpdir):
     fd = open(filename, 'rb')
     import_assets(fd, overwrite=True)
     assert count_asset_files() == 2
-    assert open('%s/assets/test.png' % path, 'r').read() == 'test'
-    assert open('%s/assets/test2.png' % path, 'r').read() == 'test2'
+    assert open('%s/assets/test.png' % path).read() == 'test'
+    assert open('%s/assets/test2.png' % path).read() == 'test2'
     clean_assets_files()
     assert count_asset_files() == 0
     clean_assets_files()
diff --git a/tests/test_calendar.py b/tests/test_calendar.py
index 16080d32..ae8ba026 100644
--- a/tests/test_calendar.py
+++ b/tests/test_calendar.py
@@ -1,7 +1,7 @@
 import datetime
 import json
+from unittest import mock
 
-import mock
 import pytest
 from django.contrib.auth.models import User
 from django.core.cache import cache
diff --git a/tests/test_cells.py b/tests/test_cells.py
index e8c8ec1a..68789771 100644
--- a/tests/test_cells.py
+++ b/tests/test_cells.py
@@ -2,8 +2,8 @@ import datetime
 import json
 import os
 import re
+from unittest import mock
 
-import mock
 import pytest
 import requests
 from django.apps import apps
@@ -81,7 +81,7 @@ def test_media():
     cells = [TextCelleWithMedia() for i in range(3)]
     assert (
         force_text(sum((cell.media for cell in cells), Media()))
-        == u'<script type="text/javascript" src="/static/coincoin.js"></script>'
+        == '<script type="text/javascript" src="/static/coincoin.js"></script>'
     )
 
 
@@ -1340,21 +1340,17 @@ def test_related_cell_types_tracking():
     assert Page.objects.get(id=page.id).related_cells['cell_types'] == ['data_textcell']
 
     LinkCell(page=page, placeholder='content', order=0, title='Test', url='http://example.net').save()
-    assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == set(
-        ['data_textcell', 'data_linkcell']
-    )
+    assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_textcell', 'data_linkcell'}
 
     with CaptureQueriesContext(connection) as ctx:
         assert len(CellBase.get_cells(page=Page.objects.get(id=page.id))) == 3
         assert len(ctx.captured_queries) == 1 + 2
 
     TextCell.objects.get(order=1).delete()
-    assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == set(
-        ['data_textcell', 'data_linkcell']
-    )
+    assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_textcell', 'data_linkcell'}
 
     TextCell.objects.get(order=0).delete()
-    assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == set(['data_linkcell'])
+    assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_linkcell'}
 
     with CaptureQueriesContext(connection) as ctx:
         assert len(CellBase.get_cells(page=Page.objects.get(id=page.id))) == 1
@@ -1371,9 +1367,7 @@ def test_related_cell_types_tracking():
     Page.objects.get(id=page.id).get_cells()
 
     TextCell(page=page, placeholder='content', order=0, text='hello').save()
-    assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == set(
-        ['data_textcell', 'data_linkcell']
-    )
+    assert set(Page.objects.get(id=page.id).related_cells['cell_types']) == {'data_textcell', 'data_linkcell'}
 
 
 def test_link_list_cell_duplicate():
@@ -1475,9 +1469,9 @@ def test_cell_assets(settings, app, admin_user):
     # only text cells are defined for assets
     settings.COMBO_CELL_ASSET_SLOTS = {'data_textcell': {'picture': {'prefix': 'Picture'}}}
     resp = app.get('/manage/assets/')
-    assert u'Picture — %s' % text_cell.get_label_for_asset() in resp.text
-    assert u'Picture — %s' % list_cell.get_label_for_asset() not in resp.text
-    assert u'Picture — %s' % item.get_label_for_asset() not in resp.text
+    assert 'Picture — %s' % text_cell.get_label_for_asset() in resp.text
+    assert 'Picture — %s' % list_cell.get_label_for_asset() not in resp.text
+    assert 'Picture — %s' % item.get_label_for_asset() not in resp.text
 
     # text and list link cells are defined for assets
     settings.COMBO_CELL_ASSET_SLOTS = {
@@ -1486,11 +1480,11 @@ def test_cell_assets(settings, app, admin_user):
         'data_linkcell': {'picture': {'prefix': 'Picture', 'suffix': 'test'}},
     }
     resp = app.get('/manage/assets/')
-    assert u'Picture — %s' % text_cell.get_label_for_asset() in resp.text
-    assert u'Picture — %s' % list_cell.get_label_for_asset() in resp.text
+    assert 'Picture — %s' % text_cell.get_label_for_asset() in resp.text
+    assert 'Picture — %s' % list_cell.get_label_for_asset() in resp.text
     # but items are excluded
     assert item.get_slug_for_asset() is None  # slug for asset is always None for items
-    assert u'Picture — %s' % item.get_label_for_asset() not in resp.text
+    assert 'Picture — %s' % item.get_label_for_asset() not in resp.text
 
     # test slugs
     link_cell = LinkCell.objects.create(
@@ -1500,13 +1494,13 @@ def test_cell_assets(settings, app, admin_user):
     )
     resp = app.get('/manage/assets/')
     assert link_cell.get_slug_for_asset() == ''
-    assert u'Picture — %s' % link_cell.get_label_for_asset() not in resp.text
+    assert 'Picture — %s' % link_cell.get_label_for_asset() not in resp.text
 
     link_cell.slug = 'foo'
     link_cell.save()
     resp = app.get('/manage/assets/')
     assert link_cell.get_slug_for_asset() == 'foo'
-    assert u'Picture — %s (test)' % link_cell.get_label_for_asset() in resp.text
+    assert 'Picture — %s (test)' % link_cell.get_label_for_asset() in resp.text
 
     link_cell.slug = ''
     link_cell.url = ''
@@ -1514,4 +1508,4 @@ def test_cell_assets(settings, app, admin_user):
     link_cell.save()
     resp = app.get('/manage/assets/')
     assert link_cell.get_slug_for_asset() == 'test_cell_assets'
-    assert u'Picture — %s (test)' % link_cell.get_label_for_asset() in resp.text
+    assert 'Picture — %s (test)' % link_cell.get_label_for_asset() in resp.text
diff --git a/tests/test_cron.py b/tests/test_cron.py
index 5aa8ed4a..dbcd35b9 100644
--- a/tests/test_cron.py
+++ b/tests/test_cron.py
@@ -1,4 +1,5 @@
-import mock
+from unittest import mock
+
 import pytest
 from django.core.management import call_command
 from django.core.management.base import CommandError
diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py
index fc6cbfb1..b9cda9cf 100644
--- a/tests/test_dashboard.py
+++ b/tests/test_dashboard.py
@@ -1,8 +1,8 @@
 import datetime
 import json
 import os
+from unittest import mock
 
-import mock
 import pytest
 from django.apps import apps
 from django.conf import settings
diff --git a/tests/test_dataviz.py b/tests/test_dataviz.py
index 3c469ec3..67ea1a67 100644
--- a/tests/test_dataviz.py
+++ b/tests/test_dataviz.py
@@ -1,8 +1,8 @@
 import json
 import urllib.parse
 from datetime import date, timedelta
+from unittest import mock
 
-import mock
 import pytest
 from django.apps import apps
 from django.contrib.auth.models import Group, User
@@ -485,9 +485,9 @@ def test_chartng_cell(app, statistics):
     assert chart.__class__.__name__ == 'Pie'
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222], {'title': u'web'}),
-        ([134], {'title': u'mail'}),
-        ([53], {'title': u'email'}),
+        ([222], {'title': 'web'}),
+        ([134], {'title': 'mail'}),
+        ([53], {'title': 'email'}),
     ]
 
     # data in Y
@@ -507,8 +507,8 @@ def test_chartng_cell(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # stacked bar with percent
@@ -518,8 +518,8 @@ def test_chartng_cell(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([64.5, 54, 0, 61.6], {'title': u'foo'}),
-        ([35.5, 46, 100, 38.4], {'title': u'bar'}),
+        ([64.5, 54, 0, 61.6], {'title': 'foo'}),
+        ([35.5, 46, 100, 38.4], {'title': 'bar'}),
     ]
     assert all(x + y == 100 for x, y in zip(chart.raw_series[0][0], chart.raw_series[1][0]))
 
@@ -538,8 +538,8 @@ def test_chartng_cell(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # loop/Y
@@ -548,8 +548,8 @@ def test_chartng_cell(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # loop/X/Y
@@ -669,9 +669,9 @@ def test_chartng_cell_hide_null_values(app, statistics):
     assert chart.__class__.__name__ == 'Pie'
     assert chart.x_labels == ['web', 'mail', 'email']
     assert chart.raw_series == [
-        ([222], {'title': u'web'}),
-        ([134], {'title': u'mail'}),
-        ([53], {'title': u'email'}),
+        ([222], {'title': 'web'}),
+        ([134], {'title': 'mail'}),
+        ([53], {'title': 'email'}),
     ]
 
     # data in Y
@@ -691,8 +691,8 @@ def test_chartng_cell_hide_null_values(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # single data point
@@ -710,8 +710,8 @@ def test_chartng_cell_hide_null_values(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # loop/Y
@@ -720,8 +720,8 @@ def test_chartng_cell_hide_null_values(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # all null
@@ -779,9 +779,9 @@ def test_chartng_cell_sort_order_alpha(app, statistics):
     assert chart.__class__.__name__ == 'Pie'
     assert chart.x_labels == ['email', 'mail', 'phone', 'web']
     assert chart.raw_series == [
-        ([53], {'title': u'email'}),
-        ([134], {'title': u'mail'}),
-        ([222], {'title': u'web'}),
+        ([53], {'title': 'email'}),
+        ([134], {'title': 'mail'}),
+        ([222], {'title': 'web'}),
     ]
 
     # data in Y
@@ -801,8 +801,8 @@ def test_chartng_cell_sort_order_alpha(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # single data point
@@ -820,8 +820,8 @@ def test_chartng_cell_sort_order_alpha(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # loop/Y
@@ -830,8 +830,8 @@ def test_chartng_cell_sort_order_alpha(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # empty data
@@ -871,9 +871,9 @@ def test_chartng_cell_sort_order_desc(app, statistics):
     assert chart.__class__.__name__ == 'Pie'
     assert chart.x_labels == ['web', 'mail', 'email', 'phone']
     assert chart.raw_series == [
-        ([222], {'title': u'web'}),
-        ([134], {'title': u'mail'}),
-        ([53], {'title': u'email'}),
+        ([222], {'title': 'web'}),
+        ([134], {'title': 'mail'}),
+        ([53], {'title': 'email'}),
     ]
 
     # data in Y
@@ -893,8 +893,8 @@ def test_chartng_cell_sort_order_desc(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # single data point
@@ -912,8 +912,8 @@ def test_chartng_cell_sort_order_desc(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # loop/Y
@@ -922,8 +922,8 @@ def test_chartng_cell_sort_order_desc(app, statistics):
     chart = cell.get_chart()
     assert chart.x_labels == ['web', 'mail', 'phone', 'email']
     assert chart.raw_series == [
-        ([222, 134, 0, 53], {'title': u'foo'}),
-        ([122, 114, 2, 33], {'title': u'bar'}),
+        ([222, 134, 0, 53], {'title': 'foo'}),
+        ([122, 114, 2, 33], {'title': 'bar'}),
     ]
 
     # empty data
diff --git a/tests/test_ensure_jsonbfields.py b/tests/test_ensure_jsonbfields.py
index e3dccb2f..034ac94f 100644
--- a/tests/test_ensure_jsonbfields.py
+++ b/tests/test_ensure_jsonbfields.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
 import pytest
 from django.core.management import call_command
 from django.db import connection
diff --git a/tests/test_fargo.py b/tests/test_fargo.py
index 6aa56d3e..43242cab 100644
--- a/tests/test_fargo.py
+++ b/tests/test_fargo.py
@@ -1,7 +1,7 @@
 import json
 import re
+from unittest import mock
 
-import mock
 import pytest
 from django.test import override_settings
 
diff --git a/tests/test_gallery_cell.py b/tests/test_gallery_cell.py
index 5cc375ae..a095bf54 100644
--- a/tests/test_gallery_cell.py
+++ b/tests/test_gallery_cell.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import os
 
 import pytest
diff --git a/tests/test_lingo_cells.py b/tests/test_lingo_cells.py
index 80dcd939..7e1a9467 100644
--- a/tests/test_lingo_cells.py
+++ b/tests/test_lingo_cells.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
 import eopayment
 import pytest
 from django.contrib.auth.models import User
@@ -88,7 +86,7 @@ def test_basket_cell(regie, user):
     item_anonymous.save()
 
     assert cell.is_relevant(context) is True
-    assert cell.get_badge(context) == {'badge': u'12345€'}
+    assert cell.get_badge(context) == {'badge': '12345€'}
 
     item.cancellation_date = timezone.now()
     item.save()
@@ -103,7 +101,7 @@ def test_basket_cell(regie, user):
 
     item.amount = 123.45
     item.save()
-    assert cell.get_badge(context) == {'badge': u'123.45€'}
+    assert cell.get_badge(context) == {'badge': '123.45€'}
 
 
 def test_basket_cell_can_pay_only_one_basket_item(regie, user):
diff --git a/tests/test_lingo_manager.py b/tests/test_lingo_manager.py
index 329f8c67..76b06336 100644
--- a/tests/test_lingo_manager.py
+++ b/tests/test_lingo_manager.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
-
 import datetime
 from decimal import Decimal
+from unittest import mock
 
 import eopayment
-import mock
 import pytest
 from django.contrib.auth.models import User
 from django.db import connection
@@ -172,7 +170,7 @@ def test_download_transaction(app, admin_user, payment_backend):
     test_add_regie(app, admin_user, payment_backend)
     regie = Regie.objects.filter(slug='test')[0]
     user = User.objects.create_user('dimebag', 'dime@bag.pan', 'pwd')
-    user.last_name = u'Darëll'
+    user.last_name = 'Darëll'
     user.first_name = 'Dimebag'
     user.save()
 
diff --git a/tests/test_lingo_payment.py b/tests/test_lingo_payment.py
index 625a8d32..712f8500 100644
--- a/tests/test_lingo_payment.py
+++ b/tests/test_lingo_payment.py
@@ -3,10 +3,10 @@ import uuid
 from contextlib import contextmanager
 from datetime import datetime, timedelta
 from decimal import Decimal
+from unittest import mock
 
 import eopayment
 import httmock
-import mock
 import pytest
 from django.apps import apps
 from django.conf import settings
@@ -728,7 +728,7 @@ def test_cancel_basket_item(app, key, regie, user_name_id):
         url = sign_url(url, key)
         data = {'basket_item_id': basket_item_id, 'notify': 'true'}
         resp = app.post_json(url, params=data)
-        assert request.call_args[0] == ('POST', u'http://example.com/jump/trigger/cancelled')
+        assert request.call_args[0] == ('POST', 'http://example.com/jump/trigger/cancelled')
         assert not BasketItem.objects.filter(amount=42, cancellation_date__isnull=True).exists()
         assert BasketItem.objects.filter(amount=21, cancellation_date__isnull=True).exists()
 
diff --git a/tests/test_lingo_remote_regie.py b/tests/test_lingo_remote_regie.py
index 564a4198..9f7cf524 100644
--- a/tests/test_lingo_remote_regie.py
+++ b/tests/test_lingo_remote_regie.py
@@ -1,12 +1,10 @@
-# -*- coding: utf-8 -*-
-
 import copy
 import json
 from decimal import Decimal
+from unittest import mock
 
 import eopayment
 import httmock
-import mock
 import pytest
 from django.apps import apps
 from django.conf import settings
@@ -108,7 +106,7 @@ def remote_regie():
     return regie
 
 
-class MockUser(object):
+class MockUser:
     email = 'foo@example.net'
     is_authenticated = True
 
@@ -739,7 +737,7 @@ def test_send_new_remote_invoices_by_email(mock_get, user_saml, admin, app, remo
     assert len(mailoutbox) == 1
     assert mailoutbox[0].recipients() == ['foo@example.net']
     assert mailoutbox[0].from_email == settings.DEFAULT_FROM_EMAIL
-    assert mailoutbox[0].subject == u'Nouvelle facture numéro 01 disponible'
+    assert mailoutbox[0].subject == 'Nouvelle facture numéro 01 disponible'
     html_message = mailoutbox[0].alternatives[0][0]
     assert 'http://localhost' in mailoutbox[0].body
     assert 'http://localhost' in html_message
diff --git a/tests/test_manager.py b/tests/test_manager.py
index a642bd9a..7e5c8f11 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -4,8 +4,8 @@ import json
 import os
 import re
 import shutil
+from unittest import mock
 
-import mock
 import pytest
 from django.conf import settings
 from django.contrib.auth.models import Group
@@ -893,9 +893,7 @@ def test_site_export_import_json(app, admin_user):
     with CaptureQueriesContext(connection) as ctx:
         resp = resp.form.submit()
         assert len(ctx.captured_queries) == 272
-    assert set(Page.objects.get(slug='one').related_cells['cell_types']) == set(
-        ['data_textcell', 'data_linkcell']
-    )
+    assert set(Page.objects.get(slug='one').related_cells['cell_types']) == {'data_textcell', 'data_linkcell'}
     assert Page.objects.count() == 4
     assert LinkCell.objects.count() == 2
     assert LinkCell.objects.get(page__slug='one').link_page.slug == 'two'
@@ -934,7 +932,7 @@ def test_site_export_import_tar(app, admin_user):
     cell.save()
     Asset(key='collectivity:banner', asset=File(BytesIO(b'test'), 'test.png')).save()
     path = default_storage.path('')
-    assert open('%s/assets/test.png' % path, 'r').read() == 'test'
+    assert open('%s/assets/test.png' % path).read() == 'test'
 
     app = login(app)
     resp = app.get('/manage/')
@@ -959,7 +957,7 @@ def test_site_export_import_tar(app, admin_user):
     assert PageSnapshot.objects.all().count() == 1
     assert TextCell.objects.count() == 1
     assert Asset.objects.filter(key='collectivity:banner').count() == 1
-    assert open('%s/assets/test.png' % path, 'r').read() == 'foo'
+    assert open('%s/assets/test.png' % path).read() == 'foo'
 
     os.remove('%s/assets/test.png' % path)
     app = login(app)
@@ -967,7 +965,7 @@ def test_site_export_import_tar(app, admin_user):
     resp = resp.click('Import Site')
     resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
     resp = resp.form.submit()
-    assert open('%s/assets/test.png' % path, 'r').read() == 'test'
+    assert open('%s/assets/test.png' % path).read() == 'test'
 
 
 def test_site_export_import_missing_group(app, admin_user):
@@ -1070,10 +1068,9 @@ def test_invalid_cell_report(app, admin_user):
     cell.mark_as_invalid('foo_bar_reason')
     resp = app.get('/manage/cells/invalid-report/')
     assert resp.context['object_list'] == [cell]
-    assert '<a href="/manage/pages/{}/">{}</a>'.format(page.pk, page.title) in resp.text
+    assert f'<a href="/manage/pages/{page.pk}/">{page.title}</a>' in resp.text
     assert (
-        '<a href="/manage/pages/{}/#cell-{}">{}</a>'.format(page.pk, cell.get_reference(), cell.get_label())
-        in resp.text
+        f'<a href="/manage/pages/{page.pk}/#cell-{cell.get_reference()}">{cell.get_label()}</a>' in resp.text
     )
 
     # cells from snapshot are not reported
@@ -1210,7 +1207,7 @@ SEARCH_SERVICES = {
 }
 
 
-class SearchServices(object):
+class SearchServices:
     def __init__(self, search_services):
         self.search_services = search_services
 
@@ -1441,7 +1438,7 @@ def test_edit_text_cell(app, admin_user):
         resp = app.get('/manage/pages/%s/' % page.id)
         resp.form['cdata_textcell-%s-text' % cells[0].id].value = 'Hello : World'
         resp = resp.form.submit()
-        assert TextCell.objects.get(id=cells[0].id).text == u'Hello\u00a0: World'
+        assert TextCell.objects.get(id=cells[0].id).text == 'Hello\u00a0: World'
 
 
 def test_edit_json_cell(app, admin_user):
@@ -2045,7 +2042,7 @@ def test_asset_export_import(app, admin_user):
     resp = resp.click('Import')
     resp.form['assets_file'] = Upload('test.tar', content)
     resp = resp.form.submit()
-    assert sum([len(x[2]) for x in os.walk(default_storage.path(''))]) == 2
+    assert sum(len(x[2]) for x in os.walk(default_storage.path(''))) == 2
     resp = resp.follow()
     assert 'The assets file has been imported.' in resp.text
 
@@ -2084,7 +2081,7 @@ def test_asset_export_import(app, admin_user):
     resp = resp.click('Import')
     resp.form['assets_file'] = Upload('test.tar', b'garbage')
     resp = resp.form.submit()
-    assert sum([len(x[2]) for x in os.walk(default_storage.path(''))]) == 0
+    assert sum(len(x[2]) for x in os.walk(default_storage.path(''))) == 0
     resp = resp.follow()
     assert 'The assets file is not valid.' in resp.text
 
diff --git a/tests/test_maps_cells.py b/tests/test_maps_cells.py
index ff160cc4..03ad027b 100644
--- a/tests/test_maps_cells.py
+++ b/tests/test_maps_cells.py
@@ -1,7 +1,6 @@
-# -*- coding: utf-8 -*-
 import json
+from unittest import mock
 
-import mock
 import pytest
 from django.conf import settings
 from django.contrib.auth.models import Group, User
diff --git a/tests/test_maps_manager.py b/tests/test_maps_manager.py
index fb688d6e..a8046f0d 100644
--- a/tests/test_maps_manager.py
+++ b/tests/test_maps_manager.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
+from unittest import mock
 
-import mock
 import pytest
 
 from combo.apps.maps.models import Map, MapLayer, MapLayerOptions
@@ -176,8 +175,8 @@ def test_list_layers(app, admin_user, layer):
 
     app = login(app)
     resp = app.get('/manage/maps/', status=200)
-    assert '/manage/pages/{}/#cell-{}'.format(page.pk, map1.get_reference()) in resp.text
-    assert '/manage/pages/{}/#cell-{}'.format(page.pk, map2.get_reference()) in resp.text
+    assert f'/manage/pages/{page.pk}/#cell-{map1.get_reference()}' in resp.text
+    assert f'/manage/pages/{page.pk}/#cell-{map2.get_reference()}' in resp.text
 
 
 @mock.patch('combo.apps.maps.models.requests.get')
diff --git a/tests/test_notification.py b/tests/test_notification.py
index 62dc8b44..238c366d 100644
--- a/tests/test_notification.py
+++ b/tests/test_notification.py
@@ -1,8 +1,8 @@
 import json
 from datetime import timedelta as timedelta
 from decimal import Decimal
+from unittest import mock
 
-import mock
 import pytest
 from django.contrib.auth.models import User
 from django.test import Client
@@ -143,7 +143,7 @@ def test_notification_cell(app, john_doe, jane_doe):
 
     app = login_app(app, username='jane.doe', password='jane.doe')
     resp = app.get('/api/menu-badges/?page[]=%s' % page.id)
-    assert resp.json == {'{}'.format(page.id): {'badge': '1'}}
+    assert resp.json == {f'{page.id}': {'badge': '1'}}
 
 
 def test_notification_ws(john_doe):
diff --git a/tests/test_pages.py b/tests/test_pages.py
index 587296ce..99d4fe53 100644
--- a/tests/test_pages.py
+++ b/tests/test_pages.py
@@ -18,10 +18,10 @@ pytestmark = pytest.mark.django_db
 
 def test_page_order():
     page = Page()
-    page.title = u'foo'
+    page.title = 'foo'
     page.save()
     page2 = Page()
-    page2.title = u'bar'
+    page2.title = 'bar'
     page2.save()
 
     assert Page.objects.get(id=page.id).order < Page.objects.get(id=page2.id).order
@@ -29,7 +29,7 @@ def test_page_order():
 
 def test_page_url():
     page = Page()
-    page.slug = u'foo'
+    page.slug = 'foo'
     page.save()
     assert page.get_online_url() == '/foo/'
     page2 = Page()
@@ -147,13 +147,13 @@ def test_page_visibility_groups_init():
 
 
 def test_import_export_pages():
-    page = Page(title=u'foo', slug='foo', order=0, description="Foo's page")
+    page = Page(title='foo', slug='foo', order=0, description="Foo's page")
     page.save()
 
     cell = TextCell(page=page, text='foo', order=0, placeholder='content')
     cell.save()
 
-    page2 = Page(title=u'bar', slug='bar', order=1, parent=page, description="Bar's page")
+    page2 = Page(title='bar', slug='bar', order=1, parent=page, description="Bar's page")
     page2.save()
 
     cell = TextCell(page=page2, text='bar', order=0, placeholder='content')
@@ -177,10 +177,10 @@ def test_import_export_pages():
 
 
 def test_import_export_pages_with_links():
-    page = Page(title=u'foo', slug='foo', order=0)
+    page = Page(title='foo', slug='foo', order=0)
     page.save()
 
-    page2 = Page(title=u'bar', slug='bar', order=1)
+    page2 = Page(title='bar', slug='bar', order=1)
     page2.save()
 
     cell = LinkCell(page=page, title='bar', placeholder='content', link_page=page2, order=1)
@@ -328,13 +328,13 @@ def test_next_previous():
 
 
 def test_import_export_management_commands():
-    page = Page(title=u'foo', slug='foo', order=0)
+    page = Page(title='foo', slug='foo', order=0)
     page.save()
 
     cell = TextCell(page=page, text='foo', order=0, placeholder='content')
     cell.save()
 
-    page2 = Page(title=u'bar', slug='bar', order=1, parent=page)
+    page2 = Page(title='bar', slug='bar', order=1, parent=page)
     page2.save()
 
     cell = TextCell(page=page2, text='bar', order=0, placeholder='content')
@@ -372,7 +372,7 @@ def test_import_export_management_commands():
 
 
 def test_get_placeholders():
-    page = Page(title=u'foo', slug='foo', template_name='standard-sidebar', order=0)
+    page = Page(title='foo', slug='foo', template_name='standard-sidebar', order=0)
     request = RequestFactory().get('/')
     placeholders = page.get_placeholders(request=request)
     assert [x.key for x in placeholders] == ['content', 'sidebar', 'footer']
@@ -394,14 +394,14 @@ def test_get_placeholders():
         }
     ):
         placeholders = page.get_placeholders(request=request)
-        assert set([x.key for x in placeholders]) == set(['content', 'content2', 'sidebar', 'footer'])
+        assert {x.key for x in placeholders} == {'content', 'content2', 'sidebar', 'footer'}
         footer_placeholder = [x for x in placeholders if x.key == 'footer'][0]
         assert footer_placeholder.acquired is False
 
 
 def test_render(app):
     page = Page(
-        title=u'foo', slug='foo', template_name='standard-sidebar', order=0, description="page description"
+        title='foo', slug='foo', template_name='standard-sidebar', order=0, description="page description"
     )
     page.save()
     response = app.get(page.get_online_url())
@@ -411,7 +411,7 @@ def test_render(app):
 
 def test_render_cell_having_href_template_error(app):
     page = Page(
-        title=u'foo', slug='foo', template_name='standard-sidebar', order=0, description="page description"
+        title='foo', slug='foo', template_name='standard-sidebar', order=0, description="page description"
     )
     page.save()
     cell = TextCell(
diff --git a/tests/test_profile.py b/tests/test_profile.py
index 7de3b4c9..0565c78a 100644
--- a/tests/test_profile.py
+++ b/tests/test_profile.py
@@ -1,7 +1,7 @@
 import datetime
 import json
+from unittest import mock
 
-import mock
 import pytest
 from django.test import override_settings
 
diff --git a/tests/test_public.py b/tests/test_public.py
index be5dd584..916f4904 100644
--- a/tests/test_public.py
+++ b/tests/test_public.py
@@ -2,8 +2,8 @@ import datetime
 import json
 import os
 import re
+from unittest import mock
 
-import mock
 import pytest
 from django.conf import settings
 from django.contrib.auth.models import Group, User
@@ -980,7 +980,7 @@ def test_familycell_unlink(requests_get, app, settings, admin_user):
 
 
 def test_synchronous_placeholder(app):
-    page = Page(title=u'foo', slug='foo', template_name='standard', order=0)
+    page = Page(title='foo', slug='foo', template_name='standard', order=0)
     page.save()
     cell = FeedCell(page=page, placeholder='content', url='http://example.net', order=1)
     cell.save()
diff --git a/tests/test_publik.py b/tests/test_publik.py
index a6c1b996..01813d8f 100644
--- a/tests/test_publik.py
+++ b/tests/test_publik.py
@@ -27,7 +27,7 @@ def normal_user():
     return user
 
 
-class KnownServices(object):
+class KnownServices:
     def __init__(self, known_services):
         self.known_services = known_services
         self.orig_value = settings.KNOWN_SERVICES
diff --git a/tests/test_pwa.py b/tests/test_pwa.py
index 1f5ec5b3..64362833 100644
--- a/tests/test_pwa.py
+++ b/tests/test_pwa.py
@@ -1,7 +1,7 @@
 import base64
 import os
+from unittest import mock
 
-import mock
 import pytest
 import pywebpush
 from django.conf import settings
diff --git a/tests/test_requests.py b/tests/test_requests.py
index 5b7b5747..9155bcfa 100644
--- a/tests/test_requests.py
+++ b/tests/test_requests.py
@@ -1,6 +1,5 @@
-# -*- coding: utf-8 -*-
+from unittest import mock
 
-import mock
 import pytest
 from django.contrib.auth.models import AnonymousUser
 from django.utils.six.moves.urllib import parse as urlparse
@@ -8,7 +7,7 @@ from django.utils.six.moves.urllib import parse as urlparse
 from combo.utils import NothingInCacheException, check_query, requests
 
 
-class MockUser(object):
+class MockUser:
     email = 'foo@example.net'
     is_authenticated = True
 
@@ -150,4 +149,4 @@ def test_requests_cache():
             requests.get('http://cache.example.org/other', raise_if_not_cached=True)
 
         # check with unicode url
-        assert requests.get(u'http://cache.example.org/éléphant').content == b'hello second world'
+        assert requests.get('http://cache.example.org/éléphant').content == b'hello second world'
diff --git a/tests/test_search.py b/tests/test_search.py
index 105a9f9d..bfea4b03 100644
--- a/tests/test_search.py
+++ b/tests/test_search.py
@@ -1,8 +1,8 @@
 import datetime
 import json
 import os
+from unittest import mock
 
-import mock
 import pytest
 from django.conf import settings
 from django.contrib.auth.models import AnonymousUser, Group, User
@@ -46,7 +46,7 @@ SEARCH_SERVICES = {
 TEMPLATE_VARS = {'search_url': 'http://search.example.net/'}
 
 
-class SearchServices(object):
+class SearchServices:
     def __init__(self, search_services):
         self.search_services = search_services
 
@@ -893,15 +893,15 @@ def test_wcs_errors(settings, app):
         mock_resp = Response()
         mock_resp.status_code = 500
         requests_get.return_value = mock_resp
-        assert set(engines.get_engines().keys()) == set(['tracking-code', 'formdata:c21f969b', '_text'])
+        assert set(engines.get_engines().keys()) == {'tracking-code', 'formdata:c21f969b', '_text'}
     with mock.patch('combo.apps.wcs.utils.requests.get') as requests_get:
         requests_get.side_effect = ConnectionError()
-        assert set(engines.get_engines().keys()) == set(['tracking-code', 'formdata:c21f969b', '_text'])
+        assert set(engines.get_engines().keys()) == {'tracking-code', 'formdata:c21f969b', '_text'}
     with mock.patch('combo.apps.wcs.utils.requests.get') as requests_get:
         mock_resp = Response()
         mock_resp.status_code = 404
         requests_get.return_value = mock_resp
-        assert set(engines.get_engines().keys()) == set(['tracking-code', 'formdata:c21f969b', '_text'])
+        assert set(engines.get_engines().keys()) == {'tracking-code', 'formdata:c21f969b', '_text'}
 
 
 @mock.patch('combo.apps.wcs.utils.get_wcs_json')
@@ -1429,5 +1429,5 @@ def test_search_by_page_title(app):
     # Check that title matching gets precedence over content matching
     assert len(hits) == 2
     assert hits[0]['text'] == page_of_interest.title
-    assert hits[0]['url'] == '/{}/'.format(page_of_interest.slug)
+    assert hits[0]['url'] == f'/{page_of_interest.slug}/'
     assert hits[0]['rank'] > hits[1]['rank']
diff --git a/tests/test_spooler.py b/tests/test_spooler.py
index ede06e3b..2ed6138b 100644
--- a/tests/test_spooler.py
+++ b/tests/test_spooler.py
@@ -1,8 +1,8 @@
 import importlib
 import pickle
 import sys
+from unittest import mock
 
-import mock
 import pytest
 
 from combo.utils import spooler
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 91e03922..2ea2ee5e 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -9,7 +9,7 @@ from django.utils.encoding import force_bytes
 from combo.utils import TemplateError, aes_hex_decrypt, aes_hex_encrypt, get_templated_url
 
 
-class MockUser(object):
+class MockUser:
     email = 'foo=3@example.net'
     is_authenticated = True
 
diff --git a/tests/test_wcs.py b/tests/test_wcs.py
index 183f59fc..0d4dd705 100644
--- a/tests/test_wcs.py
+++ b/tests/test_wcs.py
@@ -1,10 +1,8 @@
-# -*- coding: utf-8 -*-
-
 import copy
 import json
 import re
+from unittest import mock
 
-import mock
 import pytest
 from django.apps import apps
 from django.conf import settings
@@ -223,7 +221,7 @@ WCS_CARD_DATA = {
 }
 
 
-class MockUser(object):
+class MockUser:
     email = 'foo@example.net'
     is_authenticated = True
     is_anonymous = False
@@ -232,7 +230,7 @@ class MockUser(object):
         return None
 
 
-class MockUserWithNameId(object):
+class MockUserWithNameId:
     email = 'foo@example.net'
     is_authenticated = True
     is_anonymous = False
@@ -307,14 +305,14 @@ def test_form_cell_setup(mock_send):
     form_class = cell.get_default_form_class()
     form = form_class()
     assert form.fields['formdef_reference'].widget.choices == [
-        (u'default:a-private-form', u'test : a private form'),
-        (u'default:a-second-form-title', u'test : a second form title'),
-        (u'default:form-title', u'test : form title'),
-        (u'default:third-form-title', u'test : Third form title'),
-        (u'other:a-private-form', u'test2 : a private form'),
-        (u'other:a-second-form-title', u'test2 : a second form title'),
-        (u'other:form-title', u'test2 : form title'),
-        (u'other:third-form-title', u'test2 : Third form title'),
+        ('default:a-private-form', 'test : a private form'),
+        ('default:a-second-form-title', 'test : a second form title'),
+        ('default:form-title', 'test : form title'),
+        ('default:third-form-title', 'test : Third form title'),
+        ('other:a-private-form', 'test2 : a private form'),
+        ('other:a-second-form-title', 'test2 : a second form title'),
+        ('other:form-title', 'test2 : form title'),
+        ('other:third-form-title', 'test2 : Third form title'),
     ]
 
 
@@ -324,7 +322,7 @@ def test_form_cell_save_cache(mock_send):
     page.save()
     cell = WcsFormCell(page=page, placeholder='content', order=0)
     assert cell.get_additional_label() is None
-    cell.formdef_reference = u'default:form-title'
+    cell.formdef_reference = 'default:form-title'
     cell.save()
     assert cell.cached_title == 'form title'
     assert cell.get_additional_label() == 'form title'
@@ -354,7 +352,7 @@ def test_form_cell_validity(mock_send):
     assert validity_info.invalid_reason_code == 'wcs_form_not_defined'
     assert validity_info.invalid_since is not None
 
-    cell.formdef_reference = u'default:form-title'
+    cell.formdef_reference = 'default:form-title'
     cell.save()
     assert ValidityInfo.objects.exists() is False
 
@@ -378,7 +376,7 @@ def test_form_cell_validity(mock_send):
         cell.save()
     assert ValidityInfo.objects.exists() is False
 
-    cell.formdef_reference = u'default:foobar'
+    cell.formdef_reference = 'default:foobar'
     cell.save()
     validity_info = ValidityInfo.objects.latest('pk')
     assert validity_info.invalid_reason_code == 'wcs_form_not_found'
@@ -390,7 +388,7 @@ def test_form_cell_load(mock_send):
     page = Page(title='xxx', slug='test_form_cell_save_cache', template_name='standard')
     page.save()
     cell = WcsFormCell(page=page, placeholder='content', order=0)
-    cell.formdef_reference = u'default:form-title'
+    cell.formdef_reference = 'default:form-title'
     cell.save()
     site_export = [page.get_serialized_page()]
     cell.delete()
@@ -409,7 +407,7 @@ def test_category_cell_save_cache(mock_send):
     page.save()
     cell = WcsCategoryCell(page=page, placeholder='content', order=0)
     assert cell.get_additional_label() is None
-    cell.category_reference = u'default:test-3'
+    cell.category_reference = 'default:test-3'
     cell.save()
     assert cell.cached_title == 'Test 3'
     assert cell.get_additional_label() == 'Test 3'
@@ -423,7 +421,7 @@ def test_category_cell_validity(mock_send):
     assert validity_info.invalid_reason_code == 'wcs_category_not_defined'
     assert validity_info.invalid_since is not None
 
-    cell.category_reference = u'default:test-3'
+    cell.category_reference = 'default:test-3'
     cell.save()
     assert ValidityInfo.objects.exists() is False
 
@@ -459,7 +457,7 @@ def test_form_cell_render(mock_send):
     page = Page(title='xxx', slug='test_form_cell_render', template_name='standard')
     page.save()
     cell = WcsFormCell(page=page, placeholder='content', order=0)
-    cell.formdef_reference = u'default:form-title'
+    cell.formdef_reference = 'default:form-title'
     cell.save()
     result = cell.render({})
     assert 'http://127.0.0.1:8999/form-title/tryauth' in result
@@ -473,8 +471,8 @@ def test_current_forms_cell_setup(mock_send):
     form = form_class()
     assert form.fields['wcs_site'].widget.choices == [
         ('', 'All'),
-        (u'default', u'test'),
-        (u'other', u'test2'),
+        ('default', 'test'),
+        ('other', 'test2'),
     ]
     assert 'current_forms' in form.fields
     assert cell.get_additional_label() == 'All Sites - Current Forms'
@@ -726,8 +724,8 @@ def test_care_forms_cell_setup():
     form = form_class()
     assert form.fields['wcs_site'].widget.choices == [
         ('', 'All'),
-        (u'default', u'test'),
-        (u'other', u'test2'),
+        ('default', 'test'),
+        ('other', 'test2'),
     ]
     assert cell.get_additional_label() == 'All Sites'
     cell.wcs_site = 'default'
@@ -925,8 +923,8 @@ def test_forms_user_can_access_cell_setup():
     form = form_class()
     assert form.fields['wcs_site'].widget.choices == [
         ('', 'All'),
-        (u'default', u'test'),
-        (u'other', u'test2'),
+        ('default', 'test'),
+        ('other', 'test2'),
     ]
     assert cell.get_additional_label() == 'All Sites'
     cell.wcs_site = 'default'
@@ -1094,10 +1092,10 @@ def test_forms_of_category_cell_setup(mock_send):
     form_class = cell.get_default_form_class()
     form = form_class()
     assert form.fields['category_reference'].widget.choices == [
-        (u'default:test-3', u'test : Test 3'),
-        (u'default:test-9', u'test : Test 9'),
-        (u'other:test-3', u'test2 : Test 3'),
-        (u'other:test-9', u'test2 : Test 9'),
+        ('default:test-3', 'test : Test 3'),
+        ('default:test-9', 'test : Test 9'),
+        ('other:test-3', 'test2 : Test 3'),
+        ('other:test-9', 'test2 : Test 9'),
     ]
 
 
@@ -1568,7 +1566,7 @@ def test_cards_cell_load(mock_send):
 def test_cards_cell_render(mock_send, context):
     page = Page.objects.create(title='xxx', template_name='standard')
     cell = WcsCardsCell(page=page, placeholder='content', order=0)
-    cell.carddef_reference = u'default:card_model_1'
+    cell.carddef_reference = 'default:card_model_1'
     cell.save()
 
     # query should fail as nothing is cached
@@ -1602,7 +1600,7 @@ def test_cards_cell_render(mock_send, context):
     assert '<a href="/foo/12"><span class="card-title">bb</span></a>' in result
     assert '<a href="/foo/13"><span class="card-title">cc</span></a>' in result
 
-    cell.carddef_reference = u'default:card_model_1:foo'
+    cell.carddef_reference = 'default:card_model_1:foo'
     cell.save()
     page.sub_slug = 'card_model_1_id'
     page.save()
@@ -1666,7 +1664,7 @@ def test_cards_cell_only_for_user(mock_send, context):
 def test_cards_cell_render_user(mock_send, context, nocache):
     page = Page.objects.create(title='xxx', template_name='standard')
     cell = WcsCardsCell(page=page, placeholder='content', order=0)
-    cell.carddef_reference = u'default:card_model_1'
+    cell.carddef_reference = 'default:card_model_1'
     cell.save()
 
     context['card_model_1_id'] = 11
@@ -1869,7 +1867,7 @@ def test_card_cell_load(mock_send):
 def test_card_cell_render(mock_send, context):
     page = Page.objects.create(title='xxx', template_name='standard')
     cell = WcsCardInfosCell(page=page, placeholder='content', order=0)
-    cell.carddef_reference = u'default:card_model_1'
+    cell.carddef_reference = 'default:card_model_1'
     cell.custom_title = 'Foo bar {{ card.fields.title }}'
     cell.save()
 
@@ -1934,7 +1932,7 @@ def test_card_cell_render(mock_send, context):
 def test_card_cell_render_identifier(mock_send, context, nocache):
     page = Page.objects.create(title='xxx', template_name='standard')
     cell = WcsCardInfosCell(page=page, placeholder='content', order=0)
-    cell.carddef_reference = u'default:card_model_1'
+    cell.carddef_reference = 'default:card_model_1'
     cell.save()
 
     context['card_model_1_id'] = 11
@@ -1971,7 +1969,7 @@ def test_card_cell_render_identifier(mock_send, context, nocache):
 def test_card_cell_render_user(mock_send, context, nocache):
     page = Page.objects.create(title='xxx', template_name='standard')
     cell = WcsCardInfosCell(page=page, placeholder='content', order=0)
-    cell.carddef_reference = u'default:card_model_1'
+    cell.carddef_reference = 'default:card_model_1'
     cell.save()
 
     context['card_model_1_id'] = 11
@@ -2031,7 +2029,7 @@ def test_tracking_code_cell(app, nocache):
     assert requests_get.call_args_list[0][0][0] == '/api/code/FOOBAR'
     assert requests_get.call_args_list[1][0][0] == '/api/code/FOOBAR'
     remote_service_urls = [c[1]['remote_service']['url'] for c in requests_get.call_args_list]
-    assert set(remote_service_urls) == set(['http://127.0.0.1:8999/', 'http://127.0.0.2:8999/'])
+    assert set(remote_service_urls) == {'http://127.0.0.1:8999/', 'http://127.0.0.2:8999/'}
     assert resp.status_code == 302
     resp = resp.follow()
     assert '<li class="error">The tracking code could not been found.</li>' in resp.text
@@ -2130,7 +2128,7 @@ def test_tracking_code_cell(app, nocache):
 def test_cell_assets(mock_send, settings, app, admin_user):
     page = Page.objects.create(title='xxx', slug='test_cell_assets', template_name='standard')
     cell1 = WcsFormCell.objects.create(
-        page=page, placeholder='content', order=0, formdef_reference=u'default:form-title'
+        page=page, placeholder='content', order=0, formdef_reference='default:form-title'
     )
 
     cell2 = WcsFormsOfCategoryCell.objects.create(
@@ -2152,10 +2150,10 @@ def test_cell_assets(mock_send, settings, app, admin_user):
         'wcs_wcsformsofcategorycell': {'logo': {'prefix': 'Logo blabla', 'suffix': 'test'}},
     }
     resp = app.get('/manage/assets/')
-    assert u'Logo — %s' % cell2.get_label_for_asset() in resp.text
-    assert u'Logo blabla — %s' % cell2.get_label_for_asset() not in resp.text
-    assert u'Picture — %s' % cell1.get_label_for_asset() in resp.text
-    assert u'Picture blabla — %s' % cell1.get_label_for_asset() not in resp.text
+    assert 'Logo — %s' % cell2.get_label_for_asset() in resp.text
+    assert 'Logo blabla — %s' % cell2.get_label_for_asset() not in resp.text
+    assert 'Picture — %s' % cell1.get_label_for_asset() in resp.text
+    assert 'Picture blabla — %s' % cell1.get_label_for_asset() not in resp.text
     # New settings
     settings.WCS_CATEGORY_ASSET_SLOTS = {}
     settings.WCS_FORM_ASSET_SLOTS = {}
@@ -2164,8 +2162,8 @@ def test_cell_assets(mock_send, settings, app, admin_user):
         'wcs_wcsformsofcategorycell': {'logo': {'prefix': 'Logo'}},
     }
     resp = app.get('/manage/assets/')
-    assert u'Logo — %s' % cell2.get_label_for_asset() in resp.text
-    assert u'Picture — %s' % cell1.get_label_for_asset() in resp.text
+    assert 'Logo — %s' % cell2.get_label_for_asset() in resp.text
+    assert 'Picture — %s' % cell1.get_label_for_asset() in resp.text
 
     # test suffix
     settings.COMBO_CELL_ASSET_SLOTS = {
@@ -2173,8 +2171,8 @@ def test_cell_assets(mock_send, settings, app, admin_user):
         'wcs_wcsformsofcategorycell': {'logo': {'prefix': 'Logo', 'suffix': 'test'}},
     }
     resp = app.get('/manage/assets/')
-    assert u'Logo — %s (test)' % cell2.get_label_for_asset() in resp.text
-    assert u'Picture — %s (test)' % cell1.get_label_for_asset() in resp.text
+    assert 'Logo — %s (test)' % cell2.get_label_for_asset() in resp.text
+    assert 'Picture — %s (test)' % cell1.get_label_for_asset() in resp.text
 
 
 def test_tracking_code_search(settings, app, nocache):
@@ -2202,7 +2200,7 @@ def test_tracking_code_search(settings, app, nocache):
     assert requests_get.call_args_list[0][0][0] == '/api/code/BBCCDDFF'
     assert requests_get.call_args_list[1][0][0] == '/api/code/BBCCDDFF'
     remote_service_urls = [c[1]['remote_service']['url'] for c in requests_get.call_args_list]
-    assert set(remote_service_urls) == set(['http://127.0.0.1:8999/', 'http://127.0.0.2:8999/'])
+    assert set(remote_service_urls) == {'http://127.0.0.1:8999/', 'http://127.0.0.2:8999/'}
 
     with mock.patch('combo.apps.wcs.models.requests.get') as requests_get:
         requests_get.return_value = MockedRequestResponse(
@@ -2431,7 +2429,7 @@ def test_manager_add_edit_delete_list_link_item(mock_send, app, admin_user):
 
 
 def test_import_export_pages_with_links():
-    page = Page(title=u'bar', slug='bar', order=1)
+    page = Page(title='bar', slug='bar', order=1)
     page.save()
 
     cell = LinkListCell.objects.create(order=0, placeholder='content', page=page)
@@ -2478,7 +2476,7 @@ def test_list_of_links_with_form_render(app):
 
 
 def test_view_page_with_wcs_cells_num_queries(app, admin_user):
-    page = Page.objects.create(title=u'bar', slug='index', order=1)
+    page = Page.objects.create(title='bar', slug='index', order=1)
     for i in range(0, 15):
         WcsCurrentDraftsCell.objects.create(page=page, placeholder='content', order=i)
     for i in range(15, 50):
diff --git a/tests/test_wcs_templatetags.py b/tests/test_wcs_templatetags.py
index 94589c4f..52c126ff 100644
--- a/tests/test_wcs_templatetags.py
+++ b/tests/test_wcs_templatetags.py
@@ -1,9 +1,7 @@
-# -*- coding: utf-8 -*-
-
 import copy
 import json
+from unittest import mock
 
-import mock
 import pytest
 from django.template import Context, Template
 from django.test.client import RequestFactory
@@ -25,12 +23,12 @@ def context():
     return ctx
 
 
-class MockAnonymousUser(object):
+class MockAnonymousUser:
     is_authenticated = False
     is_anonymous = True
 
 
-class MockUser(object):
+class MockUser:
     email = 'foo@example.net'
     is_authenticated = True
     is_anonymous = False
@@ -39,7 +37,7 @@ class MockUser(object):
         return None
 
 
-class MockUserWithNameId(object):
+class MockUserWithNameId:
     email = 'foo@example.net'
     is_authenticated = True
     is_anonymous = False
-- 
2.30.2

