diff --git a/ckanext/odsh/collection/controller.py b/ckanext/odsh/collection/controller.py
index 86141f3ca13fc40db37c1743061227a31aee2cd5..152651b578b10c63d8cb42a3d14a8efa8976a29e 100644
--- a/ckanext/odsh/collection/controller.py
+++ b/ckanext/odsh/collection/controller.py
@@ -1,7 +1,7 @@
 from ckan.lib.helpers import is_url, url_for
 import ckan.plugins.toolkit as toolkit
 from ckan.controllers.package import PackageController
-from helpers import get_latest_resources_for_format, get_latest_dataset
+from .helpers import get_latest_resources_for_format, get_latest_dataset
 
 
 
diff --git a/ckanext/odsh/collection/helpers.py b/ckanext/odsh/collection/helpers.py
index 82edf228b576b606640fb11fba5d98d00cf4b047..fcd73083ce1c70ecf4f9bc8db9d7f26b986c58e5 100644
--- a/ckanext/odsh/collection/helpers.py
+++ b/ckanext/odsh/collection/helpers.py
@@ -1,4 +1,3 @@
-from string import lower
 from operator import itemgetter
 
 import ckan.lib.helpers as helpers
diff --git a/ckanext/odsh/collection/plugin.py b/ckanext/odsh/collection/plugin.py
index 27a28eccf53d86da24b79a31732bf10c37dc8ff5..021ac971b8fdd044aa8714f04484ef1f817df48f 100644
--- a/ckanext/odsh/collection/plugin.py
+++ b/ckanext/odsh/collection/plugin.py
@@ -2,7 +2,7 @@
 from ckan.lib.plugins import DefaultTranslation, DefaultDatasetForm
 import ckan.plugins as plugins
 import ckan.plugins.toolkit as toolkit
-import helpers as collection_helpers
+from . import helpers as collection_helpers
 from routes.mapper import SubMapper
 
 class CollectionsPlugin(plugins.SingletonPlugin, DefaultDatasetForm):
diff --git a/ckanext/odsh/commands/initialization.py b/ckanext/odsh/commands/initialization.py
index 3463fe9ee329a57bfc8f28db259cfcd6f6f731b9..f7c9b881ba285ed88e07e91fcc568fda3ee2ace4 100755
--- a/ckanext/odsh/commands/initialization.py
+++ b/ckanext/odsh/commands/initialization.py
@@ -38,7 +38,7 @@ class Initialization(CkanCommand):
         if len(self.args) > 0:
             cmd = self.args[0]
 
-            print('Command %s not recognized' % cmd)
+            print(('Command %s not recognized' % cmd))
             self.parser.print_usage()
             sys.exit(1)
 
@@ -58,37 +58,37 @@ class Initialization(CkanCommand):
 
         odsh_orgs = {
             "landeshauptstadt-kiel": {
-                "title": u"Landeshauptstadt Kiel",
-                "image": u"https://www.kiel.de/images/logo-kiel-sailing-city.svg",
-                "description": u"Die Stadt Kiel ist die nördlichste Großstadt Deutschlands und die Landeshauptstadt"
-                               u" Schleswig-Holsteins. Als kreisfreie Stadt erledigt sie neben den kommunalen"
-                               u" Selbstverwaltungsaufgaben auch Aufgaben einer unteren staatlichen Verwaltungsbehörde"
-                               u" und erzeugt, erhebt und verarbeitet als Gebietskörperschaft eine Vielzahl von Daten"
-                               u" mit lokalem Bezug."
+                "title": "Landeshauptstadt Kiel",
+                "image": "https://www.kiel.de/images/logo-kiel-sailing-city.svg",
+                "description": "Die Stadt Kiel ist die nördlichste Großstadt Deutschlands und die Landeshauptstadt"
+                               " Schleswig-Holsteins. Als kreisfreie Stadt erledigt sie neben den kommunalen"
+                               " Selbstverwaltungsaufgaben auch Aufgaben einer unteren staatlichen Verwaltungsbehörde"
+                               " und erzeugt, erhebt und verarbeitet als Gebietskörperschaft eine Vielzahl von Daten"
+                               " mit lokalem Bezug."
             },
             "statistikamt-nord": {
-                "title": u"Statistisches Amt für Hamburg und Schleswig-Holstein - Anstalt des öffentlichen Rechts - "
-                         u"(Statistikamt Nord)",
-                "image": u"https://www.statistik-nord.de/static/img/logo-text.svg",
-                "description": u"Das Statistische Amt für Hamburg und Schleswig-Holstein – das Statistikamt Nord – "
-                               u"erhebt und veröffentlicht als Teil der amtlichen Statistik in Deutschland statistische"
-                               u" Informationen zu allen gesellschaftlichen Themen für die Bundesländer Hamburg und"
-                               u" Schleswig-Holstein. Als Anstalt des öffentlichen Rechts führt es alle durch Bundes-"
-                               u" und EU- Gesetze angeordneten Statistiken im Auftrag der Trägerländer Hamburg und"
-                               u" Schleswig-Holstein für die beiden Bundesländer durch, bereitet die Daten auf und"
-                               u" interpretiert die Ergebnisse. Die objektiv und unabhängig erstellten Statistiken"
-                               u" werden Verwaltung, Politik, Medien sowie Bürgerinnen und Bürgern gleichermaßen"
-                               u" zugänglich gemacht. Darüber hinaus bietet das Amt Dienstleistungen im Bereich"
-                               u" Datenerhebung, -aufbereitung und -analyse nach individuellem Kundenwunsch an."
-                               u" Das Statistikamt Nord ist hervorgegangen aus den vormaligen Statistischen"
-                               u" Landesämtern Hamburg und Schleswig-Holstein. Seit 2004 firmiert es als"
-                               u" länderübergreifende Anstalt an den Standorten Hamburg und Kiel."
+                "title": "Statistisches Amt für Hamburg und Schleswig-Holstein - Anstalt des öffentlichen Rechts - "
+                         "(Statistikamt Nord)",
+                "image": "https://www.statistik-nord.de/static/img/logo-text.svg",
+                "description": "Das Statistische Amt für Hamburg und Schleswig-Holstein – das Statistikamt Nord – "
+                               "erhebt und veröffentlicht als Teil der amtlichen Statistik in Deutschland statistische"
+                               " Informationen zu allen gesellschaftlichen Themen für die Bundesländer Hamburg und"
+                               " Schleswig-Holstein. Als Anstalt des öffentlichen Rechts führt es alle durch Bundes-"
+                               " und EU- Gesetze angeordneten Statistiken im Auftrag der Trägerländer Hamburg und"
+                               " Schleswig-Holstein für die beiden Bundesländer durch, bereitet die Daten auf und"
+                               " interpretiert die Ergebnisse. Die objektiv und unabhängig erstellten Statistiken"
+                               " werden Verwaltung, Politik, Medien sowie Bürgerinnen und Bürgern gleichermaßen"
+                               " zugänglich gemacht. Darüber hinaus bietet das Amt Dienstleistungen im Bereich"
+                               " Datenerhebung, -aufbereitung und -analyse nach individuellem Kundenwunsch an."
+                               " Das Statistikamt Nord ist hervorgegangen aus den vormaligen Statistischen"
+                               " Landesämtern Hamburg und Schleswig-Holstein. Seit 2004 firmiert es als"
+                               " länderübergreifende Anstalt an den Standorten Hamburg und Kiel."
             },
             "landesamt-fur-soziale-dienste": {
-                "title": u"Landesamt für soziale Dienste",
+                "title": "Landesamt für soziale Dienste",
                 "image": None,
-                "description": u"Das Landesamt für soziale Dienste ist eine obere Landesbehörde des Landes"
-                               u" Schleswig-Holstein."
+                "description": "Das Landesamt für soziale Dienste ist eine obere Landesbehörde des Landes"
+                               " Schleswig-Holstein."
             }
         }
 
@@ -113,7 +113,7 @@ class Initialization(CkanCommand):
             else:
                 skip_message = 'Skipping creation of organization '
                 skip_message = skip_message + "{org_title}, as it's already present."
-                print(skip_message.format(org_title=title.encode('utf8')))
+                print((skip_message.format(org_title=title.encode('utf8'))))
 
     def _handle_harvesters(self, ckan_api_client):
         data_dict = {}
@@ -124,46 +124,46 @@ class Initialization(CkanCommand):
 
         odsh_harvesters = {
             "Landeshauptstadt Kiel": {
-                'name': u"landeshauptstadt-kiel",
-                'url': u"https://www.kiel.de/de/kiel_zukunft/statistik_kieler_zahlen/open_data/Kiel_open_data.json",
-                'source_type': u"kiel",
+                'name': "landeshauptstadt-kiel",
+                'url': "https://www.kiel.de/de/kiel_zukunft/statistik_kieler_zahlen/open_data/Kiel_open_data.json",
+                'source_type': "kiel",
                 'title': "Landeshauptstadt Kiel",
                 'active': True,
                 'owner_org': "landeshauptstadt-kiel",
                 'frequency': "MANUAL",
-                'notes': u"Die Stadt Kiel ist die nördlichste Großstadt Deutschlands und die Landeshauptstadt"
-                         u" Schleswig-Holsteins. Als kreisfreie Stadt erledigt sie neben den kommunalen"
-                         u" Selbstverwaltungsaufgaben auch Aufgaben einer unteren staatlichen Verwaltungsbehörde und"
-                         u" erzeugt, erhebt und verarbeitet als Gebietskörperschaft eine Vielzahl von Daten mit lokalem"
-                         u" Bezug."
+                'notes': "Die Stadt Kiel ist die nördlichste Großstadt Deutschlands und die Landeshauptstadt"
+                         " Schleswig-Holsteins. Als kreisfreie Stadt erledigt sie neben den kommunalen"
+                         " Selbstverwaltungsaufgaben auch Aufgaben einer unteren staatlichen Verwaltungsbehörde und"
+                         " erzeugt, erhebt und verarbeitet als Gebietskörperschaft eine Vielzahl von Daten mit lokalem"
+                         " Bezug."
             },
             "Statistisches Amt für Hamburg und Schleswig-Holstein - Anstalt des öffentlichen Rechts - "
             "(Statistikamt Nord)": {
-                'name': u"statistikamt-nord",
-                'url': u"http://www.statistik-nord.de/index.php?eID=stan_xml&products=4,6&state=2",
-                'source_type': u"statistikamt-nord",
-                'title': u"Statistisches Amt für Hamburg und Schleswig-Holstein - Anstalt des öffentlichen Rechts - "
-                         u"(Statistikamt Nord)",
+                'name': "statistikamt-nord",
+                'url': "http://www.statistik-nord.de/index.php?eID=stan_xml&products=4,6&state=2",
+                'source_type': "statistikamt-nord",
+                'title': "Statistisches Amt für Hamburg und Schleswig-Holstein - Anstalt des öffentlichen Rechts - "
+                         "(Statistikamt Nord)",
                 'active': True,
                 'owner_org': "statistikamt-nord",
                 'frequency': "MANUAL",
-                'notes': u"Das Statistische Amt für Hamburg und Schleswig-Holstein – das Statistikamt Nord – erhebt und"
-                         u" veröffentlicht als Teil der amtlichen Statistik in Deutschland statistische Informationen"
-                         u" zu allen gesellschaftlichen Themen für die Bundesländer Hamburg und Schleswig-Holstein. Als"
-                         u" Anstalt des öffentlichen Rechts führt es alle durch Bundes- und EU- Gesetze angeordneten "
-                         u"Statistiken im Auftrag der Trägerländer Hamburg und Schleswig-Holstein für die beiden "
-                         u"Bundesländer durch, bereitet die Daten auf und interpretiert die Ergebnisse. Die objektiv "
-                         u"und unabhängig erstellten Statistiken werden Verwaltung, Politik, Medien sowie Bürgerinnen "
-                         u"und Bürgern gleichermaßen zugänglich gemacht. Darüber hinaus bietet das Amt Dienstleistungen"
-                         u" im Bereich Datenerhebung, -aufbereitung und -analyse nach individuellem Kundenwunsch an. "
-                         u"Das Statistikamt Nord ist hervorgegangen aus den vormaligen Statistischen Landesämtern "
-                         u"Hamburg und Schleswig-Holstein. Seit 2004 firmiert es als länderübergreifende Anstalt an den"
-                         u" Standorten Hamburg und Kiel."
+                'notes': "Das Statistische Amt für Hamburg und Schleswig-Holstein – das Statistikamt Nord – erhebt und"
+                         " veröffentlicht als Teil der amtlichen Statistik in Deutschland statistische Informationen"
+                         " zu allen gesellschaftlichen Themen für die Bundesländer Hamburg und Schleswig-Holstein. Als"
+                         " Anstalt des öffentlichen Rechts führt es alle durch Bundes- und EU- Gesetze angeordneten "
+                         "Statistiken im Auftrag der Trägerländer Hamburg und Schleswig-Holstein für die beiden "
+                         "Bundesländer durch, bereitet die Daten auf und interpretiert die Ergebnisse. Die objektiv "
+                         "und unabhängig erstellten Statistiken werden Verwaltung, Politik, Medien sowie Bürgerinnen "
+                         "und Bürgern gleichermaßen zugänglich gemacht. Darüber hinaus bietet das Amt Dienstleistungen"
+                         " im Bereich Datenerhebung, -aufbereitung und -analyse nach individuellem Kundenwunsch an. "
+                         "Das Statistikamt Nord ist hervorgegangen aus den vormaligen Statistischen Landesämtern "
+                         "Hamburg und Schleswig-Holstein. Seit 2004 firmiert es als länderübergreifende Anstalt an den"
+                         " Standorten Hamburg und Kiel."
             }
         }
 
         for harvester_key in odsh_harvesters:
-            if unicode(harvester_key, "utf-8") not in present_harvesters_list:
+            if str(harvester_key, "utf-8") not in present_harvesters_list:
                 add_message = 'Adding harvester {harvester_key}.'.format(
                     harvester_key=harvester_key
                 )
@@ -174,7 +174,7 @@ class Initialization(CkanCommand):
             else:
                 skip_message = 'Skipping creation of harvester '
                 skip_message = skip_message + "{harvester_key}, as it's already present."
