Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fixes #12627] Include assets inside B/R #12628

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions geonode/assets/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def get_default_handler(self) -> AssetHandlerInterface:
return self._default_handler

def get_handler(self, asset):
asset = asset.get_real_instance() if isinstance(asset, Asset) else asset
asset_cls = asset if isinstance(asset, type) else asset.__class__
ret = self._registry.get(asset_cls, None)
if not ret:
Expand Down
16 changes: 16 additions & 0 deletions geonode/assets/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@

class IndexLocalLinkUrlHandler:
def get_link_url(self, asset: LocalAsset):
asset = asset.get_real_instance()

Check warning on line 31 in geonode/assets/local.py

View check run for this annotation

Codecov / codecov/patch

geonode/assets/local.py#L31

Added line #L31 was not covered by tests
mattiagiupponi marked this conversation as resolved.
Show resolved Hide resolved
if not isinstance(asset, LocalAsset):
raise TypeError("Only localasset are allowed")

Check warning on line 33 in geonode/assets/local.py

View check run for this annotation

Codecov / codecov/patch

geonode/assets/local.py#L33

Added line #L33 was not covered by tests
return build_absolute_uri(reverse("assets-link", args=(asset.pk,))) + f"/{os.path.basename(asset.location[0])}"


Expand Down Expand Up @@ -76,6 +79,7 @@
Removes the files related to an Asset.
Only files within the Assets directory are removed
"""
asset = self.__force_real_instance(asset)
if self._are_files_managed(asset):
logger.info(f"Removing files for asset {asset.pk}")
base = self._get_managed_dir(asset)
Expand All @@ -85,6 +89,7 @@
logger.info(f"Not removing unmanaged files for asset {asset.pk}")

def replace_data(self, asset: LocalAsset, files: list):
asset = self.__force_real_instance(asset)
self.remove_data(asset)
asset.location = files
asset.save()
Expand Down Expand Up @@ -123,6 +128,7 @@

def clone(self, source: LocalAsset) -> LocalAsset:
# get a new asset instance to be edited and stored back
source = self.__force_real_instance(source)
asset = LocalAsset.objects.get(pk=source.pk)

# only copy files if they are managed
Expand Down Expand Up @@ -204,12 +210,22 @@

return managed_dir

@classmethod
def __force_real_instance(cls, asset):
asset = asset.get_real_instance()
if not isinstance(asset, LocalAsset):
raise TypeError(f"Real instance of asset {asset} is not {cls.handled_asset_class()}")

Check warning on line 217 in geonode/assets/local.py

View check run for this annotation

Codecov / codecov/patch

geonode/assets/local.py#L217

Added line #L217 was not covered by tests
return asset


class LocalAssetDownloadHandler(AssetDownloadHandlerInterface):

def create_response(
self, asset: LocalAsset, attachment: bool = False, basename: str = None, path: str = None
) -> HttpResponse:
asset = asset.get_real_instance()
mattiagiupponi marked this conversation as resolved.
Show resolved Hide resolved
if not isinstance(asset, LocalAsset):
raise TypeError("Only localasset are allowed")

Check warning on line 228 in geonode/assets/local.py

View check run for this annotation

Codecov / codecov/patch

geonode/assets/local.py#L228

Added line #L228 was not covered by tests
if not asset.location:
return HttpResponse("Asset does not contain any data", status=500)

Expand Down
14 changes: 13 additions & 1 deletion geonode/assets/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@
from django.contrib.auth import get_user_model


class AssetPolymorphicManager(PolymorphicManager):
"""
This override is required for the dump procedure.
Otherwise django is not able to dump the base objects
and will be upcasted to polymorphic models
https://github.com/jazzband/django-polymorphic/blob/cfd49b26d580d99b00dcd43a02409ce439a2c78f/polymorphic/base.py#L161-L175
"""

def get_queryset(self):
return super().get_queryset().non_polymorphic()


class Asset(PolymorphicModel):
"""
A generic data linked to a ResourceBase
Expand All @@ -16,7 +28,7 @@ class Asset(PolymorphicModel):
owner = models.ForeignKey(get_user_model(), null=False, blank=False, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)

objects = PolymorphicManager()
objects = AssetPolymorphicManager()

class Meta:
verbose_name_plural = "Assets"
Expand Down
10 changes: 5 additions & 5 deletions geonode/assets/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def test_creation_and_delete_data_cloned(self):
asset.save()
self.assertIsInstance(asset, LocalAsset)

reloaded = Asset.objects.get(pk=asset.pk)
reloaded = LocalAsset.objects.get(pk=asset.pk)
self.assertIsNotNone(reloaded)
self.assertIsInstance(reloaded, LocalAsset)
file = reloaded.location[0]
Expand Down Expand Up @@ -103,7 +103,7 @@ def test_creation_and_delete_data_external(self):
asset.save()
self.assertIsInstance(asset, LocalAsset)

