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 #12326] Assets: implement migration for old uploaded files #12364

Closed
wants to merge 6 commits into from
Closed
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 .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ SECRET_KEY='{secret_key}'

STATIC_ROOT=/mnt/volumes/statics/static/
MEDIA_ROOT=/mnt/volumes/statics/uploaded/
ASSETS_ROOT=/mnt/volumes/statics/assets/
GEOIP_PATH=/mnt/volumes/statics/geoip.db

CACHE_BUSTING_STATIC_ENABLED=False
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ geonode/local_settings.py

# Uploaded files
geonode/uploaded
geonode/assets_data

#Testing output
.coverage
Expand Down
Empty file added geonode/assets/__init__.py
Empty file.
55 changes: 55 additions & 0 deletions geonode/assets/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import json
import logging
from django.db import models
from django.forms import widgets
from django.contrib import admin

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

logger = logging.getLogger(__name__)


class PrettyJSONWidget(widgets.Textarea):

def format_value(self, value):
try:
value = json.dumps(json.loads(value), indent=2, sort_keys=True)
# these lines will try to adjust size of TextArea to fit to content
row_lengths = [len(r) for r in value.split("\n")]
self.attrs["rows"] = min(max(len(row_lengths) + 2, 10), 30)
self.attrs["cols"] = min(max(max(row_lengths) + 2, 40), 120)
return value
except Exception as e:
logger.warning("Error while formatting JSON: {}".format(e))
return super(PrettyJSONWidget, self).format_value(value)


@admin.register(LocalAsset)
class LocalAssetAdmin(admin.ModelAdmin):
model = LocalAsset

list_display = ("id", "title", "type", "owner", "created_formatted", "managed", "links", "link0")
list_display_links = ("id", "title")

formfield_overrides = {models.JSONField: {"widget": PrettyJSONWidget}}

def created_formatted(self, obj):
return obj.created.strftime("%Y-%m-%d %H:%M:%S")

def links(self, obj):
return Link.objects.filter(asset=obj).count()

def link0(self, obj):
link = Link.objects.filter(asset=obj).first()
return f"{link.link_type} {link.extension}: {link.name}" if link else None

def managed(self, obj) -> bool:
try:
return LocalAssetHandler._is_file_managed(obj.location[0])
except Exception as e:
logger.error(f"Bad location for asset obj: {e}")
return None

managed.boolean = True
35 changes: 35 additions & 0 deletions geonode/assets/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#########################################################################
#
# Copyright (C) 2016 OSGeo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#########################################################################
from django.apps import AppConfig

from geonode.notifications_helper import NotificationsAppConfigBase


class BaseAppConfig(NotificationsAppConfigBase, AppConfig):
name = "geonode.assets"

def ready(self):
super().ready()
run_setup_hooks()


def run_setup_hooks(*args, **kwargs):
from geonode.assets.handlers import asset_handler_registry

asset_handler_registry.init_registry()
91 changes: 91 additions & 0 deletions geonode/assets/handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import logging

from django.conf import settings
from django.http import HttpResponse
from django.utils.module_loading import import_string

from geonode.assets.models import Asset

logger = logging.getLogger(__name__)


class AssetDownloadHandlerInterface:

def create_response(self, asset: Asset, attachment: bool = False, basename=None, path=None) -> HttpResponse:
raise NotImplementedError()


class AssetHandlerInterface:

def handled_asset_class(self):
raise NotImplementedError()

def create(self, title, description, type, owner, *args, **kwargs):
raise NotImplementedError()

def remove_data(self, asset: Asset, **kwargs):
raise NotImplementedError()

def replace_data(self, asset: Asset, files: list):
raise NotImplementedError()

def clone(self, asset: Asset) -> Asset:
"""
Creates a copy in the DB and copies the underlying data as well
"""
raise NotImplementedError()

def create_link_url(self, asset: Asset) -> str:
raise NotImplementedError()

def get_download_handler(self, asset: Asset = None) -> AssetDownloadHandlerInterface:
raise NotImplementedError()

def get_storage_manager(self, asset=None):
raise NotImplementedError()


class AssetHandlerRegistry:
_registry = {}
_default_handler = None

def init_registry(self):
self.register_asset_handlers()
self.set_default_handler()

def register_asset_handlers(self):
for module_path in settings.ASSET_HANDLERS:
handler = import_string(module_path)
self.register(handler)
logger.info(f"Registered Asset handlers: {', '.join(settings.ASSET_HANDLERS)}")

def set_default_handler(self):
# check if declared class is registered
for handler in self._registry.values():
if ".".join([handler.__class__.__module__, handler.__class__.__name__]) == settings.DEFAULT_ASSET_HANDLER:
self._default_handler = handler
break

if self._default_handler is None:
logger.error(f"Could not set default asset handler class {settings.DEFAULT_ASSET_HANDLER}")
else:
logger.info(f"Default Asset handler {settings.DEFAULT_ASSET_HANDLER}")

def register(self, asset_handler_class):
self._registry[asset_handler_class.handled_asset_class()] = asset_handler_class()

def get_default_handler(self) -> AssetHandlerInterface:
return self._default_handler

def get_handler(self, asset):
asset_cls = asset if isinstance(asset, type) else asset.__class__
ret = self._registry.get(asset_cls, None)
if not ret:
logger.warning(f"Could not find asset handler for {asset_cls}::{asset.__class__}")
logger.warning("Available asset types:")
for k, v in self._registry.items():
logger.warning(f"{k} --> {v.__class__.__name__}")
return ret


asset_handler_registry = AssetHandlerRegistry()
Loading