66 changes: 66 additions & 0 deletions geonode/layers/populate_layers_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#########################################################################

from geonode.layers.models import Style, Attribute, Layer
import notification
from django.utils.translation import ugettext as _


styles = [{"name": "test_style_1",
"sld_url": "http://localhost:8080/geoserver/rest/styles/test_style.sld",
Expand Down Expand Up @@ -121,3 +124,66 @@ def create_layer_data():
visible=attr['visible'],
display_order=attr['display_order']
)


def create_notifications():
notification.models.NoticeType.create(
"layer_created",
_("Layer Created"),
_("A Layer was created"))
notification.models.NoticeType.create(
"layer_updated",
_("Layer Updated"),
_("A Layer was updated"))
notification.models.NoticeType.create(
"layer_deleted",
_("Layer Deleted"),
_("A Layer was deleted"))
notification.models.NoticeType.create(
"layer_comment",
_("Comment on Layer"),
_("A layer was commented on"))
notification.models.NoticeType.create(
"layer_rated",
_("Rating for Layer"),
_("A rating was given to a layer"))
notification.models.NoticeType.create(
"request_download_resourcebase",
_("Request download to an owner"),
_("A request has been sent to the owner"))
notification.models.NoticeType.create(
"map_created",
_("Map Created"),
_("A Map was created"))
notification.models.NoticeType.create(
"map_updated",
_("Map Updated"),
_("A Map was updated"))
notification.models.NoticeType.create(
"map_deleted",
_("Map Deleted"),
_("A Map was deleted"))
notification.models.NoticeType.create(
"profile_created",
_("Profile Created"),
_("A Profile was created"))
notification.models.NoticeType.create(
"profile_updated",
_("Profile Updated"),
_("A Profile was updated"))
notification.models.NoticeType.create(
"profile_deleted",
_("Profile Deleted"),
_("A Profile was deleted"))
notification.models.NoticeType.create(
"document_created",
_("Document Created"),
_("A Document was created"))
notification.models.NoticeType.create(
"document_updated",
_("Document Updated"),
_("A Document was updated"))
notification.models.NoticeType.create(
"document_deleted",
_("Document Deleted"),
_("A Document was deleted"))
2 changes: 1 addition & 1 deletion geonode/layers/templates/layers/layer_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ <h4>{% trans 'Average Rating' %}</h4>
<ul class="list-group">
{% if resource.storeType != "remoteStore" %}
<li class="list-group-item">
{% if links or links_download %}
{% if links_download %}
<button class="btn btn-primary btn-md btn-block" data-toggle="modal" data-target="#download-layer">{% trans "Download Layer" %}</button>
{% else %}
{% if request.user.is_authenticated %}
Expand Down
11 changes: 8 additions & 3 deletions geonode/layers/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
from geonode.base.models import TopicCategory
from geonode.base.populate_test_data import create_models, all_public
from geonode.layers.forms import JSONField, LayerUploadForm
from .populate_layers_data import create_layer_data
from .populate_layers_data import create_layer_data, create_notifications


class LayersTest(TestCase):
Expand All @@ -60,6 +60,7 @@ def setUp(self):
self.passwd = 'admin'
create_models(type='layer')
create_layer_data()
create_notifications()
self.anonymous_user = get_anonymous_user()

# Data Tests
Expand Down Expand Up @@ -755,14 +756,18 @@ class UnpublishedObjectTests(TestCase):
fixtures = ['initial_data.json', 'bobby']

def setUp(self):
super(UnpublishedObjectTests, self).setUp()
self.user = 'admin'
self.passwd = 'admin'
create_models(type='layer')
create_layer_data()
create_notifications()
self.anonymous_user = get_anonymous_user()

self.list_url = reverse(
'api_dispatch_list',
kwargs={
'api_name': 'api',
'resource_name': 'layers'})
create_models(type='layer')
all_public()

def test_unpublished_layer(self):
Expand Down
15 changes: 13 additions & 2 deletions geonode/layers/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

# -*- coding: utf-8 -*-
#########################################################################
#
Expand Down Expand Up @@ -254,11 +255,15 @@ def layer_detail(request, layername, template='layers/layer_detail.html'):
ows_url=layer.ows_url,
layer_params=json.dumps(config))

###
# counting layer views
###

# Update count for popularity ranking,
# but do not includes admins or resource owners
if request.user != layer.owner and not request.user.is_superuser:
Layer.objects.filter(
id=layer.id).update(popular_count=F('popular_count') + 1)
from geonode.messaging import producer
producer.viewing_layer(str(request.user), str(layer.owner), layer.id)