reloaded = Asset.objects.get(pk=asset.pk)
reloaded = LocalAsset.objects.get(pk=asset.pk)
self.assertIsNotNone(reloaded)
self.assertIsInstance(reloaded, LocalAsset)
file = reloaded.location[0]
Expand All @@ -128,7 +128,7 @@ def test_clone_and_delete_data_managed(self):
asset.save()
self.assertIsInstance(asset, LocalAsset)

reloaded = Asset.objects.get(pk=asset.pk)
reloaded = LocalAsset.objects.get(pk=asset.pk)
cloned = asset_handler.clone(reloaded)
self.assertNotEqual(reloaded.pk, cloned.pk)

Expand Down Expand Up @@ -161,7 +161,7 @@ def test_clone_and_delete_data_unmanaged(self):
asset.save()
self.assertIsInstance(asset, LocalAsset)

reloaded = Asset.objects.get(pk=asset.pk)
reloaded = LocalAsset.objects.get(pk=asset.pk)
cloned = asset_handler.clone(reloaded)

self.assertEqual(reloaded.location[0], cloned.location[0])
Expand Down Expand Up @@ -268,7 +268,7 @@ def _setup_test(self, u, _file=ONE_JSON):
asset.save()
self.assertIsInstance(asset, LocalAsset)

reloaded = Asset.objects.get(pk=asset.pk)
reloaded = LocalAsset.objects.get(pk=asset.pk)

# put two more files in the asset dir
asset_dir = os.path.dirname(reloaded.location[0])
Expand Down
5 changes: 3 additions & 2 deletions geonode/assets/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ def get_default_asset(resource: ResourceBase, link_type=None) -> Asset or None:
filters = {"link__resource": resource}
if link_type:
filters["link__link_type"] = link_type

return Asset.objects.filter(**filters).first()
asset = Asset.objects.filter(**filters).first()
return asset.get_real_instance() if asset else None


DEFAULT_TYPES = {"image": ["jpg", "jpeg", "gif", "png", "bmp", "svg"]}
Expand All @@ -55,6 +55,7 @@ def find_type(ext):


def create_link(resource, asset, link_type=None, extension=None, name=None, mime=None, asset_handler=None, **kwargs):
asset = asset.get_real_instance()
asset_handler = asset_handler or asset_handler_registry.get_handler(asset)

if not link_type or not extension or not name:
Expand Down
36 changes: 24 additions & 12 deletions geonode/br/management/commands/backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
import re
import logging

from geonode.assets.local import LocalAssetHandler
from geonode.assets.models import LocalAsset

from .utils import utils

from requests.auth import HTTPBasicAuth
Expand Down Expand Up @@ -166,23 +169,21 @@

logger.info(f" - Dumping '{app_name}' into '{dump_name}.json'")
# Point stdout at a file for dumping data to.
with open(os.path.join(fixtures_target, f"{dump_name}.json"), "w") as output:
call_command("dumpdata", app_name, format="json", indent=2, stdout=output)
output_file = os.path.join(fixtures_target, f"{dump_name}.json")
call_command("dumpdata", app_name, output=output_file)

Check warning on line 173 in geonode/br/management/commands/backup.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/backup.py#L172-L173

Added lines #L172 - L173 were not covered by tests

# Store Media Root
logger.info("*** Dumping GeoNode media folder...")

media_root = settings.MEDIA_ROOT
media_folder = os.path.join(target_folder, utils.MEDIA_ROOT)
if not os.path.exists(media_folder):
os.makedirs(media_folder, exist_ok=True)
logger.info("*** Dumping GeoNode media folder...")
self.backup_folder(folder=media_folder, root=settings.MEDIA_ROOT, config=config)

Check warning on line 179 in geonode/br/management/commands/backup.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/backup.py#L178-L179

Added lines #L178 - L179 were not covered by tests

copy_tree(
media_root,
media_folder,
ignore=utils.ignore_time(config.gs_data_dt_filter[0], config.gs_data_dt_filter[1]),
)
logger.info(f"Saved media files from '{media_root}'")
logger.info("*** Dumping GeoNode assets folder...")
assets_folder = os.path.join(target_folder, utils.ASSETS_ROOT)
self.backup_folder(folder=assets_folder, root=settings.ASSETS_ROOT, config=config)

Check warning on line 183 in geonode/br/management/commands/backup.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/backup.py#L181-L183

Added lines #L181 - L183 were not covered by tests
for instance in LocalAsset.objects.iterator():
if not LocalAssetHandler._are_files_managed(instance):
logger.warning(f"The file for the asset with id {instance.pk} were not backup since is not managed by GeoNode")

Check warning on line 186 in geonode/br/management/commands/backup.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/backup.py#L186