-                print(skip_message.format(harvester_key=harvester_key))
+                print((skip_message.format(harvester_key=harvester_key)))
 
     def _create_and_purge_organization(self, organization_dict):
         """Worker method for the actual organization addition.
diff --git a/ckanext/odsh/controller.py b/ckanext/odsh/controller.py
index b296259547a5a6d4ca3a34c21bb532dd93fb2683..8a0a8991f22adcd258a22ab0c9095355c018a3e2 100644
--- a/ckanext/odsh/controller.py
+++ b/ckanext/odsh/controller.py
@@ -12,14 +12,14 @@ from ckan.controllers.feed import FeedController, ITEMS_LIMIT, _package_search,
 import ckan.lib.helpers as h
 import ckan.authz as authz
 import logging
-import matomo
+from . import matomo
 import ckan.logic as logic
 from ckan.common import c, request, config
 import hashlib
 import ckan.plugins.toolkit as toolkit
 from ckanext.dcat.controllers import DCATController
 import ckan.model as model
-import helpers
+from . import helpers
 import json
 
 
@@ -103,13 +103,13 @@ class OdshGroupController(OrganizationController):
         action = super(OdshGroupController, self)._action(name)
 
         def custom_org_list(context, data_dict):
-            sort_desc = data_dict['sort'] == u'name desc'
+            sort_desc = data_dict['sort'] == 'name desc'
             d = data_dict.copy()
             if 'offset' in d:
                 del d['offset']
                 del d['limit']
             # print(data_dict)
-            if d["type"] is not 'organization':
+            if d["type"] != 'organization':
                 return action(context, d)
             all = d['all_fields']
             query = d['q']
@@ -136,7 +136,7 @@ class OdshGroupController(OrganizationController):
                 return result[off:off+limit]
             return result 
 
-        if name is 'group_list':
+        if name == 'group_list':
             return custom_org_list
         else:
             return super(OdshGroupController, self)._action(name)
@@ -148,7 +148,7 @@ class OdshApiController(ApiController):
             try:
                 request_data = self._get_request_data(try_url_params=False)
                 log.info('POST request body: {}'.format(request_data))
-            except Exception, e:
+            except Exception as e:
                 log.error(e)
         if logic_function == 'resource_qv4yAI2rgotamXGk98gJ':
             return helpers.odsh_get_version_id()
@@ -176,7 +176,7 @@ class OdshApiController(ApiController):
             else:
                 matomo.create_matomo_request()
 
-        except Exception, e:
+        except Exception as e:
             log.error(e)
 
         return ApiController.action(self, logic_function, ver)
@@ -193,11 +193,11 @@ class OdshFeedController(FeedController):
         matomo.create_matomo_request()
         extra_fields = ['ext_startdate', 'ext_enddate',
                         'ext_bbox', 'ext_prev_extent']
-        q = request.params.get('q', u'')
+        q = request.params.get('q', '')
         fq = ''
         search_params = {}
         extras = {}
-        for (param, value) in request.params.items():
+        for (param, value) in list(request.params.items()):
             if param not in ['q', 'page', 'sort'] + extra_fields \
                     and len(value) and not param.startswith('_'):
                 search_params[param] = value
@@ -231,15 +231,15 @@ class OdshFeedController(FeedController):
                                   action='custom')
 
         atom_url = h._url_with_params('/feeds/custom.atom',
-                                      search_params.items())
+                                      list(search_params.items()))
 
         alternate_url = self._alternate_url(request.params)
 
         site_title = config.get('ckan.site_title', 'CKAN')
 
         return self.output_feed(results,
-                                feed_title=u'%s - Custom query' % site_title,
-                                feed_description=u'Recently created or updated'
+                                feed_title='%s - Custom query' % site_title,
+                                feed_description='Recently created or updated'
                                 ' datasets on %s. Custom query: \'%s\'' %
                                 (site_title, q),
                                 feed_link=alternate_url,
@@ -258,12 +258,12 @@ class MetaClass(type):
     def __new__(meta, classname, bases, classDict):
         newClassDict = {}
         wdec = decorator.decorator(only_admin)
-        for attributeName, attribute in bases[0].__dict__.items():
+        for attributeName, attribute in list(bases[0].__dict__.items()):
             if isinstance(attribute, FunctionType) and not attributeName.startswith('_'):
                 attribute = wdec(attribute)
             newClassDict[attributeName] = attribute
         return type.__new__(meta, classname, bases, newClassDict)
 
 
-class OdshHarvestController(HarvestController):
-    __metaclass__ = MetaClass  # wrap all the methods
+class OdshHarvestController(HarvestController, metaclass=MetaClass):
+    pass
diff --git a/ckanext/odsh/harvesters/kielharvester.py b/ckanext/odsh/harvesters/kielharvester.py
index c1dfb652a9a2703b4dbd579cb38413298fad5298..0970db9bd524fdb73ee50921193bd4755da9cd33 100755
--- a/ckanext/odsh/harvesters/kielharvester.py
+++ b/ckanext/odsh/harvesters/kielharvester.py
@@ -40,9 +40,9 @@ class KielHarvester(ODSHBaseHarvester):
         try:
             used_identifiers = []
             ids = []
-            package_ids_in_db = list(map(lambda x: x[0], model.Session.query(HarvestObject.guid)
+            package_ids_in_db = list([x[0] for x in model.Session.query(HarvestObject.guid)
                                 .filter(HarvestObject.current == True)
-                                .filter(HarvestObject.harvest_source_id == harvest_job.source.id).all()))
+                                .filter(HarvestObject.harvest_source_id == harvest_job.source.id).all()])
             log.info("Package IDs in DB: %s" % str(package_ids_in_db))
             for dataset in datasets:
                 guid = str(uuid.uuid3(uuid.NAMESPACE_URL,
diff --git a/ckanext/odsh/harvesters/statistikamtnordharvester.py b/ckanext/odsh/harvesters/statistikamtnordharvester.py
index 64ffbc985f33282bca88dce18e7310e539576df8..39fff8e2f254ac91e68ef34249601abd30226238 100755
--- a/ckanext/odsh/harvesters/statistikamtnordharvester.py
+++ b/ckanext/odsh/harvesters/statistikamtnordharvester.py
@@ -1,7 +1,7 @@
 # This Python file uses the following encoding: utf-8
 
 import json
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 import traceback
 
 from lxml import etree
@@ -45,7 +45,7 @@ class StatistikamtNordHarvester(ODSHBaseHarvester):
             documents_list = self.get_documents_from_content(fetched_documents)
             documents = documents_list['RegistereintragsListe']
 
-        except Exception, e:
+        except Exception as e:
             log.error('traceback while reading model: %s' % traceback.format_exc())
             self._save_gather_error('Statistik-Nord-Harvester: Error while reading model [%r]' % e, harvest_job)
             return False
@@ -53,9 +53,9 @@ class StatistikamtNordHarvester(ODSHBaseHarvester):
         try:
             used_identifiers = []
             ids = []
-            package_ids_in_db = list(map(lambda x: x[0], model.Session.query(HarvestObject.guid) \
+            package_ids_in_db = list([x[0] for x in model.Session.query(HarvestObject.guid) \
                                          .filter(HarvestObject.current == True) \
-                                         .filter(HarvestObject.harvest_source_id == harvest_job.source.id).all()))
+                                         .filter(HarvestObject.harvest_source_id == harvest_job.source.id).all()])
             log.info("Package IDs in DB: %s" % str(package_ids_in_db))
             for document in documents:
                 try:
@@ -83,13 +83,13 @@ class StatistikamtNordHarvester(ODSHBaseHarvester):
                     else:
                         count_known_dataset_ids += 1
 
-                except Exception, e:
+                except Exception as e:
                     log.error('traceback: %s' % traceback.format_exc())
                     self._save_gather_error(
                         'Statistik-Nord-Harvester: Error for the identifier %s [%r]' % (identifier, e), harvest_job)
                     continue
 
-        except Exception, e:
+        except Exception as e:
             self._save_gather_error(
                 'Statistik-Nord-Harvester: Error gathering the identifiers from the source server [%s]' % str(e),
                 harvest_job)
@@ -121,13 +121,13 @@ class StatistikamtNordHarvester(ODSHBaseHarvester):
             'return_id_only': True,
             'ignore_auth': True
         }
-        log.debug("Context: " + str(context.viewitems()))
+        log.debug("Context: " + str(context.items()))
         if not harvest_object:
             log.error('Statistik-Nord-Harvester: No harvest object received')
             return False
 
         if harvest_object.content is None:
-            self._save_object_error('Empty content for object %s' % harvest_object.id, harvest_object, u'Import')
+            self._save_object_error('Empty content for object %s' % harvest_object.id, harvest_object, 'Import')
             return False
         else:
             log.debug("Harvest Object: " + str(harvest_object))
@@ -176,7 +176,7 @@ class StatistikamtNordHarvester(ODSHBaseHarvester):
             self._handle_current_harvest_object(harvest_object, harvest_object.guid)
             result = toolkit.get_action('package_create')(context, package_dict)
             return result
-        except toolkit.ValidationError, e:
+        except toolkit.ValidationError as e:
             self._save_object_error('Validation Error: %s' % str(e.error_summary), harvest_object, 'Import')
             return False
 
@@ -222,7 +222,7 @@ class StatistikamtNordHarvester(ODSHBaseHarvester):
             resource_dict['format'] = resource['Format'].get('FormatTyp', "")
             resource_dict['url'] = resource['URLZumDownload']
             if resource['Dateigroesse'] == "0" or len(resource['Dateigroesse']) == 0:
-                resource_file = urllib2.urlopen(resource['url'])
+                resource_file = urllib.request.urlopen(resource['url'])
                 resource_dict['file_size'] = resource_file['Content-Length']
             else:
                 file_size = int(round(float(resource['Dateigroesse']) * 1048576 ))
@@ -238,7 +238,7 @@ class StatistikamtNordHarvester(ODSHBaseHarvester):
         # get the code
         code = values['StANKategorie']
 
-        if dcat_theme.has_key(str(code)):
+        if str(code) in dcat_theme:
                 for item in dcat_theme[str(code)]:
                     package_dict['groups'].append({'name': item})
         else:
@@ -248,10 +248,10 @@ class StatistikamtNordHarvester(ODSHBaseHarvester):
     def _get_content(url):
         url = url.replace(' ', '%20')
         try:
-            http_response = urllib2.urlopen(url, timeout=100000)
+            http_response = urllib.request.urlopen(url, timeout=100000)
             content = http_response.read()
             return content
-        except Exception, e:
+        except Exception as e:
             log.error('traceback WebHarvester could not get content!: %s' % traceback.format_exc())
             log.debug("Error in _get_content %s" % e)
             raise e
diff --git a/ckanext/odsh/helper_pkg_dict.py b/ckanext/odsh/helper_pkg_dict.py
index 53553613f31dafec88c4f26791e97ec2777d00d7..26f7917c6ef77f4368c9f8581fc411fee48e0e57 100644
--- a/ckanext/odsh/helper_pkg_dict.py
+++ b/ckanext/odsh/helper_pkg_dict.py
@@ -27,7 +27,7 @@ class HelperPgkDict(object):
         return True if self.pkg_dict['type'] is collection,
         false otherwise
         '''
-        dataset_type = self.pkg_dict.get(u'type')
+        dataset_type = self.pkg_dict.get('type')
         return dataset_type == 'collection'
     
     def shall_be_part_of_collection(self):
diff --git a/ckanext/odsh/helpers.py b/ckanext/odsh/helpers.py
index 0baa72be57a524c65f8e14115b6471e102ca7bd9..eeb3644a83ecb2adc8e83e90138781622c7603a9 100644
--- a/ckanext/odsh/helpers.py
+++ b/ckanext/odsh/helpers.py
@@ -13,14 +13,14 @@ from ckan.common import c
 import datetime
 from dateutil import parser
 from ckan.common import config
-import urllib
+import urllib.request, urllib.parse, urllib.error
 import hashlib
 import re
 import csv
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 from ckan.common import request
 import pdb
-from urlparse import urlsplit, urlunsplit
+from urllib.parse import urlsplit, urlunsplit
 import subprocess
 import ckan.lib.helpers as helpers
 import os.path
@@ -42,16 +42,16 @@ def odsh_openness_score_dataset_html(dataset):
             try:
                 qa = None
                 # r_qa might be a string of a dictionary when 'dataset' is send from solr
-                if isinstance(r_qa, basestring):
+                if isinstance(r_qa, str):
                     qa = ast.literal_eval(r_qa)
                 else:
                     qa = r_qa
                 resource_score = qa.get('openness_score')
                 if resource_score > score:
                     score = resource_score
-            except AttributeError, e:
+            except AttributeError as e:
                 log.error('Error while calculating openness score %s: %s\nException: %s',
-                          e.__class__.__name__,  unicode(e), traceback.format_exc())
+                          e.__class__.__name__,  str(e), traceback.format_exc())
     return score
 
 
@@ -84,9 +84,9 @@ def odsh_get_bounding_box(pkg_dict):
             if 'coordinates' in d:
                 coords = d['coordinates']
                 return compute_bounding_box(coords)
-    except Exception, e:
+    except Exception as e:
         log.error('Error while bounding box %s: %s\nException: %s',
-                  e.__class__.__name__,  unicode(e), traceback.format_exc())
+                  e.__class__.__name__,  str(e), traceback.format_exc())
     return None
 
 
@@ -162,7 +162,7 @@ def odsh_tracking_url():
     return config.get('ckanext.odsh.matomo_url')
 
 def odsh_encodeurl(url):
-    return urllib.quote(url, safe='')
+    return urllib.parse.quote(url, safe='')
 
 
 def odsh_create_checksum(in_string):
@@ -202,7 +202,7 @@ def presorted_license_options(existing_license_id=None):
     offered. Always includes the existing_license_id, if supplied.
     '''
     register = model.Package.get_license_register()
-    licenses = register.values()
+    licenses = list(register.values())
     license_ids = [license.id for license in licenses]
     if existing_license_id and existing_license_id not in license_ids:
         license_ids.insert(0, existing_license_id)
@@ -217,7 +217,7 @@ def odsh_has_more_facets(facet, limit=None, exclude_active=False):
     for facet_item in c.search_facets.get(facet)['items']:
         if not len(facet_item['name'].strip()) or facet_item['count']==0:
             continue
-        if not (facet, facet_item['name']) in request.params.items():
+        if not (facet, facet_item['name']) in list(request.params.items()):
             facets.append(dict(active=False, **facet_item))
         elif not exclude_active:
             facets.append(dict(active=True, **facet_item))
@@ -235,7 +235,7 @@ def spatial_extends_available():
 
     mapping_file = config.get('ckanext.odsh.spatial.mapping')
     try:
-        mapping_file = urllib2.urlopen(mapping_file)
+        mapping_file = urllib.request.urlopen(mapping_file)
     except Exception:
         raise Exception("Could not load spatial mapping file!")
 
@@ -309,7 +309,7 @@ def odsh_group_id_selected(selected, group_id):
     if type(selected) is not list:
         selected = [selected]
     for g in selected:
-        if (isinstance(g, basestring) and group_id == g) or (type(g) is dict and group_id == g['id']):
+        if (isinstance(g, str) and group_id == g) or (type(g) is dict and group_id == g['id']):
             return True
 
     return False
@@ -412,7 +412,7 @@ def _get_siblings_dicts_with_access(context, pkg_dict):
     if n_siblings>0:
         siblings_dicts = [get_package_dict(name) for name in list_of_siblings]
         user_has_access = lambda pkg_dict:helpers.check_access('package_show', pkg_dict)
-        siblings_dicts_with_access = filter(user_has_access, siblings_dicts)
+        siblings_dicts_with_access = list(filter(user_has_access, siblings_dicts))
         return siblings_dicts_with_access
     return None
 
@@ -443,19 +443,19 @@ def tpsh_get_successor_and_predecessor_urls(context, pkg_dict):
 
 def short_name_for_category(category_name):
     translations = {
-        'soci': u'Bevölkerung',
-        'educ': u'Bildung',
-        'ener': u'Energie',
-        'heal': u'Gesundheit',
-        'intr': u'Internationales',
-        'just': u'Justiz',
-        'agri': u'Landwirtschaft',
-        'gove': u'Regierung',
-        'regi': u'Regionales',
-        'envi': u'Umwelt',
-        'tran': u'Verkehr',
-        'econ': u'Wirtschaft',
-        'tech': u'Wissenschaft',
+        'soci': 'Bevölkerung',
+        'educ': 'Bildung',
+        'ener': 'Energie',
+        'heal': 'Gesundheit',
+        'intr': 'Internationales',
+        'just': 'Justiz',
+        'agri': 'Landwirtschaft',
+        'gove': 'Regierung',
+        'regi': 'Regionales',
+        'envi': 'Umwelt',
+        'tran': 'Verkehr',
+        'econ': 'Wirtschaft',
+        'tech': 'Wissenschaft',
     }
     return translations.get(category_name)
 
@@ -475,7 +475,7 @@ def odsh_load_mdk_sample_dataset():
     try:
         with open(sample_data_file_path) as mapping_json:
              MDK_MAPPING = json.loads(mapping_json.read(), object_pairs_hook=OrderedDict)
-             default = [{'value': u'Musterdatensatz wählen..', 'key': u''}]
+             default = [{'value': 'Musterdatensatz wählen..', 'key': ''}]
              mdk = [{'key': key, 'value': MDK_MAPPING[key]} for key in MDK_MAPPING]
              result = default+mdk
     except IOError as err:
diff --git a/ckanext/odsh/helpers_tpsh.py b/ckanext/odsh/helpers_tpsh.py
index a6345f3771cfcc7a7e1834b8968898fcbd9a7a3f..4528e1e4a3a15d951b2518749f3a9ee723f9f868 100644
--- a/ckanext/odsh/helpers_tpsh.py
+++ b/ckanext/odsh/helpers_tpsh.py
@@ -3,10 +3,9 @@
 import csv
 import datetime
 import logging
-from string import lower
 import json
 import re
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 from collections import OrderedDict
 import subprocess
 import os
@@ -39,7 +38,7 @@ def map_ckan_type_to_dct_type(ckan_type):
     return dct_type
 
 def _revert_dict(d):
-    d_inverse = {v: k for k, v in d.iteritems()}
+    d_inverse = {v: k for k, v in d.items()}
     return d_inverse
 
 def add_pkg_to_collection(id_pkg, id_collection):
@@ -145,8 +144,8 @@ def _get_language_id(pkg_dict):
 def get_spatial_for_selection():
     mapping_path = config.get('ckanext.odsh.spatial.mapping')
     try:
-        mapping_file = urllib2.urlopen(mapping_path)
-    except urllib2.URLError:
+        mapping_file = urllib.request.urlopen(mapping_path)
+    except urllib.error.URLError:
         log.error('Could not load spatial mapping file')
         raise
     cr = csv.reader(mapping_file, delimiter="\t")
diff --git a/ckanext/odsh/lib/odsh_icap_client.py b/ckanext/odsh/lib/odsh_icap_client.py
index df3df673829d5ae31c74c860da45ff0f01ffb68f..fac8b274deefe32dc907048a08231e1514590cb3 100644
--- a/ckanext/odsh/lib/odsh_icap_client.py
+++ b/ckanext/odsh/lib/odsh_icap_client.py
@@ -24,7 +24,7 @@ class ODSHICAPRequest(object):
             self.HOST = _read_from_config('ckanext.odsh.icap.host')
             self.PORT = toolkit.asint(_read_from_config('ckanext.odsh.icap.port'))
             self.CLIENTIP = _read_from_config('ckanext.odsh.icap.clientip')
-        except KeyError, e:
+        except KeyError as e:
             log.error(e)
         self.FILENAME = FILENAME
         self.FILEBUFF = FILEBUFF
@@ -90,7 +90,7 @@ class ODSHICAPRequest(object):
 
         l = fileBuffer.read(PACK_SIZE)
         while(l):
-            print('sending %d bytes of data...' % len(l))
+            print(('sending %d bytes of data...' % len(l)))
             sock.send('{:02X}'.format(len(l)).encode())
             sock.send("\r\n".encode())
             sock.send(l)
@@ -177,19 +177,19 @@ def example_print_response(response_object):
     #print('')
 
     print('HTTP-Status-Code (explicit or implied):')
-    print(response_object.http_status_code)
+    print((response_object.http_status_code))
     print('')
     
     print('HTTP-Block:')
-    print(response_object.http_block)
+    print((response_object.http_block))
     print('')
 
     print('ICAP-Block:')
-    print(response_object.icap_block)
+    print((response_object.icap_block))
     print('')
 
     print('Virus found?')
-    print(response_object.virus_found())
+    print((response_object.virus_found()))
     print('')
 
         
diff --git a/ckanext/odsh/lib/uploader.py b/ckanext/odsh/lib/uploader.py
index ab89260a54cada77b3de43f81834e2ab600bd67f..144a64ae2bf8b16e98e43507f3ed896f184aba8c 100644
--- a/ckanext/odsh/lib/uploader.py
+++ b/ckanext/odsh/lib/uploader.py
@@ -1,9 +1,9 @@
 import ckan.logic as logic
 from ckan.lib.uploader import ResourceUpload, Upload
 import ckan.plugins.toolkit as toolkit
-from pylons import config
+import ckan.plugins.toolkit as tk
 
-from odsh_icap_client import ODSHICAPRequest
+from .odsh_icap_client import ODSHICAPRequest
 import logging
 import hashlib
 
@@ -13,7 +13,7 @@ log = logging.getLogger(__name__)
 def _icap_virus_found(filename, upload_file):
     # the flag skip_icap_virus_check in can be used during development
     skip_icap_virus_check = toolkit.asbool(
-        config.get('ckanext.odsh.skip_icap_virus_check', 'False')
+        tk.config.get('ckanext.odsh.skip_icap_virus_check', 'False')
     )
     if skip_icap_virus_check:
         log.debug("WARNING: icap virus check skipped, remove parameter ckanext.odsh.skip_icap_virus_check from ckan's ini file")
@@ -74,4 +74,4 @@ class ODSHUpload(Upload):
     def upload(self, max_size=2):
         if hasattr(self, 'filename') and hasattr(self, 'upload_file'):
             _raise_validation_error_if_virus_found(self.filename, self.upload_file)
-        super(ODSHUpload, self).upload(max_size)
\ No newline at end of file
+        super(ODSHUpload, self).upload(max_size)
diff --git a/ckanext/odsh/logic/action.py b/ckanext/odsh/logic/action.py
index a4cf48d8adb682efdb4058757d83918fc14a3f6c..9b74a9d291defe8083ba28cb2cddffe3087fc14f 100644
--- a/ckanext/odsh/logic/action.py
+++ b/ckanext/odsh/logic/action.py
@@ -5,12 +5,12 @@ import ckan.model as model
 import ckan.lib.dictization.model_dictize as model_dictize
 from ckan.lib.munge import munge_title_to_name
 import ckan.plugins.toolkit as toolkit
-from pylons import config
 from ckan.lib.search.common import make_connection, SearchError
 import pysolr
 import datetime
 import cgi
-import urllib2
+import urllib.request, urllib.error, urllib.parse
+import ckan.plugins.toolkit as tk
 
 import logging
 log = logging.getLogger(__name__)
@@ -149,7 +149,7 @@ def autocomplete(context, data_dict):
 
 def odsh_resource_create(context, data_dict):
     copy_remote_resources = toolkit.asbool(
-        config.get('ckanext.odsh.copy_remote_resources', 'False')
+        tk.config.get('ckanext.odsh.copy_remote_resources', 'False')
     )
     if copy_remote_resources:
       is_linked_resource = ( not 'upload' in data_dict ) or ( not isinstance(data_dict['upload'], cgi.FieldStorage))
@@ -163,7 +163,7 @@ TMP_FILE_PATH = '/tmp/temp_file_upload'
 def _download_linked_resource_to_tmp(url):
     log.debug('Downloading linked resource from {}.'.format(url))
     setup_proxy()
-    test_file = urllib2.urlopen(url).read()
+    test_file = urllib.request.urlopen(url).read()
     clear_proxy()
     with open(TMP_FILE_PATH, 'wb') as temporary_file:
         temporary_file.write(test_file)
diff --git a/ckanext/odsh/matomo.py b/ckanext/odsh/matomo.py
index 89d802f7c83077853992bb4bce10717c2b52b898..710099ac2abdc83b66656f0c8c300b7fe2b6c342 100644
--- a/ckanext/odsh/matomo.py
+++ b/ckanext/odsh/matomo.py
@@ -1,7 +1,7 @@
 from piwikapi.tracking import PiwikTracker
 from piwikapi.tests.request import FakeRequest
 from ckan.common import c, request
-from pylons import config
+import ckan.plugins.toolkit as tk
 import logging
 from ckan.plugins.toolkit import enqueue_job
 import ckanext.odsh.helpers_tpsh as helpers_tpsh
@@ -20,14 +20,14 @@ def create_matomo_request(userId=None):
         'REMOTE_ADDR': request.headers.get('Host'),
         # 'HTTP_REFERER': 'http://referer.com/somewhere/',
         'HTTP_ACCEPT_LANGUAGE': request.headers.get('Accept-Language'),
-        'SERVER_NAME': config.get('ckan.site_url'),
+        'SERVER_NAME': tk.config.get('ckan.site_url'),
         'PATH_INFO': c.environ['PATH_INFO'],
         # 'QUERY_STRING': 'something=bar',
         'HTTPS': False,
     }
     fakerequest = FakeRequest(headers)
-    piwiktracker =PiwikTracker(config.get('ckanext.odsh.matomo_id'), fakerequest)
-    piwiktracker.set_api_url(config.get('ckanext.odsh.matomo_url'))
+    piwiktracker =PiwikTracker(tk.config.get('ckanext.odsh.matomo_id'), fakerequest)
+    piwiktracker.set_api_url(tk.config.get('ckanext.odsh.matomo_url'))
     if userId:
         piwiktracker.set_visitor_id(userId)
     # enqueue_job(piwiktracker.do_track_page_view,[request.path_qs], queue='tracking')
diff --git a/ckanext/odsh/pdf_to_thumbnail/action.py b/ckanext/odsh/pdf_to_thumbnail/action.py
index d77845bea62b3b0c99e3afeaeab0d7bccd8b6394..d4a19d19f6eef062e52a7efc2b8e039a8d6af15e 100644
--- a/ckanext/odsh/pdf_to_thumbnail/action.py
+++ b/ckanext/odsh/pdf_to_thumbnail/action.py
@@ -4,7 +4,7 @@ import ckan.lib.helpers as helpers
 from ckan.logic.action.update import package_update
 from ckan.logic.action.delete import package_delete
 
-import thumbnail
+from . import thumbnail
 
 
 def before_package_delete(context, package_id_dict):
diff --git a/ckanext/odsh/pdf_to_thumbnail/plugin.py b/ckanext/odsh/pdf_to_thumbnail/plugin.py
index ecd4fdefda6bdd1054ba6e90efd9910ae9b66c7b..3181c39692850818fc4a00c06206c4bc3ebd6b09 100644
--- a/ckanext/odsh/pdf_to_thumbnail/plugin.py
+++ b/ckanext/odsh/pdf_to_thumbnail/plugin.py
@@ -4,9 +4,9 @@ import os
 import ckan.plugins as plugins
 
 #pdf_to_thumbnail 
-import thumbnail 
-import action as thumbnail_action
-import helpers as thumbnail_helpers
+from . import thumbnail 
+from . import action as thumbnail_action
+from . import helpers as thumbnail_helpers
 
 import logging
 log = logging.getLogger(__name__)
diff --git a/ckanext/odsh/pdf_to_thumbnail/thumbnail.py b/ckanext/odsh/pdf_to_thumbnail/thumbnail.py
index 7a39dea04a486e5c8d3ee820a854ac0e67883c68..bf5199803e8959dd6a6a52b821b4938c90f9b97e 100644
--- a/ckanext/odsh/pdf_to_thumbnail/thumbnail.py
+++ b/ckanext/odsh/pdf_to_thumbnail/thumbnail.py
@@ -4,7 +4,7 @@ import magic
 from pdf2image import convert_from_bytes
 import logging
 from ckan.common import config 
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 import requests
 
 from binascii import b2a_hex
diff --git a/ckanext/odsh/plugin.py b/ckanext/odsh/plugin.py
index e4791d41fcee6af771e9ca3ea6229a20a6028552..200279c9b7de5f6d05dfec0673da43a8a4ce2a23 100644
--- a/ckanext/odsh/plugin.py
+++ b/ckanext/odsh/plugin.py
@@ -4,12 +4,10 @@ import json
 import logging
 from multiline_formatter.formatter import MultilineMessagesFormatter
 import os
-from pylons import config
 from routes.mapper import SubMapper
 import sys
 
 # imports from ckan
-from ckan.common import OrderedDict
 import ckan.lib.helpers as helpers
 from ckan.lib.plugins import DefaultTranslation, DefaultDatasetForm
 from ckan.logic.validators import tag_string_convert
@@ -21,7 +19,7 @@ import ckan.model as model
 import ckanext.odsh.helpers as odsh_helpers
 import ckanext.odsh.helpers_tpsh as helpers_tpsh
 import ckanext.odsh.helper_pkg_dict as helper_pkg_dict
-from helper_pkg_dict import HelperPgkDict
+from .helper_pkg_dict import HelperPgkDict
 import ckanext.odsh.logic.action as action
 import ckanext.odsh.validation as validation
 import ckanext.odsh.search as search
@@ -263,11 +261,11 @@ class OdshPlugin(plugins.SingletonPlugin, DefaultTranslation, DefaultDatasetForm
 
         map.redirect('/dataset/{id}/resource/{resource_id}', '/dataset/{id}')         
 
-        if plugins.toolkit.asbool(config.get('ckanext.dcat.enable_rdf_endpoints', True)):
+        if plugins.toolkit.asbool(tk.config.get('ckanext.dcat.enable_rdf_endpoints', True)):
             odsh_helpers.odsh_remove_route(map, 'dcat_catalog')
             map.connect(
                 'dcat_catalog',
-                config.get(
+                tk.config.get(
                     'ckanext.dcat.catalog_endpoint',
                     '/catalog.{_format}'
                 ),
diff --git a/ckanext/odsh/precondition.py b/ckanext/odsh/precondition.py
index 542c14fbb564c7a9a4e0b65ed00072de0617482a..eda5a961a5a8d2e555beebe0e28c59283d1032a0 100644
--- a/ckanext/odsh/precondition.py
+++ b/ckanext/odsh/precondition.py
@@ -1,5 +1,4 @@
-from pylons import config
-
+import ckan.plugins.toolkit as tk
 
 class PreconditionViolated(Exception):
     def __init__(self, message):
@@ -8,10 +7,10 @@ class PreconditionViolated(Exception):
 
 def not_on_slave(func):
     def wrapped(*args, **kwargs):
-        if config.get('ckanext.odsh.slave', False):
+        if tk.config.get('ckanext.odsh.slave', False):
             raise PreconditionViolated('not allowed on slave')
         return func(*args, **kwargs)
 
-    if config.get('ckanext.odsh.debug', False):
+    if tk.config.get('ckanext.odsh.debug', False):
         return wrapped
     return func
diff --git a/ckanext/odsh/pretty_daterange/date_range_formatter.py b/ckanext/odsh/pretty_daterange/date_range_formatter.py
index dcd513f32257bb5e339a89470fae64b5389d5c86..a9018737661cba2d120ce6392e91bc3d0ff5f1c2 100644
--- a/ckanext/odsh/pretty_daterange/date_range_formatter.py
+++ b/ckanext/odsh/pretty_daterange/date_range_formatter.py
@@ -75,25 +75,25 @@ class DateRangeFormatter(object):
 
     @staticmethod
     def _as_utf_8(s):
-        return u'' + s.decode('utf-8')
+        return '' + s.decode('utf-8')
     
     
     def _construct_half_of_year_date_string(self):
         year = self.date_start.year
         half = self._date_range.get_half_year(self.date_start)
-        half_of_year_date_string = u'{}. Halbjahr {}'.format(half, year)
+        half_of_year_date_string = '{}. Halbjahr {}'.format(half, year)
         return DateRangeFormatter._as_utf_8(half_of_year_date_string)
     
     
     def _construct_quarter_of_year_date_string(self):
         year = self.date_start.year
         quarter = self._date_range.get_quarter(self.date_start)
-        quarter_of_year_date_string = u'{}. Quartal {}'.format(quarter, year)
+        quarter_of_year_date_string = '{}. Quartal {}'.format(quarter, year)
         return DateRangeFormatter._as_utf_8(quarter_of_year_date_string)
 
     
     def _construct_date_range_string(self, format_date_start, format_date_end):
-        formatted_date_range = u'{} - {}'.format(
+        formatted_date_range = '{} - {}'.format(
             self._construct_single_date_string(self.date_start, format_date_start), 
             self._construct_single_date_string(self.date_end, format_date_end))
         return formatted_date_range
diff --git a/ckanext/odsh/profiles/__init__.py b/ckanext/odsh/profiles/__init__.py
index f87530b7c8b2c3d7eb8dc717ef414fad44ba1a55..e413591d2675e362ed4b1bc8e9faf6aeecfe9ed8 100644
--- a/ckanext/odsh/profiles/__init__.py
+++ b/ckanext/odsh/profiles/__init__.py
@@ -1,2 +1,2 @@
-from odsh_dcat_de_profile import ODSHDCATdeProfile
-from odsh_european_dcatap_profile import ODSHEuropeanDCATAPProfile
\ No newline at end of file
+from .odsh_dcat_de_profile import ODSHDCATdeProfile
+from .odsh_european_dcatap_profile import ODSHEuropeanDCATAPProfile
\ No newline at end of file
diff --git a/ckanext/odsh/profiles/odsh_dcat_de_profile.py b/ckanext/odsh/profiles/odsh_dcat_de_profile.py
index 08dc69022dd4c2eb339debe0c83c52e42c00f277..fc136aa92dc622cfcdda77e05f71e063a362c66d 100644
--- a/ckanext/odsh/profiles/odsh_dcat_de_profile.py
+++ b/ckanext/odsh/profiles/odsh_dcat_de_profile.py
@@ -6,7 +6,7 @@ import ckan.model as model
 from ckanext.dcat.profiles import DCT
 from ckanext.dcat.utils import resource_uri
 import ckanext.dcatde.dataset_utils as ds_utils
-from ckanext.dcatde.profiles import DCATdeProfile, DCATDE, DCAT, DCATDE_1_0, DCATDE_1_0_1, DCATDE_1_0_2
+from ckanext.dcatde.profiles import DCATdeProfile, DCATDE, DCAT, DCATDE_1_0, DCATDE_1_0_1
 
 import ckanext.odsh.helpers as odsh_helpers
 import ckanext.odsh.helpers_tpsh as helpers_tpsh
@@ -15,6 +15,7 @@ import ckanext.odsh.collection.helpers as helpers_collection
 
 DCT = rdflib.namespace.Namespace("http://purl.org/dc/terms/")
 DCAT = rdflib.namespace.Namespace("http://www.w3.org/ns/dcat#")
+DCATDE_1_0_2 = rdflib.namespace.Namespace("http://dcat-ap.de/def/dcatde/1.0.2/")
 
 
 class ODSHDCATdeProfile(DCATdeProfile):
@@ -35,7 +36,7 @@ class ODSHDCATdeProfile(DCATdeProfile):
         for distribution in self.g.objects(dataset_ref, DCAT.distribution):
             for resource_dict in dataset_dict.get('resources', []):
                 # Match distribution in graph and distribution in ckan-dict
-                if unicode(distribution) == resource_uri(resource_dict):
+                if str(distribution) == resource_uri(resource_dict):
                     for namespace in [DCATDE, DCATDE_1_0, DCATDE_1_0_1, DCATDE_1_0_2]:
                         value = self._object_value(
                             distribution, namespace.licenseAttributionByText)
@@ -123,7 +124,7 @@ class ODSHDCATdeProfile(DCATdeProfile):
         for distribution in self.g.objects(dataset_ref, DCAT.distribution):
             for resource_dict in dataset_dict.get('resources', []):
                 # Match distribution in graph and distribution in ckan-dict
-                if unicode(distribution) == resource_uri(resource_dict):
+                if str(distribution) == resource_uri(resource_dict):
                     last_modified = resource_dict.get('last_modified', None)
                     if last_modified:
                         self.g.set(
diff --git a/ckanext/odsh/profiles/odsh_european_dcatap_profile.py b/ckanext/odsh/profiles/odsh_european_dcatap_profile.py
index 544323bd1d9b32db11f84e78b93f4a5fb364f92d..c9a8ecf34dff0dfe58ddb821233b8eba4af5c181 100644
--- a/ckanext/odsh/profiles/odsh_european_dcatap_profile.py
+++ b/ckanext/odsh/profiles/odsh_european_dcatap_profile.py
@@ -19,7 +19,7 @@ class ODSHEuropeanDCATAPProfile(EuropeanDCATAPProfile):
         else:
             license_uri2id = {}
             license_title2id = {}
-            for license_id, license in LicenseRegister().items():
+            for license_id, license in list(LicenseRegister().items()):
                 license_uri2id[license_id] = license_id
                 license_uri2id[license.url] = license_id
                 license_title2id[license.title] = license_id
@@ -50,14 +50,14 @@ class ODSHEuropeanDCATAPProfile(EuropeanDCATAPProfile):
             dataset_dict, dataset_ref)
         for s, p, o in self.g.triples((None, rdflib.RDF.type, DCAT.Distribution)):
             for s2, p2, o2 in self.g.triples((s, DCT['format'], None)):
-                if o2.decode() in resource_formats_export():
+                if str(o2) in resource_formats_export():
                     self.g.set((s, DCT['format'], rdflib.URIRef(
-                        resource_formats_export()[o2.decode()])))
+                        resource_formats_export()[str(o2)])))
         for s, p, o in self.g.triples((None, DCT.language, None)):
-            if o.decode() in get_language():
-                self.g.set((s, p, rdflib.URIRef(get_language()[o.decode()])))
-            elif type(o) == rdflib.Literal and type(URIRefOrLiteral(o.decode())) == rdflib.URIRef:
-                self.g.set((s, p, rdflib.URIRef(o.decode())))
+            if str(o) in get_language():
+                self.g.set((s, p, rdflib.URIRef(get_language()[str(o)])))
+            elif type(o) == rdflib.Literal and type(URIRefOrLiteral(str(o))) == rdflib.URIRef:
+                self.g.set((s, p, rdflib.URIRef(str(o))))
 
         license = dataset_dict.get('license_id', None)
         if license:
@@ -84,7 +84,7 @@ def resource_formats():
     except:
         log.exception("failed to process resource_formats")
         raise Exception('failed to load formats')
-    file_types = [subj.decode() for subj in g.subjects()]
+    file_types = [str(subj) for subj in g.subjects()]
 
     for elem in sorted(set(file_types)):
         if elem.split('/')[-1] != 'file-type':
@@ -128,7 +128,7 @@ def get_language():
         with open(languages_file_path) as languages_file:
             try:
                 language_mapping_table = json.loads(languages_file.read())
-            except ValueError, e:
+            except ValueError as e:
                 # includes simplejson.decoder.JSONDecodeError
                 raise ValueError('Invalid JSON syntax in %s: %s' %
                                  (languages_file_path, e))
@@ -136,4 +136,4 @@ def get_language():
             for language_line in language_mapping_table:
                 _LANGUAGES[language_line[0]] = language_line[1]
 
-    return _LANGUAGES
\ No newline at end of file
+    return _LANGUAGES
diff --git a/ckanext/odsh/search.py b/ckanext/odsh/search.py
index bc68443ccb0fab2453156e65712d2c293fc9bdd5..8d2a794ddbc20c71e01bf50aaba354629544794c 100644
--- a/ckanext/odsh/search.py
+++ b/ckanext/odsh/search.py
@@ -53,7 +53,7 @@ def _get_start_and_end_query(start_date, end_date):
     return start_query, end_query
 
 def _get_open_end_query(end_date):
-    if end_date is '*':
+    if end_date == '*':
         open_end_query = '(*:* NOT extras_temporal_end:[* TO *])'
     else:
         open_end_query = '((*:* NOT extras_temporal_end:[* TO *]) AND extras_temporal_start:[* TO {end_date}])'.format(
@@ -70,11 +70,11 @@ def _get_enclosing_query(start_date, end_date):
     return enclosing_query
 
 def _combine_query(fq_preset, start_query, end_query, enclosing_query, open_end_query):
-    combined_query = u'{fq_preset} ({start_query} OR {end_query} {enclosing_query} OR {open_end_query})'.format(
+    combined_query = '{fq_preset} ({start_query} OR {end_query} {enclosing_query} OR {open_end_query})'.format(
         fq_preset=fq_preset, 
         start_query=start_query, 
         end_query=end_query, 
         enclosing_query=enclosing_query, 
         open_end_query=open_end_query
     )
-    return combined_query
\ No newline at end of file
+    return combined_query
diff --git a/ckanext/odsh/setup_proxy.py b/ckanext/odsh/setup_proxy.py
index 13338f65a60156bf8958107746035d50d8e2c9ab..071fdd9320bc35cb4ad858b05214739b9d71576c 100644
--- a/ckanext/odsh/setup_proxy.py
+++ b/ckanext/odsh/setup_proxy.py
@@ -1,4 +1,4 @@
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 from ckan.common import config
 
 
@@ -14,11 +14,11 @@ def setup_proxy():
 
     proxy_url = config.get('ckanext.odsh.download_proxy', None)
     if proxy_url:
-        proxy = urllib2.ProxyHandler({'http': proxy_url, 'https': proxy_url})
-        opener = urllib2.build_opener(proxy)
-        urllib2.install_opener(opener)
+        proxy = urllib.request.ProxyHandler({'http': proxy_url, 'https': proxy_url})
+        opener = urllib.request.build_opener(proxy)
+        urllib.request.install_opener(opener)
 
 def clear_proxy():
-    proxy = urllib2.ProxyHandler({})
-    opener = urllib2.build_opener(proxy)
-    urllib2.install_opener(opener)
\ No newline at end of file
+    proxy = urllib.request.ProxyHandler({})
+    opener = urllib.request.build_opener(proxy)
+    urllib.request.install_opener(opener)
\ No newline at end of file
diff --git a/ckanext/odsh/tests/ckan_selenium.py b/ckanext/odsh/tests/ckan_selenium.py
index f3ffd93c90f3aa8640190df9a36c6e9d524662eb..3bacd10f910c67790f5b4ea5a5e2ca98f3dda215 100644
--- a/ckanext/odsh/tests/ckan_selenium.py
+++ b/ckanext/odsh/tests/ckan_selenium.py
@@ -27,7 +27,7 @@ class SeleniumCkanApp:
         elem.clear()
         elem.send_keys("OpenData!0619")
         elem.send_keys(Keys.RETURN)
-        print(self.driver.title)
+        print((self.driver.title))
         assert "Datens" in self.driver.title
         # assert "No results found." not in driver.page_source
 
@@ -68,7 +68,7 @@ class SeleniumCkanApp:
         return self.driver.find_element_by_class_name(clazz)
 
     def fill_form(self, data, keyprefix=''):
-        for key, value in data.iteritems():
+        for key, value in data.items():
             elem = self.driver.find_element_by_id(keyprefix + key)
             elem.send_keys(value)
 
@@ -91,4 +91,4 @@ class SeleniumCkanApp:
 
     def onMaster(self):
         cont = self.get_slave_flag()
-        return cont == u'0'
+        return cont == '0'
diff --git a/ckanext/odsh/tests/harvest_sever_mock.py b/ckanext/odsh/tests/harvest_sever_mock.py
index cc20bdc6f2aed429c9aee37a8a2b1eaeee843016..45c7538ea7bb5886f3781df65cda25812d0f7054 100644
--- a/ckanext/odsh/tests/harvest_sever_mock.py
+++ b/ckanext/odsh/tests/harvest_sever_mock.py
@@ -1,5 +1,5 @@
 import socket
-from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+from http.server import BaseHTTPRequestHandler, HTTPServer
 import time
 import requests
 import pdb
diff --git a/ckanext/odsh/tests/icap_sever_mock.py b/ckanext/odsh/tests/icap_sever_mock.py
index cc20bdc6f2aed429c9aee37a8a2b1eaeee843016..45c7538ea7bb5886f3781df65cda25812d0f7054 100644
--- a/ckanext/odsh/tests/icap_sever_mock.py
+++ b/ckanext/odsh/tests/icap_sever_mock.py
@@ -1,5 +1,5 @@
 import socket
-from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+from http.server import BaseHTTPRequestHandler, HTTPServer
 import time
 import requests
 import pdb
diff --git a/ckanext/odsh/tests/test_env.py b/ckanext/odsh/tests/test_env.py
index e153ef72fe270d4cf309d678e9c8ea119cabca9e..39144f5c4fdb936546285f0cf4a8660694a63a45 100644
--- a/ckanext/odsh/tests/test_env.py
+++ b/ckanext/odsh/tests/test_env.py
@@ -3,12 +3,12 @@ import ckan.model as model
 import pdb
 import json
 import ckanext.odsh.profiles as profiles
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 import os
 import sys
-import ConfigParser
+import configparser
 from collections import OrderedDict
-from urlparse import urlsplit
+from urllib.parse import urlsplit
 
 expected_commit = '8cd9576884cae6abe50a27c891434cb9fe87ced2'
 
@@ -27,8 +27,8 @@ def checkConfig(key, expected=None, minLength=None):
 
 
 def readUrl(value):
-    req = urllib2.Request(value)
-    response = urllib2.urlopen(req)
+    req = urllib.request.Request(value)
+    response = urllib.request.urlopen(req)
     data = response.read()
     return data
 
@@ -90,7 +90,7 @@ def checkConfigForDuplicates():
             path = a.split('=')[1]
             break
     assert path, 'could not find config parameter'
-    config = ConfigParser.RawConfigParser(dict_type=MultiOrderedDict)
+    config = configparser.RawConfigParser(dict_type=MultiOrderedDict)
     config.read([path])
 
 
@@ -157,7 +157,7 @@ class TestEnv:
         # assert 'ckanext-odsh' in value
 
         register = model.Package.get_license_register()
-        licenses = register.values()
+        licenses = list(register.values())
 
         assert len(licenses) > 15
         assert sum(['dcat' in l.id for l in licenses]) == 23
@@ -242,21 +242,21 @@ class TestEnv:
                 if len(diff) > 0:
                     print('WARNING: missing routes:')
                     for r in sorted(diff, key=lambda tup: tup[1]):
-                        print('{cmd} {source} {target}'.format(
-                            cmd=r[0], source=r[1], target='http://<interne-IP-Master>'+r[2]))
+                        print(('{cmd} {source} {target}'.format(
+                            cmd=r[0], source=r[1], target='http://<interne-IP-Master>'+r[2])))
                 diff = current.difference(expected)
                 if len(diff) > 0:
                     print('WARNING: found unexpected routes:')
                     for r in sorted(diff, key=lambda tup: tup[1]):
-                        print('{cmd} {source} {target}'.format(
-                            cmd=r[0], source=r[1], target='<target>'+r[2]))
+                        print(('{cmd} {source} {target}'.format(
+                            cmd=r[0], source=r[1], target='<target>'+r[2])))
 
     def _parse_rules(self, lines, check_host=False):
         rules = set(['ProxyPassMatch', 'ProxyPassReverse', 'ProxyPass'])
         ret = []
         hosts = set()
         for line in lines:
-            tokens = filter(lambda t: t.strip(), line.strip().split(' '))
+            tokens = [t for t in line.strip().split(' ') if t.strip()]
             if not tokens or tokens[0] not in rules:
                 continue
             assert len(tokens) == 3
@@ -266,6 +266,6 @@ class TestEnv:
             ret.append((tokens[0], tokens[1], f.path))
             hosts.add(f.netloc)
         if check_host and len(hosts) > 1:
-            print('WARNING: found multiple target hosts: {hosts}'.format(
-                hosts=', '.join(hosts)))
+            print(('WARNING: found multiple target hosts: {hosts}'.format(
+                hosts=', '.join(hosts))))
         return set(ret)
diff --git a/ckanext/odsh/tests/test_rdfexport.py b/ckanext/odsh/tests/test_rdfexport.py
index 7b2c58490c2d27f5a499689815d4f8a2d2099b36..795c767ca8c0fe5a1862c8054aed568403e91934 100644
--- a/ckanext/odsh/tests/test_rdfexport.py
+++ b/ckanext/odsh/tests/test_rdfexport.py
@@ -5,7 +5,7 @@ from ckan.common import config
 import ckan.model as model
 import json
 import ckanext.odsh.profiles as profiles
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 import ckan.tests.helpers as helpers
 from ckan.common import config
 import ckan.config.middleware
diff --git a/ckanext/odsh/tests/test_routes.py b/ckanext/odsh/tests/test_routes.py
index c35d8af0d8328d013fd7a46fa3ec9cbdc610bbd8..00c404d80e18d285ff3f2b45ab0be42b1b8f1408 100644
--- a/ckanext/odsh/tests/test_routes.py
+++ b/ckanext/odsh/tests/test_routes.py
@@ -3,7 +3,7 @@ import ckan.model as model
 import pdb
 import json
 import ckanext.odsh.profiles as profiles
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 import ckan.tests.helpers as helpers
 from ckan.common import config
 import ckan.config.middleware
diff --git a/ckanext/odsh/tests/test_search.py b/ckanext/odsh/tests/test_search.py
index 5c2c64e9c153397066330e85376b6e3b5855c026..582c3605b845dc37537e2934ba352b3ee25b8338 100644
--- a/ckanext/odsh/tests/test_search.py
+++ b/ckanext/odsh/tests/test_search.py
@@ -8,7 +8,7 @@ from ckan.lib.mailer import create_reset_key
 from nose.tools import assert_true, assert_false, assert_equal, assert_in
 from routes import url_for
 import ckan.plugins
-from test_helpers import odsh_test
+from .test_helpers import odsh_test
 import pdb
 
 
diff --git a/ckanext/odsh/tests/test_selenium.py b/ckanext/odsh/tests/test_selenium.py
index 7c478c469aba18f3b94c26f0f07196ee12866710..5d397af1d9996328c53774fca0182541611f5a19 100644
--- a/ckanext/odsh/tests/test_selenium.py
+++ b/ckanext/odsh/tests/test_selenium.py
@@ -68,4 +68,4 @@ class TestSelenium:
         for path in paths:
             TestSelenium.app.got_to_url(path)
             cont = TestSelenium.app.get_slave_flag()
-            assert cont == u'0'
+            assert cont == '0'
diff --git a/ckanext/odsh/tests/test_statistikNordHarvester_old.py b/ckanext/odsh/tests/test_statistikNordHarvester_old.py
index bd0edc6a069c04a254641b8cc653b977e64dbd6c..c489e1145b4800bdeef6192a38a00dc1f37cab0a 100755
--- a/ckanext/odsh/tests/test_statistikNordHarvester_old.py
+++ b/ckanext/odsh/tests/test_statistikNordHarvester_old.py
@@ -30,7 +30,7 @@ class TestStatistikNordHarvester(TestCase):
         context = {
             'model': model,
             'session': model.Session,
-            'user': unicode('default'),
+            'user': str('default'),
         }
 
         result = harvester.map_fields(snh, context, h1)
diff --git a/ckanext/odsh/tests/test_upload.py b/ckanext/odsh/tests/test_upload.py
index 02eba9fa4c3100b6f8aa425a749cc9a57b8f6017..53db2eeace7c5fb934b9fa1d6ff0801c220067ae 100644
--- a/ckanext/odsh/tests/test_upload.py
+++ b/ckanext/odsh/tests/test_upload.py
@@ -4,7 +4,7 @@
 import ckan.tests.factories as factories
 import ckan.tests.helpers as helpers
 from ckan import model
-from test_helpers import odsh_test
+from .test_helpers import odsh_test
 from routes import url_for
 from nose.tools import assert_true, assert_false, assert_equal, assert_in
 from ckanext.odsh.helpers import odsh_create_checksum
diff --git a/ckanext/odsh/tests/test_user.py b/ckanext/odsh/tests/test_user.py
index fe209c8e64a3addfdbd42409bbdd66370d9a3dce..7382c19c71b84a4516ba35ac0cb802195823d40d 100644
--- a/ckanext/odsh/tests/test_user.py
+++ b/ckanext/odsh/tests/test_user.py
@@ -4,7 +4,7 @@
 import ckan.tests.factories as factories
 import ckan.tests.helpers as helpers
 from ckan import model
-from test_helpers import odsh_test
+from .test_helpers import odsh_test
 from routes import url_for
 from nose.tools import assert_true, assert_false, assert_equal, assert_in
 
diff --git a/ckanext/odsh/tests_tpsh/resources/org_dicts.py b/ckanext/odsh/tests_tpsh/resources/org_dicts.py
index 5948d7658b0e626459a3cef62392c124f8b3f723..a9b43d2dab5e8f68cbccfb656269fd7518cb1b45 100644
--- a/ckanext/odsh/tests_tpsh/resources/org_dicts.py
+++ b/ckanext/odsh/tests_tpsh/resources/org_dicts.py
@@ -1,87 +1,87 @@
 # encoding: utf-8
 
 organization_with_address = {
-            'approval_status': u'approved',
+            'approval_status': 'approved',
             'created': '2019-07-29T08:11:32.697127',
-            'description': u'',
-            'display_name': u'Test-Organisation',
+            'description': '',
+            'display_name': 'Test-Organisation',
             'extras': [
                 {
-                    'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b',
-                    'id': u'b31c1b27-8c4e-46d7-b533-30bc4cc93d0e',
-                    'key': u'location',
-                    'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676',
-                    'state': u'active',
-                    'value': u'Müllerdorf'
+                    'group_id': '63c87e74-60a9-4a4a-a980-d7983c47f92b',
+                    'id': 'b31c1b27-8c4e-46d7-b533-30bc4cc93d0e',
+                    'key': 'location',
+                    'revision_id': '5e9677a3-ddab-4306-850f-cfd4fad3d676',
+                    'state': 'active',
+                    'value': 'Müllerdorf'
                 },
                 {
-                    'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b',
-                    'id': u'7302c660-871e-44f0-92f4-18bc5b8953ec',
-                    'key': u'mail',
-                    'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676',
-                    'state': u'active',
-                    'value': u'mueller@mueller.de'
+                    'group_id': '63c87e74-60a9-4a4a-a980-d7983c47f92b',
+                    'id': '7302c660-871e-44f0-92f4-18bc5b8953ec',
+                    'key': 'mail',
+                    'revision_id': '5e9677a3-ddab-4306-850f-cfd4fad3d676',
+                    'state': 'active',
+                    'value': 'mueller@mueller.de'
                 },
                 {
-                    'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b',
-                    'id': u'6d3e02ba-b721-4362-92f3-985ed50f6c42',
-                    'key': u'person',
-                    'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676',
-                    'state': u'active',
-                    'value': u'Michael Müller'
+                    'group_id': '63c87e74-60a9-4a4a-a980-d7983c47f92b',
+                    'id': '6d3e02ba-b721-4362-92f3-985ed50f6c42',
+                    'key': 'person',
+                    'revision_id': '5e9677a3-ddab-4306-850f-cfd4fad3d676',
+                    'state': 'active',
+                    'value': 'Michael Müller'
                 },
                 {
-                    'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b',
-                    'id': u'baf92ac2-21e2-49f5-96f8-e48c7fd50cf0',
-                    'key': u'street',
-                    'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676',
-                    'state': u'active',
-                    'value': u'Müllergasse 10'
+                    'group_id': '63c87e74-60a9-4a4a-a980-d7983c47f92b',
+                    'id': 'baf92ac2-21e2-49f5-96f8-e48c7fd50cf0',
+                    'key': 'street',
+                    'revision_id': '5e9677a3-ddab-4306-850f-cfd4fad3d676',
+                    'state': 'active',
+                    'value': 'Müllergasse 10'
                 },
                 {
-                    'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b',
-                    'id': u'd5ce2972-487e-444b-95ba-c6a8db238c67',
-                    'key': u'telephone',
-                    'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676',
-                    'state': u'active',
-                    'value': u'040 123456'
+                    'group_id': '63c87e74-60a9-4a4a-a980-d7983c47f92b',
+                    'id': 'd5ce2972-487e-444b-95ba-c6a8db238c67',
+                    'key': 'telephone',
+                    'revision_id': '5e9677a3-ddab-4306-850f-cfd4fad3d676',
+                    'state': 'active',
+                    'value': '040 123456'
                 },
                 {
-                    'group_id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b',
-                    'id': u'00d1d308-6718-4d90-81ae-25ee3f0b76f7',
-                    'key': u'web',
-                    'revision_id': u'5e9677a3-ddab-4306-850f-cfd4fad3d676',
-                    'state': u'active',
-                    'value': u'mueller.de'
+                    'group_id': '63c87e74-60a9-4a4a-a980-d7983c47f92b',
+                    'id': '00d1d308-6718-4d90-81ae-25ee3f0b76f7',
+                    'key': 'web',
+                    'revision_id': '5e9677a3-ddab-4306-850f-cfd4fad3d676',
+                    'state': 'active',
+                    'value': 'mueller.de'
                 }
             ],
             'groups': [],
-            'id': u'63c87e74-60a9-4a4a-a980-d7983c47f92b',
-            'image_display_url': u'',
-            'image_url': u'',
+            'id': '63c87e74-60a9-4a4a-a980-d7983c47f92b',
+            'image_display_url': '',
+            'image_url': '',
             'is_organization': True,
-            'name': u'test-organisation',
-            'num_followers': 0L,
+            'name': 'test-organisation',
+            'num_followers': 0,
             'package_count': 51,
-            'revision_id': u'3040af6c-d3f6-462d-b48a-329d63e17a28',
-            'state': u'active',
+            'revision_id': '3040af6c-d3f6-462d-b48a-329d63e17a28',
+            'state': 'active',
             'tags': [],
-            'title': u'Test-Organisation',
-            'type': u'organization',
+            'title': 'Test-Organisation',
+            'type': 'organization',
             'users': [{
-                'about': u'',
+                'about': '',
                 'activity_streams_email_notifications': False,
-                'capacity': u'admin',
+                'capacity': 'admin',
                 'created': '2019-06-05T06:19:17.576291',
-                'display_name': u'ckanuser',
+                'display_name': 'ckanuser',
                 'email_hash': 'b28744113bd1ae8dc4f6e17aefe3bd9b',
-                'fullname': u'',
-                'id': u'ff8d002d-3908-45e5-ba7b-445381860957',
-                'name': u'ckanuser',
-                'number_created_packages': 13L,
-                'number_of_edits': 456L,
+                'fullname': '',
+                'id': 'ff8d002d-3908-45e5-ba7b-445381860957',
+                'name': 'ckanuser',
+                'number_created_packages': 13,
+                'number_of_edits': 456,
                 'openid': None,
-                'state': u'active',
+                'state': 'active',
                 'sysadmin': True
             }]
         }
\ No newline at end of file
diff --git a/ckanext/odsh/tests_tpsh/test_date_range_formatter.py b/ckanext/odsh/tests_tpsh/test_date_range_formatter.py
index d2c8e77bf148fe8ef67b6a973033ba759caa3e9e..28f16ca33d94a8b31b4646db301ec71ed243dc91 100644
--- a/ckanext/odsh/tests_tpsh/test_date_range_formatter.py
+++ b/ckanext/odsh/tests_tpsh/test_date_range_formatter.py
@@ -23,7 +23,7 @@ class TestDateRangeFormatter(object):
         self._assert_output_string_equals(
             date_start = datetime.date(2019,1,2), 
             date_end = datetime.date(2020, 2, 4), 
-            expected_str = u'02.01.2019 - 04.02.2020')
+            expected_str = '02.01.2019 - 04.02.2020')
     
     def _assert_output_string_equals(self, date_start, date_end, expected_str):
         drf = DateRangeFormatter(date_start, date_end)
@@ -34,112 +34,112 @@ class TestDateRangeFormatter(object):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 1, 2), 
             date_end = datetime.date(2019, 1, 2), 
-            expected_str = u'02.01.2019')
+            expected_str = '02.01.2019')
 
     def test_it_returns_only_year_for_full_year(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 1, 1), 
             date_end = datetime.date(2019, 12, 31), 
-            expected_str = u'2019')
+            expected_str = '2019')
     
     def test_it_returns_only_year_for_multiple_full_years(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 1, 1), 
             date_end = datetime.date(2020, 12, 31), 
-            expected_str = u'2019 - 2020')
+            expected_str = '2019 - 2020')
     
     def test_it_returns_month_for_full_month(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 2, 1), 
             date_end = datetime.date(2019, 2, 28), 
-            expected_str = u'Februar 2019')
+            expected_str = 'Februar 2019')
     
     def test_it_returns_months_for_range_of_months(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 2, 1), 
             date_end = datetime.date(2020, 3, 31), 
-            expected_str = u'Februar 2019 - März 2020')
+            expected_str = 'Februar 2019 - März 2020')
     
     def test_it_returns_months_for_range_of_months_in_same_year(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 2, 1), 
             date_end = datetime.date(2019, 3, 31), 
-            expected_str = u'Februar - März 2019')
+            expected_str = 'Februar - März 2019')
     
     def test_it_returns_first_quarter(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 1, 1), 
             date_end = datetime.date(2019, 3, 31), 
-            expected_str = u'1. Quartal 2019')
+            expected_str = '1. Quartal 2019')
     
     def test_it_returns_second_quarter(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 4, 1), 
             date_end = datetime.date(2019, 6, 30), 
-            expected_str = u'2. Quartal 2019')
+            expected_str = '2. Quartal 2019')
     
     def test_it_returns_third_quarter(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 7, 1), 
             date_end = datetime.date(2019, 9, 30), 
-            expected_str = u'3. Quartal 2019')
+            expected_str = '3. Quartal 2019')
     
     def test_it_returns_fourth_quarter(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 10, 1), 
             date_end = datetime.date(2019, 12, 31), 
-            expected_str = u'4. Quartal 2019')
+            expected_str = '4. Quartal 2019')
     
     def test_it_returns_first_half_year(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 1, 1), 
             date_end = datetime.date(2019, 6, 30), 
-            expected_str = u'1. Halbjahr 2019')
+            expected_str = '1. Halbjahr 2019')
 
     def test_it_returns_second_half_year(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 7, 1), 
             date_end = datetime.date(2019, 12, 31), 
-            expected_str = u'2. Halbjahr 2019')
+            expected_str = '2. Halbjahr 2019')
     
     def test_it_returns_date_start_if_date_end_is_none(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 7, 1), 
             date_end = None, 
-            expected_str = u'ab 01.07.2019'
+            expected_str = 'ab 01.07.2019'
         )
     
     def test_it_returns_date_end_if_date_start_is_none(self):
         self._assert_output_string_equals(
             date_start = None, 
             date_end = datetime.date(2019, 7, 1), 
-            expected_str = u'bis 01.07.2019'
+            expected_str = 'bis 01.07.2019'
         )
 
     def test_it_returns_empty_string_if_date_start_and_end_are_none(self):
         self._assert_output_string_equals(
             date_start = None, 
             date_end = None, 
-            expected_str = u''
+            expected_str = ''
         )
 
     def test_it_returns_date_start_if_date_end_is_empty(self):
         self._assert_output_string_equals(
             date_start = datetime.date(2019, 7, 1), 
             date_end = '', 
-            expected_str = u'ab 01.07.2019'
+            expected_str = 'ab 01.07.2019'
         )
     
     def test_it_returns_date_end_if_date_start_is_empty(self):
         self._assert_output_string_equals(
             date_start = '', 
             date_end = datetime.date(2019, 7, 1), 
-            expected_str = u'bis 01.07.2019'
+            expected_str = 'bis 01.07.2019'
         )
 
     def test_it_returns_empty_string_if_date_start_and_end_are_empty(self):
         self._assert_output_string_equals(
             date_start = '', 
             date_end = '', 
-            expected_str = u''
+            expected_str = ''
         )
\ No newline at end of file
diff --git a/ckanext/odsh/tests_tpsh/test_helper_pkg_dict.py b/ckanext/odsh/tests_tpsh/test_helper_pkg_dict.py
index 2b5af7be0bade0d18299473ba0052fa9118047f0..33cb9d049c72cb9a5cb6d39d9bb4553570b425d5 100644
--- a/ckanext/odsh/tests_tpsh/test_helper_pkg_dict.py
+++ b/ckanext/odsh/tests_tpsh/test_helper_pkg_dict.py
@@ -9,7 +9,7 @@ import ckanext.odsh.uri_store as uri_store
 class TestHelperPkgDict(object):
     def test_is_collection_returns_true(self):
         dataset_dict = {
-            u'type': u'collection',
+            'type': 'collection',
         }
         h = HelperPgkDict(dataset_dict)
         is_collection = h.is_collection()
@@ -22,7 +22,7 @@ class TestHelperPkgDict(object):
         nt.assert_false(is_collection)
     
     def test_is_collection_returns_false_if_type_dataset(self):
-        dataset_dict = {u'type': u'dataset'}
+        dataset_dict = {'type': 'dataset'}
         h = HelperPgkDict(dataset_dict)
         is_collection = h.is_collection()
         nt.assert_false(is_collection)
@@ -50,12 +50,12 @@ class TestHelperPkgDict(object):
             # arange
             # taken from debugging _update_relations_to_collection_members:
             dataset_dict_collection = {
-                'id': u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d',
-                u'extras': [
-                    {u'key': u'has_version', u'value': u'["http://transparenz.schleswig-holstein.de/9f9ae60bb0d8985e10e9ab8aa6a7ca34", "http://transparenz.schleswig-holstein.de/3a0d0674120fbf06b5cb8737124e3fd0", "http://transparenz.schleswig-holstein.de/b5cd3f303f594ecde96e1017e953b688", "http://transparenz.schleswig-holstein.de/ff612d0f165d46f3091f58e1ef56a2ec", "http://transparenz.schleswig-holstein.de/cd606b042789723a2b6d61cb31c46c39"]'},
+                'id': '248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d',
+                'extras': [
+                    {'key': 'has_version', 'value': '["http://transparenz.schleswig-holstein.de/9f9ae60bb0d8985e10e9ab8aa6a7ca34", "http://transparenz.schleswig-holstein.de/3a0d0674120fbf06b5cb8737124e3fd0", "http://transparenz.schleswig-holstein.de/b5cd3f303f594ecde96e1017e953b688", "http://transparenz.schleswig-holstein.de/ff612d0f165d46f3091f58e1ef56a2ec", "http://transparenz.schleswig-holstein.de/cd606b042789723a2b6d61cb31c46c39"]'},
                 ]
             }
-            id_collection = u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d'
+            id_collection = '248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d'
             uri_collection_member = 'http://transparenz.schleswig-holstein.de/b5cd3f303f594ecde96e1017e953b688'
             id_collection_member = 'fake_id_collection_member'
             uri_store._set_uri_to_id({uri_collection_member: id_collection_member})
@@ -76,13 +76,13 @@ class TestHelperPkgDict(object):
             # arange
             # taken from debugging _update_relations_to_collection_members:
             dataset_dict_collection_member = {
-                u'add_to_collection': True,
-                u'extras': [
-                    {u'key': u'is_version_of', u'value': u'["http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63"]'},
+                'add_to_collection': True,
+                'extras': [
+                    {'key': 'is_version_of', 'value': '["http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63"]'},
                 ],
-                'id': u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d',
+                'id': '248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d',
             }
-            id_collection_member = u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d'
+            id_collection_member = '248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d'
             uri_collection = 'http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63'
             id_collection = 'fake_id_collection'
             uri_store._set_uri_to_id({uri_collection: id_collection})
@@ -100,29 +100,29 @@ class TestHelperPkgDict(object):
     
     def test_get_collection_uri(self):
         dataset_dict_collection_member = {
-            u'add_to_collection': True,
-            u'extras': [
-                {u'key': u'is_version_of', u'value': u'["http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63"]'},
+            'add_to_collection': True,
+            'extras': [
+                {'key': 'is_version_of', 'value': '["http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63"]'},
             ],
-            'id': u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d',
+            'id': '248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d',
         }
         h = HelperPgkDict(dataset_dict_collection_member)
         collection = h.get_collection_uri()
-        nt.assert_equal(collection, u'http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63')
+        nt.assert_equal(collection, 'http://transparenz.schleswig-holstein.de/5ffd27c528f7ab6936318da90d5cdd63')
     
     def test_get_collection_uri_if_not_in_extras(self):
         dataset_dict_collection_member = {
-            u'id': u'248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d',
+            'id': '248ceefd-2fb8-4a3c-ab3c-d2c873b24e4d',
         }
         h = HelperPgkDict(dataset_dict_collection_member)
         collection = h.get_collection_uri()
         nt.assert_equal(collection, None)
     
     def test_get_uris_collection_members(self):
-        collection_members_as_string = u'["http://transparenz.schleswig-holstein.de/cd606b042789723a2b6d61cb31c46c39", "http://transparenz.schleswig-holstein.de/3a0d0674120fbf06b5cb8737124e3fd0", "http://transparenz.schleswig-holstein.de/b5cd3f303f594ecde96e1017e953b688", "http://transparenz.schleswig-holstein.de/9f9ae60bb0d8985e10e9ab8aa6a7ca34", "http://transparenz.schleswig-holstein.de/ff612d0f165d46f3091f58e1ef56a2ec"]'
+        collection_members_as_string = '["http://transparenz.schleswig-holstein.de/cd606b042789723a2b6d61cb31c46c39", "http://transparenz.schleswig-holstein.de/3a0d0674120fbf06b5cb8737124e3fd0", "http://transparenz.schleswig-holstein.de/b5cd3f303f594ecde96e1017e953b688", "http://transparenz.schleswig-holstein.de/9f9ae60bb0d8985e10e9ab8aa6a7ca34", "http://transparenz.schleswig-holstein.de/ff612d0f165d46f3091f58e1ef56a2ec"]'
         dataset_dict = {
-            u'extras': [
-                {u'key': u'has_version', u'value': collection_members_as_string},
+            'extras': [
+                {'key': 'has_version', 'value': collection_members_as_string},
             ]
         }
         h = HelperPgkDict(dataset_dict)
@@ -137,33 +137,33 @@ class Test_get_date_start_and_end_from_pkg_dict(object):
 
     def setUp(self):
         self.dict_with_start_and_end_date = {
-            u'extras': [
-                {u'key': u'groups', u'value': u''}, 
-                {u'key': u'issued', u'value': u'2019-07-06T00:00:00'}, 
-                {u'key': u'licenseAttributionByText', u'value': u''}, 
-                {u'key': u'subject_text', u'value': u''}, 
-                {u'key': u'temporal_end', u'value': u'2019-08-31T00:00:00'}, 
-                {u'key': u'temporal_start', u'value': u'2019-08-01T00:00:00'}
+            'extras': [
+                {'key': 'groups', 'value': ''}, 
+                {'key': 'issued', 'value': '2019-07-06T00:00:00'}, 
+                {'key': 'licenseAttributionByText', 'value': ''}, 
+                {'key': 'subject_text', 'value': ''}, 
+                {'key': 'temporal_end', 'value': '2019-08-31T00:00:00'}, 
+                {'key': 'temporal_start', 'value': '2019-08-01T00:00:00'}
             ],
         }
         self.dict_with_empty_start_date = {
-            u'extras': [
-                {u'key': u'groups', u'value': u''}, 
-                {u'key': u'issued', u'value': u'2019-07-06T00:00:00'}, 
-                {u'key': u'licenseAttributionByText', u'value': u''}, 
-                {u'key': u'subject_text', u'value': u''}, 
-                {u'key': u'temporal_end', u'value': u'2019-08-31T00:00:00'}, 
-                {u'key': u'temporal_start', u'value': u''}
+            'extras': [
+                {'key': 'groups', 'value': ''}, 
+                {'key': 'issued', 'value': '2019-07-06T00:00:00'}, 
+                {'key': 'licenseAttributionByText', 'value': ''}, 
+                {'key': 'subject_text', 'value': ''}, 
+                {'key': 'temporal_end', 'value': '2019-08-31T00:00:00'}, 
+                {'key': 'temporal_start', 'value': ''}
             ],
         }
         self.dict_with_empty_end_date = {
-            u'extras': [
-                {u'key': u'groups', u'value': u''}, 
-                {u'key': u'issued', u'value': u'2019-07-06T00:00:00'}, 
-                {u'key': u'licenseAttributionByText', u'value': u''}, 
-                {u'key': u'subject_text', u'value': u''}, 
-                {u'key': u'temporal_end', u'value': u''}, 
-                {u'key': u'temporal_start', u'value': u'2019-08-01T00:00:00'}
+            'extras': [
+                {'key': 'groups', 'value': ''}, 
+                {'key': 'issued', 'value': '2019-07-06T00:00:00'}, 
+                {'key': 'licenseAttributionByText', 'value': ''}, 
+                {'key': 'subject_text', 'value': ''}, 
+                {'key': 'temporal_end', 'value': ''}, 
+                {'key': 'temporal_start', 'value': '2019-08-01T00:00:00'}
             ],
         }
         self.date_start_expected = datetime.date(2019, 8, 1)
diff --git a/ckanext/odsh/tests_tpsh/test_helpers_tpsh.py b/ckanext/odsh/tests_tpsh/test_helpers_tpsh.py
index 78c106081ea654955449b41e79412f5c7563e33a..1615d88d10a3808c1d9ad613ab740e0e1db8384c 100644
--- a/ckanext/odsh/tests_tpsh/test_helpers_tpsh.py
+++ b/ckanext/odsh/tests_tpsh/test_helpers_tpsh.py
@@ -64,11 +64,11 @@ class Test_correct_missing_relationship(object):
     def setUp(self):
         self.relationships_from_model = [
             FakePackageRelationship(
-                object_package_id=u'object package id',
-                revision_id=u'revision id',
-                subject_package_id=u'subject package id',
-                id=u'id',
-                type=u'type'
+                object_package_id='object package id',
+                revision_id='revision id',
+                subject_package_id='subject package id',
+                id='id',
+                type='type'
             )
         ]
 
@@ -83,17 +83,17 @@ class Test_correct_missing_relationship(object):
     
     def test_it_does_not_modify_pkg_dict_if_relationships_already_in_dict(self):
         original_pkg_dict = {
-            u'type': u'dataset',
-            u'relationships_as_subject': [
+            'type': 'dataset',
+            'relationships_as_subject': [
                 {
-                    u'__extras': {
-                        u'object_package_id': u'original object package id', 
-                        u'revision_id': u'original revision id', 
-                        u'subject_package_id': u'original subject package id'
+                    '__extras': {
+                        'object_package_id': 'original object package id', 
+                        'revision_id': 'original revision id', 
+                        'subject_package_id': 'original subject package id'
                     }, 
-                    u'comment': u'', 
-                    u'id': u'original id', 
-                    u'type': u'original type'
+                    'comment': '', 
+                    'id': 'original id', 
+                    'type': 'original type'
                 }
             ]
         }
@@ -105,17 +105,17 @@ class Test_correct_missing_relationship(object):
     
     def test_it_does_not_modify_pkg_dict_if_type_collection(self):
         original_pkg_dict = {
-            u'type': u'collection',
-            u'relationships_as_subject': [
+            'type': 'collection',
+            'relationships_as_subject': [
                 {
-                    u'__extras': {
-                        u'object_package_id': u'original object package id', 
-                        u'revision_id': u'original revision id', 
-                        u'subject_package_id': u'original subject package id'
+                    '__extras': {
+                        'object_package_id': 'original object package id', 
+                        'revision_id': 'original revision id', 
+                        'subject_package_id': 'original subject package id'
                     }, 
-                    u'comment': u'', 
-                    u'id': u'original id', 
-                    u'type': u'original type'
+                    'comment': '', 
+                    'id': 'original id', 
+                    'type': 'original type'
                 }
             ]
         }
@@ -127,24 +127,24 @@ class Test_correct_missing_relationship(object):
     
     def test_it_adds_relationships_if_not_already_in_dict(self):
         pkg_dict = {
-            u'type': u'dataset',
-            u'relationships_as_subject': []
+            'type': 'dataset',
+            'relationships_as_subject': []
         }
         correct_missing_relationship(
             pkg_dict, self.relationships_from_model
         )
         expected_pkg_dict = {
-            u'type': u'dataset',
-            u'relationships_as_subject': [
+            'type': 'dataset',
+            'relationships_as_subject': [
                 {
-                    u'__extras': {
-                        u'object_package_id': u'object package id', 
-                        u'revision_id': u'revision id', 
-                        u'subject_package_id': u'subject package id'
+                    '__extras': {
+                        'object_package_id': 'object package id', 
+                        'revision_id': 'revision id', 
+                        'subject_package_id': 'subject package id'
                     }, 
-                    u'comment': u'', 
-                    u'id': u'id', 
-                    u'type': u'type'
+                    'comment': '', 
+                    'id': 'id', 
+                    'type': 'type'
                 }
             ]
         }
@@ -180,18 +180,18 @@ class Test_get_language_of_package(object):
 
     def test_it_returns_Englisch(self):
         test_package = {
-                'id': u'language_test',
-                u'extras': [
-                    {u'key': u'language', u'value': u'http://publications.europa.eu/resource/authority/language/ENG'},
+                'id': 'language_test',
+                'extras': [
+                    {'key': 'language', 'value': 'http://publications.europa.eu/resource/authority/language/ENG'},
                 ]
             }
         nt.assert_equal(get_language_of_package(test_package),'Englisch')
     
     def test_it_returns_None_if_language_id_not_in_dict(self):
         test_package = {
-                'id': u'language_test',
-                u'extras': [
-                    {u'key': u'language', u'value': u'tlhIngan Hol'},
+                'id': 'language_test',
+                'extras': [
+                    {'key': 'language', 'value': 'tlhIngan Hol'},
                 ]
             }
         nt.assert_equal(get_language_of_package(test_package), None)
@@ -205,12 +205,12 @@ class Test_get_address_org(object):
     def test_it_returns_address_for_org_with_address(self):
         organization = org_dicts.organization_with_address
         address = get_address_org(organization)
-        nt.assert_equal(address.get('location'), u'Müllerdorf')
-        nt.assert_equal(address.get('person'), u'Michael Müller')
-        nt.assert_equal(address.get('mail'), u'mueller@mueller.de')
-        nt.assert_equal(address.get('street'), u'Müllergasse 10')
-        nt.assert_equal(address.get('telephone'), u'040 123456')
-        nt.assert_equal(address.get('web'), u'http://mueller.de')
+        nt.assert_equal(address.get('location'), 'Müllerdorf')
+        nt.assert_equal(address.get('person'), 'Michael Müller')
+        nt.assert_equal(address.get('mail'), 'mueller@mueller.de')
+        nt.assert_equal(address.get('street'), 'Müllergasse 10')
+        nt.assert_equal(address.get('telephone'), '040 123456')
+        nt.assert_equal(address.get('web'), 'http://mueller.de')
 
     def test_it_returns_empty_dict_if_called_via_organization_new(self):
         organization = dict()
@@ -236,11 +236,11 @@ class Test_load_json_to_ordered_dict(object):
         nt.assert_is(type(self.result), OrderedDict)
     
     def test_it_preserves_order_of_keys(self):
-        keys = self.result.keys()
-        nt.assert_equal(keys, [u'A', u'B', u'D', u'C', u'E'])
+        keys = list(self.result.keys())
+        nt.assert_equal(keys, ['A', 'B', 'D', 'C', 'E'])
     
     def test_it_preserves_order_of_values(self):
-        values = self.result.values()
+        values = list(self.result.values())
         nt.assert_equal(values, [1, 2, 3, 4, 0])
 
 class Test_load_subject_mapping(object):
@@ -255,11 +255,11 @@ class Test_load_subject_mapping(object):
         nt.assert_is(type(self.SUBJECT_MAPPING), OrderedDict)
     
     def test_it_preserves_order_of_json_file(self):
-        keys = self.SUBJECT_MAPPING.keys()
-        nt.assert_equal(keys[0], u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Verwaltungsvorschrift')
-        nt.assert_equal(keys[1], u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Organisationsplan')
-        nt.assert_equal(keys[2], u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Geschaeftsverteilungsplan')
-        nt.assert_equal(keys[3], u'http://transparenz.schleswig-holstein.de/informationsgegenstand#Aktenplan')
+        keys = list(self.SUBJECT_MAPPING.keys())
+        nt.assert_equal(keys[0], 'http://transparenz.schleswig-holstein.de/informationsgegenstand#Verwaltungsvorschrift')
+        nt.assert_equal(keys[1], 'http://transparenz.schleswig-holstein.de/informationsgegenstand#Organisationsplan')
+        nt.assert_equal(keys[2], 'http://transparenz.schleswig-holstein.de/informationsgegenstand#Geschaeftsverteilungsplan')
+        nt.assert_equal(keys[3], 'http://transparenz.schleswig-holstein.de/informationsgegenstand#Aktenplan')
 
 class Test_get_subject_for_selection(object):
     def setUp(self):
diff --git a/ckanext/odsh/tests_tpsh/test_odsh_helpers.py b/ckanext/odsh/tests_tpsh/test_odsh_helpers.py
index a4c488230d254139ec4b3c7da1df93dd762a41dc..9adfc5e6da311f1a44ea941c1cd9204a6be0acfe 100644
--- a/ckanext/odsh/tests_tpsh/test_odsh_helpers.py
+++ b/ckanext/odsh/tests_tpsh/test_odsh_helpers.py
@@ -22,21 +22,21 @@ class Test_tpsh_get_successor_and_predecessor_dataset(object):
         # public1   2014-07-03
 
         self.names_collection_members = [
-            u'public3', u'public2', u'public1', u'public4', 
-            u'private3', u'private2', u'private1']
+            'public3', 'public2', 'public1', 'public4', 
+            'private3', 'private2', 'private1']
         self.dates_collection_members = [
-            u'2014-07-01T00:00:00', #public3
-            u'2014-07-01T00:00:00', #public2
-            u'2014-07-03T00:00:00', #public1
-            u'2014-07-02T00:00:00', #public4
-            u'2014-07-05T00:00:00', #private3
-            u'2014-07-06T00:00:00', #private2
-            u'2014-07-07T00:00:00', #private1
+            '2014-07-01T00:00:00', #public3
+            '2014-07-01T00:00:00', #public2
+            '2014-07-03T00:00:00', #public1
+            '2014-07-02T00:00:00', #public4
+            '2014-07-05T00:00:00', #private3
+            '2014-07-06T00:00:00', #private2
+            '2014-07-07T00:00:00', #private1
         ]
         self.pkg_dicts_collection_members = [
             {
-                u'name': name,
-                u'extras': {u'issued': date}
+                'name': name,
+                'extras': {'issued': date}
             }
             for (name, date) in zip(self.names_collection_members, self.dates_collection_members)
         ]
@@ -48,7 +48,7 @@ class Test_tpsh_get_successor_and_predecessor_dataset(object):
             return False
         
         def fake_get_package_dict(name):
-            package_list = filter(lambda pkg_dict:pkg_dict.get('name')==name, self.pkg_dicts_collection_members)
+            package_list = [pkg_dict for pkg_dict in self.pkg_dicts_collection_members if pkg_dict.get('name')==name]
             return package_list[0]
 
         self.patch_get_datasets_belonging_to_collection_by_dataset = patch.object(
@@ -81,12 +81,12 @@ class Test_tpsh_get_successor_and_predecessor_dataset(object):
         nt.assert_equal(return_value, self.names_collection_members)
     
     def test_patch_access_checker_returns_True(self):
-        pkg_dict = {u'name': u'public1'}
+        pkg_dict = {'name': 'public1'}
         has_access = helpers.check_access('package_show', pkg_dict)
         nt.assert_true(has_access)
     
     def test_patch_access_checker_returns_False(self):
-        pkg_dict = {u'name': u'private1'}
+        pkg_dict = {'name': 'private1'}
         has_access = helpers.check_access('package_show', pkg_dict)
         nt.assert_false(has_access)
     
@@ -95,25 +95,25 @@ class Test_tpsh_get_successor_and_predecessor_dataset(object):
         nt.assert_equal(pkg_dict.get('name'), 'public1')
     
     def test_it_returns_correct_for_public2(self):
-        pkg_dict = {u'name': u'public2'}
+        pkg_dict = {'name': 'public2'}
         successor, predecessor = helpers_collection.get_successor_and_predecessor_dataset(pkg_dict)
         nt.assert_equal(successor, 'public3')
         nt.assert_equal(predecessor, None)
     
     def test_it_returns_correct_for_public3(self):
-        pkg_dict = {u'name': u'public3'}
+        pkg_dict = {'name': 'public3'}
         successor, predecessor = helpers_collection.get_successor_and_predecessor_dataset(pkg_dict)
         nt.assert_equal(successor, 'public4')
         nt.assert_equal(predecessor, 'public2')
 
     def test_it_returns_correct_for_public4(self):
-        pkg_dict = {u'name': u'public4'}
+        pkg_dict = {'name': 'public4'}
         successor, predecessor = helpers_collection.get_successor_and_predecessor_dataset(pkg_dict)
         nt.assert_equal(successor, 'public1')
         nt.assert_equal(predecessor, 'public3')
 
     def test_it_returns_correct_for_public1(self):
-        pkg_dict = {u'name': u'public1'}
+        pkg_dict = {'name': 'public1'}
         successor, predecessor = helpers_collection.get_successor_and_predecessor_dataset(pkg_dict)
         nt.assert_equal(successor, None)
         nt.assert_equal(predecessor, 'public4')
@@ -124,7 +124,7 @@ class Test_tpsh_get_successor_and_predecessor_dataset(object):
             'get_all_datasets_belonging_to_collection_by_dataset',
             return_value = list()
         ):
-            pkg_dict = {u'name': u'some_name'}
+            pkg_dict = {'name': 'some_name'}
             successor, predecessor = helpers_collection.get_successor_and_predecessor_dataset(pkg_dict)
             nt.assert_equal(successor, None)
             nt.assert_equal(predecessor, None)
diff --git a/ckanext/odsh/tests_tpsh/test_plugin.py b/ckanext/odsh/tests_tpsh/test_plugin.py
index e5084a4518c3639bba1956d4dac29f0606ad3c28..627518a2a359f3a8a97563bb95edbc89b1f56418 100644
--- a/ckanext/odsh/tests_tpsh/test_plugin.py
+++ b/ckanext/odsh/tests_tpsh/test_plugin.py
@@ -12,8 +12,8 @@ class TestMethodBeforeView(object):
         hundred_days_ago_as_ckan_str = self._date_as_ckan_str(hundred_days_ago)
         dict_for_template = plugin_object.before_view(
             {
-                u'extras': [
-                    {u'key': 'issued', u'value': hundred_days_ago_as_ckan_str}
+                'extras': [
+                    {'key': 'issued', 'value': hundred_days_ago_as_ckan_str}
                 ]
             }
         )
@@ -29,8 +29,8 @@ class TestMethodBeforeView(object):
         ten_days_ago_as_ckan_str = self._date_as_ckan_str(ten_days_ago)
         dict_for_template = plugin_object.before_view(
             {
-                u'extras': [
-                    {u'key': 'issued', u'value': ten_days_ago_as_ckan_str}
+                'extras': [
+                    {'key': 'issued', 'value': ten_days_ago_as_ckan_str}
                 ]
             }
         )
@@ -43,8 +43,8 @@ class TestMethodBeforeView(object):
         ten_days_ago_as_ckan_str = self._date_as_ckan_str(ten_days_ago)
         dict_for_template = plugin_object.before_view(
             {
-                u'extras': [
-                    {u'key': 'issued', u'value': ten_days_ago_as_ckan_str}
+                'extras': [
+                    {'key': 'issued', 'value': ten_days_ago_as_ckan_str}
                 ],
                 'some_other_key': 'some_other_value', 
             }
diff --git a/ckanext/odsh/tests_tpsh/test_profiles.py b/ckanext/odsh/tests_tpsh/test_profiles.py
index 0d6e859b98e3dcf8ec557ee0df1d2bd0160a9690..f26405a354e0a58d23198180a477cae5c7a5cda7 100644
--- a/ckanext/odsh/tests_tpsh/test_profiles.py
+++ b/ckanext/odsh/tests_tpsh/test_profiles.py
@@ -97,7 +97,7 @@ class TestODSHDCATdeProfileGraphFromDataset(object):
     def get_graph_and_assert_in(self, dataset_dict, dataset_ref, expected_node):
         self.profile.graph_from_dataset(dataset_dict, dataset_ref)
         graph_serialized = self.profile.g.serialize()
-        print(self.profile.g.serialize(format='pretty-xml'))
+        print((self.profile.g.serialize(format='pretty-xml')))
         nt.assert_in(expected_node, graph_serialized)
     
     
diff --git a/ckanext/odsh/tests_tpsh/test_search.py b/ckanext/odsh/tests_tpsh/test_search.py
index 8e5008f9bf5dc88ecf3c0df5f224824319e9118a..e3b0dd6ecd7bb2df39de5c6cd0cfc6c42c7122c8 100644
--- a/ckanext/odsh/tests_tpsh/test_search.py
+++ b/ckanext/odsh/tests_tpsh/test_search.py
@@ -6,11 +6,11 @@ class Test_before_search(object):
         self.search_params_before_test = {
             'extras': {}, 
             'facet.field': ['organization', 'subject_text', 'groups'], 
-            'fq': u'organization:"test-organisation" groups:"gove" subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset', 
+            'fq': 'organization:"test-organisation" groups:"gove" subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset', 
             'include_private': True, 
-            'q': u'', 
+            'q': '', 
             'rows': 20, 
-            'sort': u'score desc, metadata_modified desc', 
+            'sort': 'score desc, metadata_modified desc', 
             'start': 0
         }
         self.search_params_with_facet_mincount = self.search_params_before_test.copy()
@@ -28,16 +28,16 @@ class Test_before_search(object):
     def test_it_adds_fq_if_empty_range(self):
         # arange
         search_params = self.search_params_before_test.copy()
-        extras = {'ext_enddate': u'2019-08-01', 'ext_startdate': u'2019-08-02'}
+        extras = {'ext_enddate': '2019-08-01', 'ext_startdate': '2019-08-02'}
         search_params.update({'extras': extras})
         search_params_expected = self.search_params_with_facet_mincount.copy()
         search_params_expected.update({'extras': extras})
         search_params_expected.update({
             'fq': (
-                u'organization:"test-organisation" groups:"gove" subject_text:"T\xe4tigkeitsbericht" '
-                u'+dataset_type:dataset (+extras_temporal_start:[2019-08-02T00:00:00Z TO 2019-08-01T00:00:00Z] '
-                u'OR +extras_temporal_end:[2019-08-02T00:00:00Z TO 2019-08-01T00:00:00Z]  OR '
-                u'((*:* NOT extras_temporal_end:[* TO *]) AND extras_temporal_start:[* TO 2019-08-01T00:00:00Z]))'
+                'organization:"test-organisation" groups:"gove" subject_text:"T\xe4tigkeitsbericht" '
+                '+dataset_type:dataset (+extras_temporal_start:[2019-08-02T00:00:00Z TO 2019-08-01T00:00:00Z] '
+                'OR +extras_temporal_end:[2019-08-02T00:00:00Z TO 2019-08-01T00:00:00Z]  OR '
+                '((*:* NOT extras_temporal_end:[* TO *]) AND extras_temporal_start:[* TO 2019-08-01T00:00:00Z]))'
             )
         })
         # act
@@ -48,7 +48,7 @@ class Test_before_search(object):
     def test_it_solely_adds_facet_mincount_to_dict_if_wrong_date_format_in_extras(self):
         # arange
         search_params = self.search_params_before_test.copy()
-        extras = {'ext_enddate': u'some_date', 'ext_startdate': u'some_date'}
+        extras = {'ext_enddate': 'some_date', 'ext_startdate': 'some_date'}
         search_params.update({'extras': extras})
         search_params_expected = self.search_params_with_facet_mincount.copy()
         search_params_expected.update({'extras': extras})
@@ -60,20 +60,20 @@ class Test_before_search(object):
     def test_it_adds_fq_if_enclosing_range(self):
         # arange
         search_params = self.search_params_before_test.copy()
-        extras = {'ext_enddate': u'2019-08-02', 'ext_startdate': u'2019-08-01'}
+        extras = {'ext_enddate': '2019-08-02', 'ext_startdate': '2019-08-01'}
         search_params.update({'extras': extras})
         search_params_expected = self.search_params_with_facet_mincount.copy()
         search_params_expected.update({'extras': extras})
         search_params_expected.update({
             'fq': (
-                u'organization:"test-organisation" groups:"gove" '
-                u'subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset '
-                u'(+extras_temporal_start:[2019-08-01T00:00:00Z TO 2019-08-02T00:00:00Z] '
-                u'OR +extras_temporal_end:[2019-08-01T00:00:00Z TO 2019-08-02T00:00:00Z]  '
-                u'OR (extras_temporal_start:[* TO 2019-08-01T00:00:00Z] AND '
-                u'extras_temporal_end:[2019-08-02T00:00:00Z TO *]) OR '
-                u'((*:* NOT extras_temporal_end:[* TO *]) AND '
-                u'extras_temporal_start:[* TO 2019-08-02T00:00:00Z]))'
+                'organization:"test-organisation" groups:"gove" '
+                'subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset '
+                '(+extras_temporal_start:[2019-08-01T00:00:00Z TO 2019-08-02T00:00:00Z] '
+                'OR +extras_temporal_end:[2019-08-01T00:00:00Z TO 2019-08-02T00:00:00Z]  '
+                'OR (extras_temporal_start:[* TO 2019-08-01T00:00:00Z] AND '
+                'extras_temporal_end:[2019-08-02T00:00:00Z TO *]) OR '
+                '((*:* NOT extras_temporal_end:[* TO *]) AND '
+                'extras_temporal_start:[* TO 2019-08-02T00:00:00Z]))'
             )
         })
         # act
@@ -84,17 +84,17 @@ class Test_before_search(object):
     def test_it_adds_fq_if_start_only(self):
         # arange
         search_params = self.search_params_before_test.copy()
-        extras = {'ext_startdate': u'2019-08-01'}
+        extras = {'ext_startdate': '2019-08-01'}
         search_params.update({'extras': extras})
         search_params_expected = self.search_params_with_facet_mincount.copy()
         search_params_expected.update({'extras': extras})
         search_params_expected.update({
             'fq': (
-                u'organization:"test-organisation" groups:"gove" '
-                u'subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset '
-                u'(+extras_temporal_start:[2019-08-01T00:00:00Z TO *] '
-                u'OR +extras_temporal_end:[2019-08-01T00:00:00Z TO *]  '
-                u'OR (*:* NOT extras_temporal_end:[* TO *]))'
+                'organization:"test-organisation" groups:"gove" '
+                'subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset '
+                '(+extras_temporal_start:[2019-08-01T00:00:00Z TO *] '
+                'OR +extras_temporal_end:[2019-08-01T00:00:00Z TO *]  '
+                'OR (*:* NOT extras_temporal_end:[* TO *]))'
             )
         })
         # act
@@ -105,18 +105,18 @@ class Test_before_search(object):
     def test_it_adds_fq_if_end_only(self):
         # arange
         search_params = self.search_params_before_test.copy()
-        extras = {'ext_enddate': u'2019-08-02'}
+        extras = {'ext_enddate': '2019-08-02'}
         search_params.update({'extras': extras})
         search_params_expected = self.search_params_with_facet_mincount.copy()
         search_params_expected.update({'extras': extras})
         search_params_expected.update({
             'fq': (
-                u'organization:"test-organisation" groups:"gove" '
-                u'subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset '
-                u'(+extras_temporal_start:[* TO 2019-08-02T00:00:00Z] OR '
-                u'+extras_temporal_end:[* TO 2019-08-02T00:00:00Z]  '
-                u'OR ((*:* NOT extras_temporal_end:[* TO *]) '
-                u'AND extras_temporal_start:[* TO 2019-08-02T00:00:00Z]))'
+                'organization:"test-organisation" groups:"gove" '
+                'subject_text:"T\xe4tigkeitsbericht" +dataset_type:dataset '
+                '(+extras_temporal_start:[* TO 2019-08-02T00:00:00Z] OR '
+                '+extras_temporal_end:[* TO 2019-08-02T00:00:00Z]  '
+                'OR ((*:* NOT extras_temporal_end:[* TO *]) '
+                'AND extras_temporal_start:[* TO 2019-08-02T00:00:00Z]))'
             )
         })
         # act
diff --git a/ckanext/odsh/tests_tpsh/test_uri_store.py b/ckanext/odsh/tests_tpsh/test_uri_store.py
index 0fc92f09df43419cc3375dcd3188c0697e6d9e69..0bdcac3331f8adf53c01d1e899ceea3bcc886880 100644
--- a/ckanext/odsh/tests_tpsh/test_uri_store.py
+++ b/ckanext/odsh/tests_tpsh/test_uri_store.py
@@ -3,28 +3,28 @@ from ckanext.odsh.uri_store import add_uri, get_id_from_uri, _set_uri_to_id, _ge
 
 class Test_uri_store(object):
     def test_add_uri_adds_values_to_dict(self):
-        _set_uri_to_id({u'http://some_uri': u'some_id'})
+        _set_uri_to_id({'http://some_uri': 'some_id'})
         dataset_dict = {
-            'id': u'some_new_id',
-            u'extras': [
-                {u'key': u'uri', u'value': u'http://some_new_uri'},
+            'id': 'some_new_id',
+            'extras': [
+                {'key': 'uri', 'value': 'http://some_new_uri'},
             ]
         }
         add_uri(dataset_dict)
         nt.assert_equal(
             _get_uri_to_id().get('http://some_uri'),
-            u'some_id'
+            'some_id'
         )
         nt.assert_equal(
             _get_uri_to_id().get('http://some_new_uri'),
-            u'some_new_id'
+            'some_new_id'
         )
     
     def test_get_id_returns_id(self):
-        _set_uri_to_id({u'http://some_uri': u'some_id'})
+        _set_uri_to_id({'http://some_uri': 'some_id'})
         uri = 'http://some_uri'
         id = get_id_from_uri(uri)
-        id_expected = u'some_id'
+        id_expected = 'some_id'
         nt.assert_equal(id, id_expected)
     
     def test_get_id_from_uri_returns_None_if_dict_empty(self):
@@ -33,6 +33,6 @@ class Test_uri_store(object):
         nt.assert_equal(id, None)
     
     def test_get_id_from_uri_returns_None_if_id_unknown(self):
-        _set_uri_to_id({'uri_to_id': {u'http://some_uri': u'some_id'}})
+        _set_uri_to_id({'uri_to_id': {'http://some_uri': 'some_id'}})
         id = get_id_from_uri('some_unknown_id')
         nt.assert_equal(id, None)
\ No newline at end of file
diff --git a/ckanext/odsh/tools.py b/ckanext/odsh/tools.py
index d6fa856871a78fda448cd49f289be6fc024cf2c9..63a45750be357ee61373e13d3f05f17ad1d811d3 100644
--- a/ckanext/odsh/tools.py
+++ b/ckanext/odsh/tools.py
@@ -2,8 +2,8 @@ import os
 from ckanext.odsh.pdf_to_thumbnail.thumbnail import get_resource_path
 from ckanext.odsh.lib.uploader import calculate_hash
 import ckan.plugins.toolkit as toolkit
-import magic
-import pdftotext
+#import magic
+#import pdftotext
 
 def add_attributes_resources(context, resource):
     package_id = resource.get('package_id')
@@ -31,12 +31,12 @@ def add_attributes_resources(context, resource):
                     
             
                     #number of pages
-                    file_type = magic.from_file(path, mime = True)                    
-                    if file_type == 'application/pdf':
-                        file.seek(0)            
-                        pdf = pdftotext.PDF(file)
-                        number_of_pages = len(pdf)
-                        item.update({'number_of_pages':number_of_pages})
+#                    file_type = magic.from_file(path, mime = True)                    
+#                    if file_type == 'application/pdf':
+#                        file.seek(0)            
+#                        pdf = pdftotext.PDF(file)
+#                        number_of_pages = len(pdf)
+#                        item.update({'number_of_pages':number_of_pages})
 
                     resources[i] = item 
             break                         
diff --git a/ckanext/odsh/uri_store/__init__.py b/ckanext/odsh/uri_store/__init__.py
index 8f72f0e2a35f457b004d2a60703ca917a2fb864e..688f954e7f4127b88479524c53e6776a14f4bf86 100644
--- a/ckanext/odsh/uri_store/__init__.py
+++ b/ckanext/odsh/uri_store/__init__.py
@@ -1 +1 @@
-from modifiers import add_uri, get_id_from_uri, _set_uri_to_id, _get_uri_to_id
\ No newline at end of file
+from .modifiers import add_uri, get_id_from_uri, _set_uri_to_id, _get_uri_to_id
\ No newline at end of file
diff --git a/ckanext/odsh/uri_store/modifiers.py b/ckanext/odsh/uri_store/modifiers.py
index 4fc1175b1bf10d870aca353edb6b5eddc38b0f34..4a488eaf3983ec7d2177138c35bbb91c125aa637 100644
--- a/ckanext/odsh/uri_store/modifiers.py
+++ b/ckanext/odsh/uri_store/modifiers.py
@@ -1,4 +1,4 @@
-import store 
+from . import store 
 
 import ckanext.odsh.helpers as odsh_helpers
 
diff --git a/ckanext/odsh/validation.py b/ckanext/odsh/validation.py
index 9858858935d543834958b505edae5215c9669735..ed36e069ca8bcfb208aaf6993f3ee657611c0999 100644
--- a/ckanext/odsh/validation.py
+++ b/ckanext/odsh/validation.py
@@ -2,11 +2,10 @@
 import logging
 import unicodecsv as csv
 import re
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 import json
 from itertools import count
 from dateutil.parser import parse
-from pylons import config
 
 import ckan.plugins.toolkit as toolkit
 import ckan.model as model
@@ -21,7 +20,7 @@ log = logging.getLogger(__name__)
 
 def _extract_value(data, field):
     key = None
-    for k in data.keys():
+    for k in list(data.keys()):
         if data[k] == field:
             key = k
             break
@@ -43,7 +42,7 @@ def validate_extra_groups(data, requireAtLeastOne, errors):
             return
 
         groups = [g.strip() for g in value.split(',') if value.strip()]
-        for k in data.keys():
+        for k in list(data.keys()):
             if len(k) == 3 and k[0] == 'groups':
                 data[k] = ''
                 # del data[k]
@@ -52,7 +51,7 @@ def validate_extra_groups(data, requireAtLeastOne, errors):
                 errors['groups'] = error_message_no_group
             return
 
-        for num, group in zip(range(len(groups)), groups):
+        for num, group in zip(list(range(len(groups))), groups):
             data[('groups', num, 'id')] = group
     else:  # no extra-field 'groups'
         # dataset might come from a harvest process
@@ -67,14 +66,14 @@ def validate_extras(key, data, errors, context):
     isStaNord = ('id',) in data and data[('id',)][:7] == 'StaNord'
     harvesting = ('ignore_auth' in context) and (context['ignore_auth'] == True)
     owner_org = data[('owner_org',)]
-    lenient_with = config.get('ckanext.odsh.lenient_with','')
+    lenient_with = tk.config.get('ckanext.odsh.lenient_with','')
 
     is_optional_temporal_start = toolkit.asbool(
-        config.get('ckanext.odsh.is_optional_temporal_start', False)
+        tk.config.get('ckanext.odsh.is_optional_temporal_start', False)
     ) or ( harvesting and (owner_org in lenient_with) )
 
     require_at_least_one_category = toolkit.asbool(
-        config.get('ckanext.odsh.require_at_least_one_category', False)
+        tk.config.get('ckanext.odsh.require_at_least_one_category', False)
     )
     validate_extra_groups(
         data=data, 
@@ -106,7 +105,7 @@ def validate_extras(key, data, errors, context):
         errors=extra_errors
     )
 
-    if len(extra_errors.values()):
+    if len(list(extra_errors.values())):
         raise toolkit.Invalid(extra_errors)
 
 def is_date_start_before_date_end(data, extra_errors):
@@ -118,7 +117,7 @@ def is_date_start_before_date_end(data, extra_errors):
 
 def _set_value(data, field, value):
     key = None
-    for k in data.keys():
+    for k in list(data.keys()):
         if data[k] == field:
             key = k
             break
@@ -165,7 +164,7 @@ def validate_licenseAttributionByText(key, data, errors, context):
                 hasAttribution = value != ''
                 break
     if not hasAttribution:
-        current_indexes = [k[1] for k in data.keys()
+        current_indexes = [k[1] for k in list(data.keys())
                            if len(k) > 1 and k[0] == 'extras']
 
         new_index = max(current_indexes) + 1 if current_indexes else 0
@@ -186,7 +185,7 @@ def known_spatial_uri(key, data, errors, context):
         _copy_spatial_uri_temp_to_extras(data)
     value = _extract_value(data, 'spatial_uri')
     require_spatial_uri = toolkit.asbool(
-        config.get('ckanext.odsh.require_spatial_uri', False)
+        tk.config.get('ckanext.odsh.require_spatial_uri', False)
     )
     error_message_spatial_uri_empty = 'spatial_uri: empty not allowed'
 
@@ -214,9 +213,9 @@ def known_spatial_uri(key, data, errors, context):
                 data[('extras', new_index+1, 'value')] = poly
             return
 
-    mapping_file = config.get('ckanext.odsh.spatial.mapping')
+    mapping_file = tk.config.get('ckanext.odsh.spatial.mapping')
     try:
-        mapping_file = urllib2.urlopen(mapping_file)
+        mapping_file = urllib.request.urlopen(mapping_file)
     except Exception:
         raise Exception("Could not load spatial mapping file!")
 
@@ -262,7 +261,7 @@ def _copy_spatial_uri_temp_to_extras(data):
     
 
 def next_extra_index(data):
-    current_indexes = [k[1] for k in data.keys()
+    current_indexes = [k[1] for k in list(data.keys())
                        if len(k) > 1 and k[0] == 'extras']
 
     return max(current_indexes) + 1 if current_indexes else 0
@@ -280,14 +279,14 @@ def tag_string_convert(key, data, errors, context):
     '''Takes a list of tags that is a comma-separated string (in data[key])
     and parses tag names. These are added to the data dict, enumerated. They
     are also validated.'''
-    if isinstance(data[key], basestring):
+    if isinstance(data[key], str):
         tags = [tag.strip()
                 for tag in data[key].split(',')
                 if tag.strip()]
     else:
         tags = data[key]
 
-    current_index = max([int(k[1]) for k in data.keys()
+    current_index = max([int(k[1]) for k in list(data.keys())
                          if len(k) == 3 and k[0] == 'tags'] + [-1])
 
     for num, tag in zip(count(current_index+1), tags):
@@ -304,7 +303,7 @@ def _convert_subjectID_to_subjectText(subject_id, flattened_data):
         return flattened_data
 
     default_subject_mapping_file_path = '/usr/lib/ckan/default/src/ckanext-odsh/subject_mapping.json'
-    subject_mapping_file_path = config.get(
+    subject_mapping_file_path = tk.config.get(
         'ckanext.odsh.subject_mapping_file_path', default_subject_mapping_file_path)
     
     try:
@@ -342,7 +341,7 @@ def _convert_subjectID_to_subjectText(subject_id, flattened_data):
 def validate_subject(key, flattened_data, errors, context):
     subject_id = flattened_data[key]
     require_subject = toolkit.asbool(
-        config.get('ckanext.odsh.require_subject', True)
+        tk.config.get('ckanext.odsh.require_subject', True)
     )
     if not require_subject:
         flattened_data = _convert_subjectID_to_subjectText(subject_id, flattened_data)
diff --git a/validation.py b/validation.py
index e50cbb4394ddbcc447a7fe87d200e738ba6019e6..b7b04835c474e80fe7627c92a6e77a6c506d845c 100644
--- a/validation.py
+++ b/validation.py
@@ -2,7 +2,7 @@
 import logging
 import csv
 import re
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 import json
 from itertools import count
 from dateutil.parser import parse
@@ -11,8 +11,6 @@ import ckan.plugins.toolkit as toolkit
 import ckan.model as model
 from ckan.lib.navl.dictization_functions import Missing
 
-from pylons import config
-
 import pdb
 
 _ = toolkit._
@@ -22,7 +20,7 @@ log = logging.getLogger(__name__)
 
 def _extract_value(data, field):
     key = None
-    for k in data.keys():
+    for k in list(data.keys()):
         if data[k] == field:
             key = k
             break
@@ -43,7 +41,7 @@ def validate_extra_groups(data, requireAtLeastOne, errors):
             return
 
         groups = [g.strip() for g in value.split(',') if value.strip()]
-        for k in data.keys():
+        for k in list(data.keys()):
             if len(k) == 3 and k[0] == 'groups':
                 data[k] = ''
                 # del data[k]
@@ -52,7 +50,7 @@ def validate_extra_groups(data, requireAtLeastOne, errors):
                 errors['groups'] = 'at least one group needed'
             return
 
-        for num, group in zip(range(len(groups)), groups):
+        for num, group in zip(list(range(len(groups))), groups):
             data[('groups', num, 'id')] = group
     else:  # no extra-field 'groups'
         # dataset might come from a harvest process
@@ -71,13 +69,13 @@ def validate_extras(key, data, errors, context):
                             data, isStaNord, extra_errors)
     validate_extra_date_new(key, 'temporal_end', data, True, extra_errors)
 
-    if len(extra_errors.values()):
+    if len(list(extra_errors.values())):
         raise toolkit.Invalid(extra_errors)
 
 
 def _set_value(data, field, value):
     key = None
-    for k in data.keys():
+    for k in list(data.keys()):
         if data[k] == field:
             key = k
             break
@@ -124,7 +122,7 @@ def validate_licenseAttributionByText(key, data, errors, context):
                 hasAttribution = value != ''
                 break
     if not hasAttribution:
-        current_indexes = [k[1] for k in data.keys()
+        current_indexes = [k[1] for k in list(data.keys())
                            if len(k) > 1 and k[0] == 'extras']
 
         new_index = max(current_indexes) + 1 if current_indexes else 0
@@ -166,9 +164,9 @@ def known_spatial_uri(key, data, errors, context):
                 data[('extras', new_index+1, 'value')] = poly
             return
 
-    mapping_file = config.get('ckanext.odsh.spatial.mapping')
+    mapping_file = tk.config.get('ckanext.odsh.spatial.mapping')
     try:
-        mapping_file = urllib2.urlopen(mapping_file)
+        mapping_file = urllib.request.urlopen(mapping_file)
     except Exception:
         raise Exception("Could not load spatial mapping file!")
 
@@ -196,7 +194,7 @@ def known_spatial_uri(key, data, errors, context):
 
 
 def next_extra_index(data):
-    current_indexes = [k[1] for k in data.keys()
+    current_indexes = [k[1] for k in list(data.keys())
                        if len(k) > 1 and k[0] == 'extras']
 
     return max(current_indexes) + 1 if current_indexes else 0
@@ -214,14 +212,14 @@ def tag_string_convert(key, data, errors, context):
     '''Takes a list of tags that is a comma-separated string (in data[key])
     and parses tag names. These are added to the data dict, enumerated. They
     are also validated.'''
-    if isinstance(data[key], basestring):
+    if isinstance(data[key], str):
         tags = [tag.strip()
                 for tag in data[key].split(',')
                 if tag.strip()]
     else:
         tags = data[key]
 
-    current_index = max([int(k[1]) for k in data.keys()
+    current_index = max([int(k[1]) for k in list(data.keys())
                          if len(k) == 3 and k[0] == 'tags'] + [-1])
 
     for num, tag in zip(count(current_index+1), tags):