# center/zoom don't matter; the viewer will center on the layer bounds
map_obj = GXPMap(projection=getattr(settings, 'DEFAULT_MAP_CRS', 'EPSG:900913'))
Expand Down Expand Up @@ -893,6 +898,12 @@ def layer_metadata_detail(request, layername, template='layers/layer_metadata_de
}))


def layer_view_counter(layer_id):
Layer.objects.filter(
id=layer_id).update(popular_count=F('popular_count') + 1)
return


def layer_metadata_upload(request, layername, template='layers/layer_metadata_upload.html'):
layer = _resolve_layer(request, layername, 'view_resourcebase', _PERMISSION_MSG_METADATA)
return render_to_response(template, RequestContext(request, {
Expand Down
2 changes: 2 additions & 0 deletions geonode/maps/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from geonode.utils import default_map_config
from geonode.base.populate_test_data import create_models
from geonode.maps.tests_populate_maplayers import create_maplayers
from geonode.layers.populate_layers_data import create_notifications


class MapsTest(TestCase):
Expand All @@ -52,6 +53,7 @@ def setUp(self):
create_models(type='map')
create_models(type='layer')
create_maplayers()
create_notifications()

default_abstract = "This is a demonstration of GeoNode, an application \
for assembling and publishing web based maps. After adding layers to the map, \
Expand Down
Empty file added geonode/messaging/__init__.py
Empty file.
125 changes: 125 additions & 0 deletions geonode/messaging/consumer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import logging
import sys

from geonode.geoserver.signals import geoserver_post_save2
from geonode.security.views import send_email_consumer, send_email_owner_on_view
from geonode.social.signals import notification_post_save_resource2
from geonode.layers.views import layer_view_counter
from kombu.mixins import ConsumerMixin
from django.conf import settings

from queues import queue_email_events, queue_geoserver_events,\
queue_notifications_events, queue_all_events,\
queue_geoserver_catalog, queue_geoserver_data,\
queue_geoserver, queue_layer_viewers

logger = logging.getLogger(__package__)
logger.addHandler(logging.StreamHandler(sys.stdout))
logger.setLevel(logging.DEBUG)


class Consumer(ConsumerMixin):
def __init__(self, connection):
self.connection = connection
return

def get_consumers(self, Consumer, channel):
return [
Consumer(queue_all_events,
callbacks=[self.on_message]),
Consumer(queue_email_events,
callbacks=[self.on_email_messages]),
Consumer(queue_geoserver_events,
callbacks=[self.on_geoserver_messages]),
Consumer(queue_notifications_events,
callbacks=[self.on_notifications_messages]),
Consumer(queue_geoserver_catalog,
callbacks=[self.on_geoserver_catalog]),
Consumer(queue_geoserver_data,
callbacks=[self.on_geoserver_data]),
Consumer(queue_geoserver,
callbacks=[self.on_geoserver_all]),
Consumer(queue_layer_viewers,
callbacks=[self.on_layer_viewer]),
]

def on_consume_end(self, connection, channel):
super(Consumer, self).on_consume_end(connection, channel)
logger.info("finished.")

def on_message(self, body, message):
logger.info("broadcast: RECEIVED MSG - body: %r" % (body,))
message.ack()
return

def on_email_messages(self, body, message):
logger.info("on_email_messages: RECEIVED MSG - body: %r" % (body,))
layer_uuid = body.get("layer_uuid")
user_id = body.get("user_id")
send_email_consumer(layer_uuid, user_id)
# Not sure if we need to send ack on this fanout version.
message.ack()
logger.info("on_email_messages: finished")
return

def on_geoserver_messages(self, body, message):
logger.info("on_geoserver_messages: RECEIVED MSG - body: %r" % (body,))
layer_id = body.get("id")
geoserver_post_save2(layer_id)
# Not sure if we need to send ack on this fanout version.
message.ack()
logger.info("on_geoserver_messages: finished")
return

def on_notifications_messages(self, body, message):
logger.info("on_notifications_message: RECEIVED MSG - body: %r" % (body,))
instance_id = body.get("id")
app_label = body.get("app_label")
model = body.get("model")
created = body.get("created")
notification_post_save_resource2(instance_id, app_label, model, created)
message.ack()
logger.info("on_notifications_message: finished")
return

def on_geoserver_all(self, body, message):
logger.info("on_geoserver_all: RECEIVED MSG - body: %r" % (body,))
message.ack()
logger.info("on_geoserver_all: finished")
# TODO:Adding consurmer's producers.
return

def on_geoserver_catalog(self, body, message):
logger.info("on_geoserver_catalog: RECEIVED MSG - body: %r" % (body,))
message.ack()
logger.info("on_geoserver_catalog: finished")
return

def on_geoserver_data(self, body, message):
logger.info("on_geoserver_data: RECEIVED MSG - body: %r" % (body,))
message.ack()
logger.info("on_geoserver_data: finished")
return

def on_consume_ready(self, connection, channel, consumers, **kwargs):
logger.info(">>> Ready:")
logger.info(connection)
logger.info("{} consumers:".format(len(consumers)))
for i, consumer in enumerate(consumers, start=1):
logger.info("{0} {1}".format(i, consumer))

super(Consumer, self).on_consume_ready(connection, channel, consumers,
**kwargs)

def on_layer_viewer(self, body, message):
logger.info("on_layer_viewer: RECEIVED MSG - body: %r" % (body,))
viewer = body.get("viewer")
owner_layer = body.get("owner_layer")
layer_id = body.get("layer_id")
layer_view_counter(layer_id)
if settings.EMAIL_ENABLE:
send_email_owner_on_view(owner_layer, viewer, layer_id)
message.ack()
logger.info("on_layer_viewer: finished")

return
39 changes: 39 additions & 0 deletions geonode/messaging/management/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
#########################################################################
#
# 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.conf import settings
from django.db.models import signals
from django.utils.translation import ugettext_noop as _
import logging
logger = logging.getLogger(__name__)

if "notification" in settings.INSTALLED_APPS:
import notification

def create_notice_types(app, created_models, verbosity, **kwargs):
notification.models.NoticeType.create("layer_uploaded", _("Layer Uploaded"), _("A layer was uploaded"))
notification.models.NoticeType.create("layer_comment", _("Comment on Layer"), _("A layer was commented on"))
notification.models.NoticeType.create("layer_rated", _("Rating for Layer"), _("A rating was given to a layer"))

signals.post_syncdb.connect(create_notice_types, sender=notification)
logger.info("Notifications Configured for geonode.layers.managment.commands")
else:
logger.info("Skipping creation of NoticeTypes for geonode.layers.management.commands, since notification app was \
not found.")
19 changes: 19 additions & 0 deletions geonode/messaging/management/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
#########################################################################
#
# 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/>.
#
#########################################################################
40 changes: 40 additions & 0 deletions geonode/messaging/management/commands/purgemessaging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
#########################################################################
#
# 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.core.management.base import BaseCommand

from geonode.messaging.queues import queue_email_events, queue_geoserver_events, \
queue_notifications_events, queue_all_events, \
queue_geoserver_catalog, queue_geoserver_data, \
queue_geoserver, queue_layer_viewers


class Command(BaseCommand):
help = 'Start the MQ consumer to perform non blocking tasks'

def handle(self, **options):
queue_geoserver_events.purge()
queue_notifications_events.purge()
queue_email_events.purge()
queue_all_events.purge()
queue_geoserver_catalog.purge()
queue_geoserver_data.purge()
queue_geoserver.purge()
queue_layer_viewers.purge()
55 changes: 55 additions & 0 deletions geonode/messaging/management/commands/runmessaging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
#########################################################################
#
# 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/>.
#
#########################################################################

import logging
import sys
from optparse import make_option

from django.core.management.base import BaseCommand
from geonode.settings import BROKER_URL

logger = logging.getLogger(__package__)
logger.addHandler(logging.StreamHandler(sys.stdout))
logger.setLevel(logging.DEBUG)


class Command(BaseCommand):
help = 'Start the MQ consumer to perform non blocking tasks'
option_list = BaseCommand.option_list + (
make_option(
'-i',
'--ignore-errors',
action='store_true',
dest='ignore_errors',
default=False,
help='Stop after any errors are encountered.'),
)

def handle(self, **options):
from kombu import BrokerConnection
from geonode.messaging.consumer import Consumer

with BrokerConnection(BROKER_URL) as connection:
try:
logger.info("Consumer starting.")
worker = Consumer(connection)
worker.run()
except KeyboardInterrupt:
logger.info("Consumer stopped.")
65 changes: 65 additions & 0 deletions geonode/messaging/producer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from __future__ import with_statement

from geonode.settings import BROKER_URL
from kombu import BrokerConnection
from kombu.common import maybe_declare
from kombu.pools import producers
from queues import queue_email_events, queue_geoserver_events,\
queue_notifications_events, queue_layer_viewers


connection = BrokerConnection(BROKER_URL)


def send_email_producer(layer_uuid, user_id):
with producers[connection].acquire(block=True) as producer:
maybe_declare(queue_email_events, producer.channel)
payload = {
"layer_uuid": layer_uuid,
"user_id": user_id

}
producer.publish(
payload,
exchange='geonode',
serializer='json',
routing_key='email'
)


def geoserver_upload_layer(payload):
with producers[connection].acquire(block=True) as producer:
maybe_declare(queue_geoserver_events, producer.channel)
producer.publish(
payload,
exchange='geonode',
serializer='json',
routing_key='geonode.geoserver'
)


def notifications_send(payload, created=None):
with producers[connection].acquire(block=True) as producer:
maybe_declare(queue_notifications_events, producer.channel)
payload['created'] = created
producer.publish(
payload,
exchange='geonode',
serializer='json',
routing_key='notifications'
)


def viewing_layer(user, owner, layer_id):
with producers[connection].acquire(block=True) as producer:
maybe_declare(queue_layer_viewers, producer.channel)

payload = {"viewer": user,
"owner_layer": owner,
"layer_id": layer_id}
producer.publish(
payload,
exchange='geonode',
serializer='json',
routing_key='geonode.viewer'
)
12 changes: 12 additions & 0 deletions geonode/messaging/queues.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from kombu import Exchange, Queue

geonode_exchange = Exchange("geonode", type="topic", durable=False)

queue_all_events = Queue("broadcast", geonode_exchange, routing_key="#")
queue_email_events = Queue("email.events", geonode_exchange, routing_key="email")
queue_geoserver = Queue("all.geoserver", geonode_exchange, routing_key="geoserver.#")
queue_geoserver_catalog = Queue("geoserver.catalog", geonode_exchange, routing_key="geoserver.catalog")
queue_geoserver_data = Queue("geoserver.data", geonode_exchange, routing_key="geoserver.catalog")
queue_geoserver_events = Queue("geoserver.events", geonode_exchange, routing_key="geonode.geoserver")
queue_notifications_events = Queue("notifications.events", geonode_exchange, routing_key="notifications")
queue_layer_viewers = Queue("geonode.layer.viewer", geonode_exchange, routing_key="geonode.viewer")
30 changes: 30 additions & 0 deletions geonode/messaging/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
#########################################################################
#
# 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.test import TestCase


class MessagingTest(TestCase):
"""
Tests geonode.messaging
"""
def setUp(self):
self.adm_un = "admin"
self.adm_pw = "admin"
37 changes: 31 additions & 6 deletions geonode/security/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

from geonode.utils import resolve_object
from geonode.base.models import ResourceBase
from geonode.people.models import Profile
from geonode.layers.models import Layer

if "notification" in settings.INSTALLED_APPS:
from notification import models as notification
Expand Down Expand Up @@ -144,13 +146,10 @@ def request_permissions(request):
""" Request permission to download a resource.
"""
uuid = request.POST['uuid']
resource = get_object_or_404(ResourceBase, uuid=uuid)
try:
notification.send(
[resource.owner],
'request_download_resourcebase',
{'from_user': request.user, 'resource': resource}
)
from geonode.messaging.producer import send_email_producer
send_email_producer(uuid, request.user.id)

return HttpResponse(
json.dumps({'success': 'ok', }),
status=200,
Expand All @@ -160,3 +159,29 @@ def request_permissions(request):
json.dumps({'error': 'error delivering notification'}),
status=400,
content_type='text/plain')


def send_email_consumer(layer_uuid, user_id):
resource = get_object_or_404(ResourceBase, uuid=layer_uuid)
user = Profile.objects.get(id=user_id)
notification.send(
[resource.owner],
'request_download_resourcebase',
{'from_user': user, 'resource': resource}
)
return


def send_email_owner_on_view(owner, viewer, layer_id, geonode_email="email@geo.node"):
# get owner and viewer emails
owner_email = Profile.objects.get(username=owner).email
layer = Layer.objects.get(id=layer_id)
# check if those values are empty
if owner_email and geonode_email:
from django.core.mail import send_mail
# TODO: Copy edit message.
subject_email = "Your Layer has been seen."
msg = ("Your layer called {0} with uuid={1}"
" was seen by {2}").format(layer.name, layer.uuid, viewer)
send_mail(subject_email, msg, geonode_email, [owner_email, ])
return
12 changes: 10 additions & 2 deletions geonode/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
# otherwise it will raise errors for the missing non-minified dependencies
DEBUG_STATIC = strtobool(os.getenv('DEBUG_STATIC', 'False'))

#Define email service on GeoNode
EMAIL_ENABLE = strtobool(os.getenv('EMAIL_ENABLE', 'True'))

# This is needed for integration tests, they require
# geonode to be listening for GeoServer auth requests.
os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'localhost:8000'
Expand Down Expand Up @@ -263,6 +266,7 @@
'geonode.geoserver',
'geonode.upload',
'geonode.tasks',
'geonode.messaging'

)

Expand Down Expand Up @@ -317,6 +321,7 @@
'mptt',
# 'modeltranslation',
'djcelery',
'djkombu',
'storages',
'floppyforms',

Expand All @@ -330,7 +335,7 @@
'avatar',
'dialogos',
'agon_ratings',
# 'notification',
'notification',
'announcements',
'actstream',
'user_messages',
Expand Down Expand Up @@ -952,7 +957,6 @@

# Queue non-blocking notifications.
NOTIFICATION_QUEUE_ALL = False

BROKER_URL = os.getenv('BROKER_URL', "django://")
CELERY_ALWAYS_EAGER = True
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
Expand Down Expand Up @@ -1065,3 +1069,7 @@
# Filter: (boolean, optional, default false) a filter option on that thesaurus will appear in the main search page
# THESAURI = [{'name':'inspire_themes', 'required':False, 'filter':True}]
THESAURI = []

if EMAIL_ENABLE:
#Setting up email backend
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
49 changes: 48 additions & 1 deletion geonode/social/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@
relationships, actstream user_messages and potentially others
"""
import logging
import datetime
from collections import defaultdict
from dialogos.models import Comment

from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.db.models import signals
from django.utils.translation import ugettext as _

Expand Down Expand Up @@ -62,6 +63,33 @@
from agon_ratings.models import Rating


def json_serializer_producer(dictionary):
output = {}
# pop no useful information for others services which wants to connect to geonode
if 'supplemental_information_en' in dictionary.keys():
dictionary.pop('supplemental_information_en', None)
if 'supplemental_information' in dictionary.keys():
dictionary.pop('supplemental_information', None)
if 'doc_file' in dictionary.keys():
file_object = dictionary['doc_file']
dictionary['doc_file'] = str(file_object)
if 'keywords' in dictionary.keys():
keys = dictionary['keywords']
dictionary['keywords'] = str(keys)
for (x, y) in dictionary.items():
if not y:
# this is used to solve
# TypeError: [] is not JSON serializable when it is null
y = str(y)
# check datetime object
# TODO: Use instanceof
if type(y) == datetime.datetime:
y = str(y)

output[x] = y
return output


def activity_post_modify_object(sender, instance, created=None, **kwargs):
"""
Creates new activities after a Map, Layer, or Comment is created/updated/deleted.
Expand Down Expand Up @@ -156,9 +184,28 @@ def relationship_post_save(instance, sender, created, **kwargs):
if notification_app:

def notification_post_save_resource(instance, sender, created, **kwargs):
from geonode.messaging.producer import notifications_send
from django.forms.models import model_to_dict

ct = ContentType.objects.get_for_model(sender)
instance_dict = model_to_dict(instance)
instance_dict['app_label'] = ct.app_label
instance_dict['model'] = ct.model
payload = json_serializer_producer(instance_dict)
notifications_send(payload, created=created)

def notification_post_save_resource2(instance_id, app_label, model, created, **kwargs):
""" Send a notification when a layer, map or document is created or
updated
"""
# TODO: There is a potential race condition where this code gets
# called but there is no information in the database yet.
import time
time.sleep(4)

ct = ContentType.objects.get(app_label=app_label, model=model)
instance_class = ct.model_class()
instance = instance_class.objects.get(id=instance_id)
notice_type_label = '%s_created' if created else '%s_updated'
notice_type_label = notice_type_label % instance.class_name.lower()
recipients = get_notification_recipients(notice_type_label)
Expand Down
3 changes: 2 additions & 1 deletion geonode/social/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from django.utils.translation import ugettext as _
from geonode.layers.populate_layers_data import create_layer_data
from geonode.layers.populate_layers_data import create_layer_data, create_notifications
from geonode.base.populate_test_data import create_models
from geonode.social.templatetags.social_tags import activity_item
from geonode.layers.models import Layer
Expand All @@ -50,6 +50,7 @@ def setUp(self):
create_models(type='layer')
create_layer_data()
self.user = get_user_model().objects.filter(username='admin')[0]
create_notifications()

def test_layer_activity(self):
"""
Expand Down
47 changes: 47 additions & 0 deletions geonode/tests/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

# from geonode.security.models import *
from geonode.layers.models import Layer
from geonode.geoserver.signals import geoserver_post_save2
from geonode.maps.models import Map
from geonode import GeoNodeException
from geonode.layers.utils import (
Expand All @@ -54,6 +55,7 @@
# from geonode.geoserver.helpers import get_wms
# from geonode.geoserver.helpers import set_time_info
from geonode.geoserver.signals import gs_catalog
from geonode.geoserver.signals import geoserver_delete


LOGIN_URL = "/accounts/login/"
Expand Down Expand Up @@ -104,6 +106,12 @@
"""


def sync():
call_command('loaddata', 'default_oauth_apps.json', verbosity=0)
call_command('loaddata', 'initial_data', verbosity=0)
call_command('layer_notice_types', verbosity=0)


class GeoNodeCoreTest(TestCase):

"""Tests geonode.security app/module
Expand Down Expand Up @@ -171,6 +179,7 @@ class GeoNodeMapTest(TestCase):

def setUp(self):
call_command('loaddata', 'people_data', verbosity=0)
sync()

def tearDown(self):
pass
Expand All @@ -181,6 +190,9 @@ def test_raster_upload(self):
"""Test that the wcs links are correctly created for a raster"""
filename = os.path.join(gisdata.GOOD_DATA, 'raster/test_grid.tif')
uploaded = file_upload(filename)

geoserver_post_save2(uploaded.id)

wcs_link = False
for link in uploaded.link_set.all():
if link.mime == 'image/tiff':
Expand Down Expand Up @@ -432,6 +444,9 @@ def test_layer_delete_from_geoserver(self):
gisdata.VECTOR_DATA,
'san_andres_y_providencia_poi.shp')
shp_layer = file_upload(shp_file, overwrite=True)

shp_layer = geoserver_post_save2(shp_layer.id)

ws = gs_cat.get_workspace(shp_layer.workspace)
shp_store = gs_cat.get_store(shp_layer.store, ws)
shp_store_name = shp_store.name
Expand All @@ -444,6 +459,9 @@ def test_layer_delete_from_geoserver(self):
# Test Uploading then Deleting a TIFF file from GeoServer
tif_file = os.path.join(gisdata.RASTER_DATA, 'test_grid.tif')
tif_layer = file_upload(tif_file)

tif_layer = geoserver_post_save2(tif_layer.id)

ws = gs_cat.get_workspace(tif_layer.workspace)
tif_store = gs_cat.get_store(tif_layer.store, ws)
tif_layer.delete()
Expand All @@ -464,6 +482,11 @@ def test_delete_layer(self):
gisdata.VECTOR_DATA,
'san_andres_y_providencia_poi.shp')
shp_layer = file_upload(shp_file)

shp_layer = geoserver_post_save2(shp_layer.id)

time.sleep(20)

shp_layer_id = shp_layer.pk
ws = gs_cat.get_workspace(shp_layer.workspace)
shp_store = gs_cat.get_store(shp_layer.store, ws)
Expand All @@ -474,6 +497,8 @@ def test_delete_layer(self):
# Delete it with the Layer.delete() method
shp_layer.delete()

geoserver_delete(shp_layer.typename)

# Verify that it no longer exists in GeoServer
# self.assertIsNone(gs_cat.get_resource(name, store=shp_store))
# self.assertIsNone(gs_cat.get_layer(shp_layer.name))
Expand Down Expand Up @@ -507,11 +532,19 @@ def test_cascading_delete(self):
'san_andres_y_providencia_poi.shp')
shp_layer = file_upload(shp_file)

shp_layer = geoserver_post_save2(shp_layer.id)

time.sleep(20)

# Save the names of the Resource/Store/Styles
self.assertIsNotNone(shp_layer.name)
resource_name = shp_layer.name
self.assertIsNotNone(shp_layer.workspace)
ws = gs_cat.get_workspace(shp_layer.workspace)
self.assertIsNotNone(shp_layer.store)
store = gs_cat.get_store(shp_layer.store, ws)
store_name = store.name
self.assertIsNotNone(resource_name)
layer = gs_cat.get_layer(resource_name)
styles = layer.styles + [layer.default_style]

Expand Down Expand Up @@ -658,6 +691,7 @@ class GeoNodePermissionsTest(TestCase):

def setUp(self):
call_command('loaddata', 'people_data', verbosity=0)
sync()

def tearDown(self):
pass
Expand Down Expand Up @@ -782,6 +816,9 @@ def test_unpublished(self):
gisdata.VECTOR_DATA,
'san_andres_y_providencia_poi.shp')
layer = file_upload(thefile, overwrite=True)

layer = geoserver_post_save2(layer.id)

layer.set_default_permissions()
check_layer(layer)

Expand Down Expand Up @@ -811,6 +848,9 @@ def test_unpublished(self):
gisdata.VECTOR_DATA,
'san_andres_y_providencia_administrative.shp')
layer = file_upload(thefile, overwrite=True)

layer = geoserver_post_save2(layer.id)

layer.set_default_permissions()
check_layer(layer)

Expand All @@ -832,6 +872,8 @@ def test_unpublished(self):
resource.is_published = True
resource.save()

layer = geoserver_post_save2(layer.id)

request = urllib2.Request(url)
response = urllib2.urlopen(request)
self.assertTrue(any(str_to_check in s for s in response.readlines()))
Expand All @@ -847,6 +889,7 @@ class GeoNodeThumbnailTest(TestCase):

def setUp(self):
call_command('loaddata', 'people_data', verbosity=0)
sync()

def tearDown(self):
pass
Expand All @@ -869,6 +912,8 @@ def test_layer_thumbnail(self):
overwrite=True,
)

saved_layer = geoserver_post_save2(saved_layer.id)

thumbnail_url = saved_layer.get_thumbnail_url()

assert thumbnail_url != staticfiles.static(settings.MISSING_THUMBNAIL)
Expand Down Expand Up @@ -906,6 +951,7 @@ class GeoNodeMapPrintTest(TestCase):

def setUp(self):
call_command('loaddata', 'people_data', verbosity=0)
sync()

def tearDown(self):
pass
Expand Down Expand Up @@ -999,6 +1045,7 @@ class GeoNodeGeoServerSync(TestCase):

def setUp(self):
call_command('loaddata', 'people_data', verbosity=0)
sync()

def tearDown(self):
pass
Expand Down
2 changes: 2 additions & 0 deletions geonode/upload/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def test_scan_file(self):
self.assertTrue(os.path.exists(path))


""""
class TimeFormFormTest(TestCase):
def _form(self, data):
Expand Down Expand Up @@ -168,3 +169,4 @@ def test_start_end_attribute_and_type(self):
('start_year', 'Number'),
('end_date', 'Date')
)
"""
3 changes: 3 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import sys

if __name__ == "__main__":
sys.path.insert(0,
'/usr/local/Cellar/gdal/1.11.3_1/lib/python2.7/site-packages')

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "geonode.settings")

from django.core.management import execute_from_command_line
Expand Down
52 changes: 44 additions & 8 deletions pavement.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import fileinput
import yaml

from setuptools.command import easy_install
from urlparse import urlparse

from paver.easy import task, options, cmdopts, needs
Expand All @@ -43,6 +42,7 @@
# probably trying to run install_win_deps.
pass


try:
from paver.path import pushd
except ImportError:
Expand Down Expand Up @@ -177,6 +177,9 @@ def win_install_deps(options):
print "Installing file ... " + tempfile
grab_winfiles(url, tempfile, package)
try:
# This import causes an error with six.moves on OSX
# let's leave it in the windows specific section.
from setuptools.command import easy_install
easy_install.main([tempfile])
except Exception, e:
failed = True
Expand Down Expand Up @@ -218,6 +221,7 @@ def sync(options):
sh("python manage.py loaddata sample_admin.json")
sh("python manage.py loaddata geonode/base/fixtures/default_oauth_apps.json")
sh("python manage.py loaddata geonode/base/fixtures/initial_data.json")
sh("python manage.py layer_notice_types")


@task
Expand Down Expand Up @@ -295,6 +299,7 @@ def start():
"""
Start GeoNode (Django, GeoServer & Client)
"""
call_task('start_messaging')
info("GeoNode is now available.")


Expand Down Expand Up @@ -338,6 +343,14 @@ def start_django():
sh('python manage.py runserver %s %s' % (bind, foreground))


def start_messaging():
"""
Start the GeoNode messaging server
"""
foreground = '' if options.get('foreground', False) else '&'
sh('python manage.py runmessaging %s' % foreground)


@cmdopts([
('java_path=', 'j', 'Full path to java install for Windows')
])
Expand Down Expand Up @@ -431,8 +444,31 @@ def test(options):
"""
Run GeoNode's Unit Test Suite
"""
sh("%s manage.py test %s.tests --noinput" % (options.get('prefix'),
'.tests '.join(GEONODE_APPS)))

prefix = options.get('prefix', 'python')
sh("%s manage.py test %s.tests --noinput -v 2" % (prefix,
'.tests '.join(GEONODE_APPS)))


@task
def singletest(options):
"""
Run GeoNode's Unit Test Suite
"""
GEONODE_APPS = [
'geonode.maps.tests:MapsTest.test_map_remove',
'geonode.maps.tests:MapsTest.test_rating_map_remove',
'geonode.social.tests:SimpleTest.test_layer_activity',
'geonode.documents.tests:DocumentsTest.test_ajax_document_permissions',
'geonode.documents.tests:DocumentsTest.test_create_document_url',
'geonode.documents.tests:DocumentsTest.test_create_document_with_no_rel',
'geonode.documents.tests:DocumentsTest.test_create_document_with_rel',
]

apps_to_test = ' '.join(GEONODE_APPS)

prefix = options.get('prefix', 'python')
sh("%s manage.py test %s --noinput --failfast" % (prefix, apps_to_test))


@task
Expand All @@ -458,13 +494,13 @@ def test_integration(options):

success = False
try:
call_task('sync')
if name == 'geonode.tests.csw':
call_task('sync')
call_task('start')
sh('sleep 30')
call_task('setup_data')
sh(('python manage.py test %s'
' --noinput --liveserver=localhost:8000' % name))
' --noinput -v 3 --liveserver=localhost:8000' % name))
except BuildFailure, e:
info('Tests failed! %s' % str(e))
else:
Expand Down Expand Up @@ -492,7 +528,7 @@ def run_tests(options):
prefix = 'python'
sh('%s manage.py test geonode.tests.smoke' % prefix)
call_task('test', options={'prefix': prefix})
call_task('test_integration')
call_task('test_integration', options={'prefix': prefix})
call_task('test_integration', options={'name': 'geonode.tests.csw'})
sh('flake8 geonode')

Expand Down Expand Up @@ -576,9 +612,9 @@ def deb(options):

sh(('git-dch --spawn-editor=snapshot --git-author --new-version=%s'
' --id-length=6 --ignore-branch --release' % (simple_version)))
#In case you publish from Ubuntu Xenial (git-dch is removed from upstream)
# In case you publish from Ubuntu Xenial (git-dch is removed from upstream)
# use the following line instead:
#sh(('gbp dch --spawn-editor=snapshot --git-author --new-version=%s'
# sh(('gbp dch --spawn-editor=snapshot --git-author --new-version=%s'
# ' --id-length=6 --ignore-branch --release' % (simple_version)))

deb_changelog = path('debian') / 'changelog'
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ django-geoexplorer==4.0.5
django-guardian==1.4.6
django-haystack==2.4.1
django-jsonfield==0.9.16
django-kombu==0.9.4
django-leaflet==0.13.7
django-modeltranslation==0.12
django-oauth-toolkit==0.10.0
Expand Down
3 changes: 2 additions & 1 deletion scripts/docker/env/production/django.env
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ ALLOWED_HOSTS=['django',]
C_FORCE_ROOT=1
GEOSERVER_PUBLIC_LOCATION=http://geonode/geoserver/
GEOSERVER_LOCATION=http://geonode/geoserver/
SITEURL=http://geonode/
SITEURL=http://geonode/

3 changes: 2 additions & 1 deletion scripts/docker/env/production/geoserver.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ DOCKER_HOST_IP
DJANGO_URL=http://django:8000/
GEOSERVER_PUBLIC_LOCATION=http://geonode/geoserver/
GEOSERVER_LOCATION=http://geoserver:8080/geoserver/
SITEURL=http://geonode/
SITEURL=http://geonode/

8 changes: 7 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@
"django-forms-bootstrap>=3.0.1",
"django-friendly-tag-loader>=1.2.1",
"django-activity-stream>=0.6.1",
"kombu>=3,<4a0",
"django-kombu>=0.9.4",
"django-downloadview>=1.2",
"django-tastypie>=0.12.2",

"django-polymorphic>=0.9.2",
"django-leaflet>=0.13.7",
"django-autocomplete-light>=2.3.3, <3.0a0",
"django-modeltranslation>=0.11", # python-django-modeltranslation (0.11 Debian)
Expand All @@ -113,7 +119,7 @@
"geonode-agon-ratings>=0.3.5", # (0.3.1 in ppa) FIXME
"geonode-user-accounts>=1.0.13", # (1.0.11 in ppa) FIXME
"geonode-arcrest>=10.2",
"geonode-notification>=1.1.1",
"geonode-notification>=1.1.3",
"geonode-dialogos>=0.5",
"gsconfig>=1.0.6", # (1.0.3 in ppa) FIXME
"gsimporter>=1.0.0", # (0.1 in ppa) FIXME
Expand Down