def delete(self, *args, current_user=None, **kwargs): with_user = current_user # On supprime toutes les ressources attachées au jeu de données Resource = apps.get_model(app_label='idgo_admin', model_name='Resource') for resource in Resource.objects.filter(dataset=self): resource.delete(current_user=current_user, synchronize_dataset=False) # > > > > > > BETA < < < < < < # if BETA: ResourceBeta = apps.get_model(app_label='idgo_resource', model_name='Resource') for resource_beta in ResourceBeta.objects.filter(dataset=self): resource_beta._related.delete() resource_beta.delete() # > > > > > > BETA < < < < < < # # On supprime le package CKAN ckan_id = str(self.ckan_id) if with_user: username = with_user.username apikey = CkanHandler.get_user(username)['apikey'] with CkanUserHandler(apikey=apikey) as ckan_user: ckan_user.delete_dataset(ckan_id) else: CkanHandler.delete_dataset(ckan_id) CkanHandler.purge_dataset(ckan_id) # On supprime l'instance super().delete(*args, **kwargs)
def delete(self, *args, current_user=None, **kwargs): with_user = current_user # On supprime la ressource CKAN if with_user: username = with_user.username apikey = CkanHandler.get_user(username)['apikey'] with CkanUserHandler(apikey=apikey) as ckan_user: ckan_user.delete_resource(self.name) else: CkanHandler.delete_resource(self.name) # On supprime les ressources MRA try: MRAHandler.del_layer(self.name) ws_name = self.resource.dataset.organisation.slug if self.type == 'vector': MRAHandler.del_featuretype(ws_name, 'public', self.name) if self.type == 'raster': MRAHandler.del_coverage(ws_name, self.name, self.name) # MRAHandler.del_coveragestore(ws_name, self.name) except Exception as e: logger.error(e) pass # On supprime la table de données PostGIS try: drop_table(self.name) except Exception as e: logger.error(e) pass # Puis on supprime l'instance super().delete(*args, **kwargs)
def delete(self, *args, current_user=None, synchronize_dataset=True, **kwargs): with_user = current_user for layer in self.get_layers(): layer.delete(current_user=current_user) # On supprime la ressource CKAN ckan_id = str(self.ckan_id) if with_user: username = with_user.username apikey = CkanHandler.get_user(username)['apikey'] with CkanUserHandler(apikey=apikey) as ckan_user: ckan_user.delete_resource(ckan_id) else: CkanHandler.delete_resource(ckan_id) # On supprime l'instance super().delete(*args, **kwargs) # Ce n'est vraiment pas une bonne idée de synchroniser ici le dataset : self.dataset.date_modification = timezone.now().date() self.dataset.save(current_user=current_user, synchronize=synchronize_dataset, update_fields=['date_modification'])
def delete_ckan_resource(sender, instance, **kwargs): """Supprimer la resource CKAN à la suppression d'une resource.""" apikey = CkanHandler.get_user(instance.dataset.editor.username)['apikey'] with CkanUserHandler(apikey=apikey) as ckan: ckan.delete_resource(str(instance.ckan_id)) logger.info( "CKAN Resource \"{pk}\" has been deleted.".format(pk=instance.ckan_id))
def asynchronous_tasks(filename, dir, content_type, data, user_pk, dataset_pk, resource_pk, package_id, **kwargs): if content_type.endswith('zip'): unzip(filename, dir, flush=True) else: raise NotImplementedError('TODO') username = User.objects.get(pk=user_pk).username apikey = CkanHandler.get_user(username)['apikey'] html = get_html_content(dataset_pk, resource_pk) upload = io.BytesIO(html.encode('utf-8')) data['upload'] = upload ckan_package = CkanHandler.get_package(package_id) apikey = CkanHandler.get_user(username)['apikey'] with CkanUserHandler(apikey=apikey) as ckan: ckan.publish_resource(ckan_package, **data) upload.close()
def synchronize(instance, with_user=None): ckan_package = CkanHandler.get_package(str(instance.dataset.ckan_id)) username = with_user and with_user.username or instance.dataset.editor.username apikey = CkanHandler.get_user(username)['apikey'] location = os.path.join(DIRECTORY_STORAGE, str(instance.pk)) base_url = reverse('resource:directory_storage', kwargs={ 'dataset_id': instance.dataset.pk, 'resource_id': instance.pk }) files = iterate(location, base_url=base_url) html = render_to_string('resource/store/ckan_resource_template.html', context={'files': files}) upload = io.BytesIO(html.encode('utf-8')) data = { 'id': str(instance.ckan_id), 'url': instance.store.url, 'name': instance.title, 'description': instance.description, 'lang': instance.language, 'data_type': instance.resource_type, 'view_type': 'text_view', 'upload': upload, 'size': '', 'mimetype': 'text/html', 'format': '', 'api': '{}', 'restricted_by_jurisdiction': '', 'extracting_service': 'False', 'crs': '', 'restricted': json.dumps({'level': 'public'}), } with CkanUserHandler(apikey=apikey) as ckan: ckan.publish_resource(ckan_package, **data)
def handle(self, *args, **options): for resource in Resource.objects.exclude(organisations_allowed=None): dataset = resource.dataset ckan_params = { 'id': str(resource.ckan_id), 'restricted': json.dumps({ 'allowed_users': ','.join( get_all_users_for_organisations([ r.pk for r in resource.organisations_allowed.all() ])), 'level': 'only_allowed_users' }) } apikey = CkanHandler.get_user(dataset.editor.username)['apikey'] with CkanUserHandler(apikey=apikey) as ckan: package = ckan.get_package(str(dataset.ckan_id)) ckan.push_resource(package, **ckan_params)
def save_ckan_resource(self, with_user=None): resource = self.instance if with_user: username = with_user.username else: username = resource.dataset.editor.username upload = open(self.filename, 'rb') data = { 'id': str(resource.ckan_id), 'url': '', 'name': resource.title, 'description': resource.description, 'lang': resource.language, 'data_type': resource.resource_type, 'upload': upload, 'size': resource.upload.file_path.size, 'format': resource.format_type.ckan_format, 'mimetype': resource.format_type.mimetype[0], 'view_type': resource.format_type.ckan_view, # 'api': '{}', 'restricted_by_jurisdiction': 'False', 'extracting_service': 'False', 'crs': '', 'restricted': json.dumps({'level': 'public'}), } ckan_package = CkanHandler.get_package(str(resource.dataset.ckan_id)) apikey = CkanHandler.get_user(username)['apikey'] with CkanUserHandler(apikey=apikey) as ckan: ckan.publish_resource(ckan_package, **data) upload.close()
def synchronize(self, url=None, filename=None, content_type=None, file_extras=None, with_user=None): """Synchronizer le jeu de données avec l'instance de CKAN.""" # Identifiant de la resource CKAN : id = str(self.ckan_id) # Définition des propriétés du « package » : data = { 'crs': self.crs and self.crs.description or '', 'name': self.title, 'description': self.description, 'data_type': self.data_type, 'extracting_service': 'False', # I <3 CKAN 'format': self.format_type and self.format_type.ckan_format, 'view_type': self.format_type and self.format_type.ckan_view, 'id': id, 'lang': self.lang, 'restricted_by_jurisdiction': str(self.geo_restriction), 'url': url and url or '', 'api': '{}' } # TODO: Factoriser # (0) Aucune restriction if self.restricted_level == 'public': restricted = json.dumps({'level': 'public'}) # (1) Uniquement pour un utilisateur connecté elif self.restricted_level == 'registered': restricted = json.dumps({'level': 'registered'}) # (2) Seulement les utilisateurs indiquées elif self.restricted_level == 'only_allowed_users': restricted = json.dumps({ 'allowed_users': ','.join( self.profiles_allowed.exists() and [p.user.username for p in self.profiles_allowed.all()] or []), 'level': 'only_allowed_users' }) # (3) Les utilisateurs de cette organisation elif self.restricted_level == 'same_organization': restricted = json.dumps({ 'allowed_users': ','.join( get_all_users_for_organisations( self.organisations_allowed.all())), 'level': 'only_allowed_users' }) # (3) Les utilisateurs des organisations indiquées elif self.restricted_level == 'any_organization': restricted = json.dumps({ 'allowed_users': ','.join( get_all_users_for_organisations( self.organisations_allowed.all())), 'level': 'only_allowed_users' }) data['restricted'] = restricted if self.referenced_url: data['url'] = self.referenced_url if self.dl_url and filename: downloaded_file = File(open(filename, 'rb')) data['upload'] = downloaded_file data['size'] = downloaded_file.size data['mimetype'] = content_type if self.up_file and file_extras: data['upload'] = self.up_file.file data['size'] = file_extras.get('size') data['mimetype'] = file_extras.get('mimetype') if self.ftp_file: if not url: data['upload'] = self.ftp_file.file data['size'] = self.ftp_file.size data['mimetype'] = None # TODO if self.data_type == 'raw': if self.ftp_file or self.dl_url or self.up_file: data['resource_type'] = 'file.upload' elif self.referenced_url: data['resource_type'] = 'file' if self.data_type == 'annexe': data['resource_type'] = 'documentation' if self.data_type == 'service': data['resource_type'] = 'api' ckan_package = CkanHandler.get_package(str(self.dataset.ckan_id)) if with_user: username = with_user.username apikey = CkanHandler.get_user(username)['apikey'] with CkanUserHandler(apikey=apikey) as ckan: ckan.publish_resource(ckan_package, **data) else: return CkanHandler.publish_resource(ckan_package, **data)
def save(self, *args, current_user=None, synchronize=False, file_extras=None, **kwargs): # Version précédante de la ressource (avant modification) previous, created = self.pk \ and (Resource.objects.get(pk=self.pk), False) or (None, True) if previous: # crs est immuable sauf si le jeu de données change (Cf. plus bas) self.crs = previous.crs # Quelques valeur par défaut à la création de l'instance if created or not ( # Ou si l'éditeur n'est pas partenaire du CRIGE current_user and current_user.profile.crige_membership): # Mais seulement s'il s'agit de données SIG, sauf # qu'on ne le sait pas encore... self.geo_restriction = False self.ogc_services = True self.extractable = True # La restriction au territoire de compétence désactive toujours les services OGC if self.geo_restriction: self.ogc_services = False # Quelques contrôles sur les fichiers de données téléversée ou à télécharger filename = False content_type = None file_must_be_deleted = False # permet d'indiquer si les fichiers doivent être supprimés à la fin de la chaine de traitement publish_raw_resource = True # permet d'indiquer si les ressources brutes sont publiées dans CKAN if self.ftp_file: filename = self.ftp_file.file.name # Si la taille de fichier dépasse la limite autorisée, # on traite les données en fonction du type détecté if self.ftp_file.size > DOWNLOAD_SIZE_LIMIT: extension = self.format_type.extension.lower() if self.format_type.is_gis_format: try: gdalogr_obj = get_gdalogr_object(filename, extension) except NotDataGISError: # On essaye de traiter le jeux de données normalement, même si ça peut être long. pass else: if gdalogr_obj.__class__.__name__ == 'GdalOpener': s0 = str(self.ckan_id) s1, s2, s3 = s0[:3], s0[3:6], s0[6:] dir = os.path.join(CKAN_STORAGE_PATH, s1, s2) os.makedirs(dir, mode=0o777, exist_ok=True) shutil.copyfile(filename, os.path.join(dir, s3)) src = os.path.join(dir, s3) dst = os.path.join(dir, filename.split('/')[-1]) try: os.symlink(src, dst) except FileNotFoundError as e: logger.error(e) else: logger.debug( 'Created a symbolic link {dst} pointing to {src}.' .format(dst=dst, src=src)) # if gdalogr_obj.__class__.__name__ == 'OgrOpener': # On ne publie que le service OGC dans CKAN publish_raw_resource = False elif (self.up_file and file_extras): # GDAL/OGR ne semble pas prendre de fichier en mémoire.. # ..à vérifier mais si c'est possible comment indiquer le vsi en préfixe du filename ? super().save(*args, **kwargs) kwargs['force_insert'] = False filename = self.up_file.file.name file_must_be_deleted = True elif self.dl_url: try: directory, filename, content_type = download( self.dl_url, settings.MEDIA_ROOT, max_size=DOWNLOAD_SIZE_LIMIT) except SizeLimitExceededError as e: l = len(str(e.max_size)) if l > 6: m = '{0} mo'.format(Decimal(int(e.max_size) / 1024 / 1024)) elif l > 3: m = '{0} ko'.format(Decimal(int(e.max_size) / 1024)) else: m = '{0} octets'.format(int(e.max_size)) raise ValidationError(('La taille du fichier dépasse ' 'la limite autorisée : {0}.').format(m), code='dl_url') except Exception as e: if e.__class__.__name__ == 'HTTPError': if e.response.status_code == 404: msg = ('La ressource distante ne semble pas exister. ' "Assurez-vous que l'URL soit correcte.") if e.response.status_code == 403: msg = ("Vous n'avez pas l'autorisation pour " 'accéder à la ressource.') if e.response.status_code == 401: msg = ('Une authentification est nécessaire ' 'pour accéder à la ressource.') else: msg = 'Le téléchargement du fichier a échoué.' raise ValidationError(msg, code='dl_url') file_must_be_deleted = True # Synchronisation avec CKAN # ========================= # La synchronisation doit s'effectuer avant la publication des # éventuelles couches de données SIG car dans le cas des données # de type « raster », nous utilisons le filestore de CKAN. if synchronize and publish_raw_resource: self.synchronize(content_type=content_type, file_extras=file_extras, filename=filename, with_user=current_user) elif synchronize and not publish_raw_resource: url = reduce(urljoin, [ settings.CKAN_URL, 'dataset/', str(self.dataset.ckan_id) + '/', 'resource/', str(self.ckan_id) + '/', 'download/', Path(self.ftp_file.name).name ]) self.synchronize(url=url, with_user=current_user) # Détection des données SIG # ========================= if filename: # On vérifie s'il s'agit de données SIG, uniquement pour # les extensions de fichier autorisées.. extension = self.format_type.extension.lower() if self.format_type.is_gis_format: # Si c'est le cas, on monte les données dans la base PostGIS dédiée # et on déclare la couche au service OGC:WxS de l'organisation. # Mais d'abord, on vérifie si la ressource contient # déjà des « Layers », auquel cas il faudra vérifier si # la table de données a changée. existing_layers = {} if not created: existing_layers = dict( (re.sub('^(\w+)_[a-z0-9]{7}$', '\g<1>', layer.name), layer.name) for layer in self.get_layers()) try: # C'est carrément moche mais c'est pour aller vite. # Il faudrait factoriser tout ce bazar et créer # un décorateur pour gérer le rool-back sur CKAN. try: gdalogr_obj = get_gdalogr_object(filename, extension) except NotDataGISError: tables = [] pass else: try: self.format_type = ResourceFormats.objects.get( extension=extension, ckan_format=gdalogr_obj.format) # except ResourceFormats.MultipleObjectsReturned: # pass except Exception: pass # ========================== # Jeu de données vectorielle # ========================== if gdalogr_obj.__class__.__name__ == 'OgrOpener': # On convertit les données vers PostGIS try: tables = ogr2postgis( gdalogr_obj, update=existing_layers, epsg=self.crs and self.crs.auth_code or None, encoding=self.encoding) except NotOGRError as e: logger.warning(e) file_must_be_deleted and remove_file(filename) msg = ( "Le fichier reçu n'est pas reconnu " 'comme étant un jeu de données SIG correct.' ) raise ValidationError(msg, code='__all__') except DataDecodingError as e: logger.warning(e) file_must_be_deleted and remove_file(filename) msg = ( 'Impossible de décoder correctement les ' "données. Merci d'indiquer l'encodage " 'ci-dessous.') raise ValidationError(msg, code='encoding') except WrongDataError as e: logger.warning(e) file_must_be_deleted and remove_file(filename) msg = ( 'Votre ressource contient des données SIG que ' 'nous ne parvenons pas à lire correctement. ' 'Un ou plusieurs objets sont erronés.') raise ValidationError(msg) except NotFoundSrsError as e: logger.warning(e) file_must_be_deleted and remove_file(filename) msg = ( 'Votre ressource semble contenir des données SIG ' 'mais nous ne parvenons pas à détecter le système ' 'de coordonnées. Merci de sélectionner le code du ' 'CRS dans la liste ci-dessous.') raise ValidationError(msg, code='crs') except NotSupportedSrsError as e: logger.warning(e) file_must_be_deleted and remove_file(filename) msg = ( 'Votre ressource semble contenir des données SIG ' 'mais le système de coordonnées de celles-ci ' "n'est pas supporté par l'application.") raise ValidationError(msg, code='__all__') except ExceedsMaximumLayerNumberFixedError as e: logger.warning(e) file_must_be_deleted and remove_file(filename) raise ValidationError(e.__str__(), code='__all__') else: # Avant de créer des relations, l'objet doit exister if created: # S'il s'agit d'une création, alors on sauve l'objet. super().save(*args, **kwargs) kwargs['force_insert'] = False # Ensuite, pour tous les jeux de données SIG trouvés, # on crée le service ows à travers la création de `Layer` try: Layer = apps.get_model( app_label='idgo_admin', model_name='Layer') for table in tables: try: Layer.objects.get(name=table['id'], resource=self) except Layer.DoesNotExist: save_opts = { 'synchronize': synchronize } Layer.vector.create( bbox=table['bbox'], name=table['id'], resource=self, save_opts=save_opts) except Exception as e: logger.error(e) file_must_be_deleted and remove_file( filename) for table in tables: drop_table(table['id']) raise e # ========================== # Jeu de données matricielle # ========================== if gdalogr_obj.__class__.__name__ == 'GdalOpener': coverage = gdalogr_obj.get_coverage() try: tables = [ gdalinfo( coverage, update=existing_layers, epsg=self.crs and self.crs.auth_code or None) ] except NotFoundSrsError as e: logger.warning(e) file_must_be_deleted and remove_file(filename) msg = ( 'Votre ressource semble contenir des données SIG ' 'mais nous ne parvenons pas à détecter le système ' 'de coordonnées. Merci de sélectionner le code du ' 'CRS dans la liste ci-dessous.') raise ValidationError(msg, code='crs') except NotSupportedSrsError as e: logger.warning(e) file_must_be_deleted and remove_file(filename) msg = ( 'Votre ressource semble contenir des données SIG ' 'mais le système de coordonnées de celles-ci ' "n'est pas supporté par l'application.") raise ValidationError(msg, code='__all__') else: if created: # S'il s'agit d'une création, alors on sauve l'objet. super().save(*args, **kwargs) kwargs['force_insert'] = False # Super Crado Code s0 = str(self.ckan_id) s1, s2, s3 = s0[:3], s0[3:6], s0[6:] dir = os.path.join(CKAN_STORAGE_PATH, s1, s2) src = os.path.join(dir, s3) dst = os.path.join(dir, filename.split('/')[-1]) try: os.symlink(src, dst) except FileExistsError as e: logger.warning(e) except FileNotFoundError as e: logger.error(e) else: logger.debug( 'Created a symbolic link {dst} pointing to {src}.' .format(dst=dst, src=src)) try: Layer = apps.get_model(app_label='idgo_admin', model_name='Layer') for table in tables: try: Layer.objects.get(name=table['id'], resource=self) except Layer.DoesNotExist: Layer.raster.create(bbox=table['bbox'], name=table['id'], resource=self) except Exception as e: logger.error(e) file_must_be_deleted and remove_file(filename) raise e except Exception as e: if created: if current_user: username = current_user.username apikey = CkanHandler.get_user(username)['apikey'] with CkanUserHandler(apikey) as ckan: ckan.delete_resource(str(self.ckan_id)) else: CkanHandler.delete_resource(str(self.ckan_id)) for layer in self.get_layers(): layer.delete(current_user=current_user) # Puis on « raise » l'erreur raise e # On met à jour les champs de la ressource SupportedCrs = apps.get_model(app_label='idgo_admin', model_name='SupportedCrs') crs = [ SupportedCrs.objects.get(auth_name='EPSG', auth_code=table['epsg']) for table in tables ] # On prend la première valeur (c'est moche) self.crs = crs and crs[0] or None # Si les données changent.. if existing_layers and \ previous.get_layers() != self.get_layers(): # on supprime les anciens `layers`.. for layer in previous.get_layers(): layer.delete() #### if self.get_layers(): extent = self.get_layers().aggregate( models.Extent('bbox')).get('bbox__extent') if extent: xmin, ymin = extent[0], extent[1] xmax, ymax = extent[2], extent[3] setattr(self, 'bbox', bounds_to_wkt(xmin, ymin, xmax, ymax)) else: # Si la ressource n'est pas de type SIG, on passe les trois arguments # qui concernent exclusivement ces dernières à « False ». self.geo_restriction = False self.ogc_services = False self.extractable = False super().save(*args, **kwargs) # Puis dans tous les cas.. # on met à jour le statut des couches du service cartographique.. if not created: self.update_enable_layers_status() # on supprime les données téléversées ou téléchargées.. if file_must_be_deleted: remove_file(filename) # [Crado] on met à jour la ressource CKAN if synchronize: CkanHandler.update_resource(str(self.ckan_id), extracting_service=str( self.extractable)) for layer in self.get_layers(): layer.save(synchronize=synchronize) self.dataset.date_modification = timezone.now().date() self.dataset.save(current_user=None, synchronize=True, update_fields=['date_modification'])
def synchronize(self, with_user=None, activate=None): """Synchronizer le jeu de données avec l'instance de CKAN.""" # Identifiant du package CKAN : id = self.ckan_id and str(self.ckan_id) or None # Si la valeur est `None`, alors il s'agit d'une création. # Définition des propriétés du « paquet » # ======================================= datatype = [item.slug for item in self.data_type.all()] date_creation = self.date_creation and str(self.date_creation) or '' date_modification = self.date_modification and str( self.date_modification) or '' date_publication = self.date_publication and str( self.date_publication) or '' broadcaster_name = self.broadcaster_name or \ self.support and self.support.name or DEFAULT_PLATFORM_NAME broadcaster_email = self.broadcaster_email or \ self.support and self.support.email or DEFAULT_CONTACT_EMAIL geocover = self.geocover or '' granularity = self.granularity and self.granularity.slug or '' licenses = [license['id'] for license in CkanHandler.get_licenses()] if self.license and self.license.ckan_id in licenses: license_id = self.license.ckan_id else: license_id = '' ows = False Resource = apps.get_model(app_label='idgo_admin', model_name='Resource') for resource in Resource.objects.filter(dataset=self): ows = resource.ogc_services spatial = self.bbox and self.bbox.geojson or '' support = self.support and self.support.slug or '' tags = [{'name': keyword.name} for keyword in self.keywords.all()] try: thumbnail = urljoin(DOMAIN_NAME, self.thumbnail.url) except ValueError: thumbnail = '' # On vérifie si le jeu de données est un cas particulier # de jeu de données moissonné CKAN/CSW/DCAT remote_url = None if ENABLE_CKAN_HARVESTER: # (1) DCAT RemoteCkanDataset = apps.get_model(app_label='idgo_admin', model_name='RemoteCkanDataset') try: remote_dataset = RemoteCkanDataset.objects.get(dataset=self) except RemoteCkanDataset.DoesNotExist: pass else: remote_url = remote_dataset.url if ENABLE_CSW_HARVESTER: # (2) CSW RemoteCswDataset = apps.get_model(app_label='idgo_admin', model_name='RemoteCswDataset') try: remote_dataset = RemoteCswDataset.objects.get(dataset=self) except RemoteCswDataset.DoesNotExist: pass else: remote_url = remote_dataset.url if ENABLE_DCAT_HARVESTER: # (3) DCAT RemoteDcatDataset = apps.get_model(app_label='idgo_admin', model_name='RemoteDcatDataset') try: remote_dataset = RemoteDcatDataset.objects.get(dataset=self) except RemoteDcatDataset.DoesNotExist: pass else: remote_url = remote_dataset.url data = { 'author': self.owner_name, 'author_email': self.owner_email, 'datatype': datatype, 'dataset_creation_date': date_creation, 'dataset_modification_date': date_modification, 'dataset_publication_date': date_publication, 'frequency': self.update_frequency or 'unknown', 'geocover': geocover, 'granularity': granularity, 'groups': [], 'inspire_url': self.geonet_url, 'license_id': license_id, 'maintainer': broadcaster_name, 'maintainer_email': broadcaster_email, 'name': self.slug, 'notes': self.description, 'owner_org': str(self.organisation.ckan_id), 'ows': str(ows), # IMPORTANT 'private': self.private, 'remote_url': remote_url or '', 'spatial': spatial, 'support': support, 'tags': tags, 'title': self.title, 'thumbnail': thumbnail, 'url': '', # IMPORTANT } if activate is not None: data['state'] = activate and 'active' or 'deleted' # Synchronisation des catégories : for category in self.categories.all(): data['groups'].append({'name': category.slug}) organisation_id = str(self.organisation.ckan_id) # Synchronisation de l'organisation ; si l'organisation # n'existe pas il faut la créer ckan_organisation = CkanHandler.get_organisation(organisation_id) if not ckan_organisation: CkanHandler.add_organisation(self.organisation) # et si l'organisation est désactiver il faut l'activer elif ckan_organisation.get('state') == 'deleted': CkanHandler.activate_organisation(organisation_id) if with_user: username = with_user.username # TODO: C'est très lourd de faire cela systématiquement -> voir pour améliorer cela CkanHandler.add_user_to_organisation(username, organisation_id) for category in self.categories.all(): category_id = str(category.ckan_id) CkanHandler.add_user_to_group(username, category_id) # apikey = CkanHandler.get_user(username)['apikey'] with CkanUserHandler(apikey=apikey) as ckan_user: return ckan_user.publish_dataset(id=id, **data) else: return CkanHandler.publish_dataset(id=id, **data)