Added line #L186 was not covered by tests

# Create Final ZIP Archive
logger.info("*** Creating final ZIP archive...")
Expand Down Expand Up @@ -214,6 +215,17 @@

return str(os.path.join(backup_dir, f"{dir_time_suffix}.zip"))

def backup_folder(self, folder, root, config):
if not os.path.exists(folder):
os.makedirs(root, exist_ok=True)

Check warning on line 220 in geonode/br/management/commands/backup.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/backup.py#L220

Added line #L220 was not covered by tests

copy_tree(

Check warning on line 222 in geonode/br/management/commands/backup.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/backup.py#L222

Added line #L222 was not covered by tests
root,
folder,
ignore=utils.ignore_time(config.gs_data_dt_filter[0], config.gs_data_dt_filter[1]),
)
logger.info(f"Saved files from '{root}'")

Check warning on line 227 in geonode/br/management/commands/backup.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/backup.py#L227

Added line #L227 was not covered by tests

def create_geoserver_backup(self, config, settings, target_folder, ignore_errors):
# Create GeoServer Backup
url = settings.OGC_SERVER["default"]["LOCATION"]
Expand Down
44 changes: 34 additions & 10 deletions geonode/br/management/commands/restore.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
from typing import Union
from datetime import datetime

from geonode.assets.models import LocalAsset

from .utils import utils

from distutils import dir_util
Expand Down Expand Up @@ -265,13 +267,16 @@
# Write Checks
media_root = settings.MEDIA_ROOT
media_folder = os.path.join(target_folder, utils.MEDIA_ROOT)

assets_root = settings.ASSETS_ROOT
assets_folder = os.path.join(target_folder, utils.ASSETS_ROOT)

Check warning on line 271 in geonode/br/management/commands/restore.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/restore.py#L270-L271

Added lines #L270 - L271 were not covered by tests
try:
logger.info("*** Performing some checks...")
logger.info(f"[Sanity Check] Full Write Access to restore folder: '{restore_folder}' ...")
chmod_tree(restore_folder)
logger.info(f"[Sanity Check] Full Write Access to media root: '{media_root}' ...")
chmod_tree(media_root)
logger.info(f"[Sanity Check] Full Write Access to assets root: '{assets_root}' ...")
chmod_tree(assets_root)

Check warning on line 279 in geonode/br/management/commands/restore.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/restore.py#L278-L279

Added lines #L278 - L279 were not covered by tests
except Exception as e:
if notify:
restore_notification.apply_async(
Expand Down Expand Up @@ -393,15 +398,23 @@

# Restore Media Root
logger.info("*** Restore media root...")
if config.gs_data_dt_filter[0] is None:
shutil.rmtree(media_root, ignore_errors=True)

if not os.path.exists(media_root):
os.makedirs(media_root, exist_ok=True)

copy_tree(media_folder, media_root)
chmod_tree(media_root)
logger.info(f"Media files restored into '{media_root}'.")
self.restore_folder(config, media_root, media_folder)
logger.info("*** Restore assets root...")
self.restore_folder(config, assets_root, assets_folder)

Check warning on line 403 in geonode/br/management/commands/restore.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/restore.py#L401-L403

Added lines #L401 - L403 were not covered by tests

logger.info("*** Update Assets path...")

Check warning on line 405 in geonode/br/management/commands/restore.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/restore.py#L405

Added line #L405 was not covered by tests
for instance in LocalAsset.objects.iterator():
should_be_updated = any(settings.ASSETS_ROOT not in loc for loc in instance.location)
if should_be_updated:
new_assets = []

Check warning on line 409 in geonode/br/management/commands/restore.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/restore.py#L409

Added line #L409 was not covered by tests
for loc in instance.location:
if settings.ASSETS_ROOT not in loc:
new_assets.append(loc.replace(os.path.dirname(os.path.dirname(loc)), settings.ASSETS_ROOT))

Check warning on line 412 in geonode/br/management/commands/restore.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/restore.py#L412

Added line #L412 was not covered by tests
else:
new_assets.append(loc)
instance.location = new_assets
instance.save()
logger.info("*** Assets path updated...")

Check warning on line 417 in geonode/br/management/commands/restore.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/restore.py#L414-L417

Added lines #L414 - L417 were not covered by tests

# store backup info
restored_backup = RestoredBackup(
Expand Down Expand Up @@ -441,6 +454,17 @@
logger.info("*** Final filesystem cleanup ...")
shutil.rmtree(restore_folder)

def restore_folder(self, config, root, folder):
if config.gs_data_dt_filter[0] is None:
shutil.rmtree(root, ignore_errors=True)

Check warning on line 459 in geonode/br/management/commands/restore.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/restore.py#L459

Added line #L459 was not covered by tests

if not os.path.exists(root):
os.makedirs(root, exist_ok=True)

Check warning on line 462 in geonode/br/management/commands/restore.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/restore.py#L462

Added line #L462 was not covered by tests

copy_tree(folder, root)
chmod_tree(root)
logger.info(f"Files restored into '{root}'.")

Check warning on line 466 in geonode/br/management/commands/restore.py

View check run for this annotation

Codecov / codecov/patch

geonode/br/management/commands/restore.py#L464-L466

Added lines #L464 - L466 were not covered by tests

def validate_backup_file_options(self, **options) -> None:
"""
Method validating --backup-file and --backup-files-dir options
Expand Down
4 changes: 2 additions & 2 deletions geonode/br/management/commands/settings_docker_sample.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ dumprasterdata = yes
# data_layername_exclude_filter = {comma separated list of layernames, optionally with glob syntax} e.g.: tuscany_*,italy

[fixtures]
apps = contenttypes,auth,people,groups,account,guardian,admin,actstream,announcements,avatar,base,documents,geoserver,invitations,pinax_notifications,harvesting,services,layers,maps,oauth2_provider,sites,socialaccount,taggit,tastypie,upload,user_messages,geonode_themes,geoapps,favorite,geonode_client
dumps = contenttypes,auth,people,groups,account,guardian,admin,actstream,announcements,avatar,base,documents,geoserver,invitations,pinax_notifications,harvesting,services,layers,maps,oauth2_provider,sites,socialaccount,taggit,tastypie,upload,user_messages,geonode_themes,geoapps,favorite,geonode_client
apps = contenttypes,auth,people,groups,account,guardian,admin,actstream,announcements,avatar,assets,base,documents,geoserver,invitations,pinax_notifications,harvesting,services,layers,maps,oauth2_provider,sites,socialaccount,taggit,tastypie,upload,user_messages,geonode_themes,geoapps,favorite,geonode_client
dumps = contenttypes,auth,people,groups,account,guardian,admin,actstream,announcements,avatar,assets,base,documents,geoserver,invitations,pinax_notifications,harvesting,services,layers,maps,oauth2_provider,sites,socialaccount,taggit,tastypie,upload,user_messages,geonode_themes,geoapps,favorite,geonode_client

4 changes: 2 additions & 2 deletions geonode/br/management/commands/settings_sample.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ dumprasterdata = yes
# data_layername_exclude_filter = {comma separated list of layernames, optionally with glob syntax} e.g.: tuscany_*,italy

[fixtures]
apps = contenttypes,auth,people,groups,account,guardian,admin,actstream,announcements,avatar,base,documents,geoserver,invitations,pinax_notifications,harvesting,services,layers,maps,oauth2_provider,sites,socialaccount,taggit,tastypie,upload,user_messages,geonode_themes,geoapps,favorite,geonode_client
dumps = contenttypes,auth,people,groups,account,guardian,admin,actstream,announcements,avatar,base,documents,geoserver,invitations,pinax_notifications,harvesting,services,layers,maps,oauth2_provider,sites,socialaccount,taggit,tastypie,upload,user_messages,geonode_themes,geoapps,favorite,geonode_client
apps = contenttypes,auth,people,groups,account,guardian,admin,actstream,announcements,avatar,assets,base,documents,geoserver,invitations,pinax_notifications,harvesting,services,layers,maps,oauth2_provider,sites,socialaccount,taggit,tastypie,upload,user_messages,geonode_themes,geoapps,favorite,geonode_client
dumps = contenttypes,auth,people,groups,account,guardian,admin,actstream,announcements,avatar,assets,base,documents,geoserver,invitations,pinax_notifications,harvesting,services,layers,maps,oauth2_provider,sites,socialaccount,taggit,tastypie,upload,user_messages,geonode_themes,geoapps,favorite,geonode_client
1 change: 1 addition & 0 deletions geonode/br/management/commands/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
TEMPLATE_DIRS = "template_dirs"
LOCALE_PATHS = "locale_dirs"
EXTERNAL_ROOT = "external"
ASSETS_ROOT = "assets_data"


logger = logging.getLogger(__name__)
Expand Down
4 changes: 2 additions & 2 deletions geonode/documents/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from django.utils.functional import classproperty
from django.utils.translation import gettext_lazy as _

from geonode.assets.models import Asset
from geonode.assets.models import LocalAsset
from geonode.client.hooks import hookset
from geonode.base.models import ResourceBase
from geonode.groups.conf import settings as groups_settings
Expand Down Expand Up @@ -79,7 +79,7 @@ def compact_permission_labels(cls):

@property
def files(self):
asset = Asset.objects.filter(link__resource=self).first()
asset = LocalAsset.objects.filter(link__resource=self).first()
return asset.location if asset else []

@property
Expand Down
Loading