Skip to content

Commit

Permalink
Use django.utils.module_loading.import_by_path to import synchronizers
Browse files Browse the repository at this point in the history
  • Loading branch information
nemesifier committed Sep 22, 2014
1 parent 500db14 commit 09fb740
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 83 deletions.
62 changes: 29 additions & 33 deletions nodeshot/interoperability/management/commands/synchronize.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
from django.core.management.base import BaseCommand, CommandError
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.db.models import Q
from django.utils.module_loading import import_by_path

from nodeshot.core.layers.models import Layer

from importlib import import_module
from optparse import make_option


class Command(BaseCommand):
args = '<layer_slug layer_slug ...>'
help = 'Synchronize external layers with the local database'

option_list = BaseCommand.option_list + (
make_option(
'--exclude',
Expand All @@ -29,15 +29,15 @@ def retrieve_layers(self, *args, **options):
"""
Retrieve specified layers or all external layers if no layer specified.
"""

# init empty Q object
queryset = Q()

# if no layer specified
if len(args) < 1:
# cache queryset
all_layers = Layer.objects.published().external()

# check if there is any layer to exclude
if options['exclude']:
# convert comma separated string in python list, ignore spaces
Expand All @@ -48,88 +48,84 @@ def retrieve_layers(self, *args, **options):
# nothing to exclude, retrieve all layers
self.verbose('no layer specified, will retrieve all layers!')
return all_layers

# otherwise loop over args and retrieve each specified layer
for layer_slug in args:
queryset = queryset | Q(slug=layer_slug)

# verify existence
try:
# retrieve layer
layer = Layer.objects.get(slug=layer_slug)

# raise exception if layer is not external
if not layer.is_external:
raise CommandError('Layer "%s" is not an external layer\n\r' % layer_slug)

# raise exception if layer is not published
if not layer.is_published:
raise CommandError('Layer "%s" is not published. Why are you trying to work on an unpublished layer?\n\r' % layer_slug)

# raise exception if one of the layer looked for doesn't exist
except Layer.DoesNotExist:
raise CommandError('Layer "%s" does not exist\n\r' % layer_slug)

# return published external layers
return Layer.objects.published().external().select_related().filter(queryset)

def verbose(self, message):
if self.verbosity == 2:
self.stdout.write('%s\n\r' % message)
self.stdout.write('%s\n\r' % message)

def handle(self, *args, **options):
""" execute synchronize command """
# store verbosity level in instance attribute for later use
self.verbosity = int(options.get('verbosity'))

# blank line
self.stdout.write('\r\n')

# retrieve layers
layers = self.retrieve_layers(*args, **options)

if len(layers) < 1:
self.stdout.write('no layers to process\n\r')
return
else:
self.verbose('going to process %d layers...' % len(layers))

# loop over
for layer in layers:
# retrieve interop class if available
try:
interop = layer.external.interoperability
synchronizer_path = layer.external.interoperability
except (ObjectDoesNotExist, AttributeError):
self.stdout.write('External Layer %s does not have an interoperability class specified\n\r' % layer.name)
continue
# if no interop jump to next layer
if interop == 'None':

# if no synchronizer_path jump to next layer
if synchronizer_path == 'None':
self.stdout.write('External Layer %s does not have an interoperability class specified\n\r' % layer.name)
continue

if layer.external.config is None:
self.stdout.write('Layer %s does not have a config yet\n\r' % layer.name)
continue

# else go ahead and import module
interop_module = import_module(interop)
# retrieve class name (split and get last piece)
class_name = interop.split('.')[-1]

# retrieve class
interop_class = getattr(interop_module, class_name)
self.stdout.write('imported module %s\r\n' % interop_module.__file__)
Synchronizer = import_by_path(synchronizer_path)
self.stdout.write('imported module %s\r\n' % Synchronizer.__name__)

# try running
try:
instance = interop_class(layer, verbosity=self.verbosity)
instance = Synchronizer(layer, verbosity=self.verbosity)
self.stdout.write('Processing layer "%s"\r\n' % layer.slug)
messages = instance.process()
except ImproperlyConfigured, e:
self.stdout.write('Validation error: %s\r\n' % e)
continue

for message in messages:
self.stdout.write('%s\n\r' % message)

self.stdout.write('\r\n')
8 changes: 2 additions & 6 deletions nodeshot/interoperability/models/layer_external.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from importlib import import_module
import simplejson as json

from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError, ImproperlyConfigured
from django.utils.module_loading import import_by_path

from ..settings import SYNCHRONIZERS

Expand Down Expand Up @@ -119,11 +119,7 @@ def synchronizer_class(self):
self._synchronizer_class = None

if not self._synchronizer_class:
synchronizer_module = import_module(self.interoperability)
# retrieve class name (split and get last piece)
class_name = self.interoperability.split('.')[-1]
# retrieve class
self._synchronizer_class = getattr(synchronizer_module, class_name)
self._synchronizer_class = import_by_path(self.interoperability)

return self._synchronizer_class

Expand Down
24 changes: 12 additions & 12 deletions nodeshot/interoperability/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@


DEFAULT_SYNCHRONIZERS = [
('nodeshot.interoperability.synchronizers.Nodeshot', 'Nodeshot'),
('nodeshot.interoperability.synchronizers.GeoJson', 'GeoJSON'),
('nodeshot.interoperability.synchronizers.GeoRss', 'GeoRSS'),
('nodeshot.interoperability.synchronizers.OpenWisp', 'OpenWisp'),
('nodeshot.interoperability.synchronizers.OpenWispCitySdkTourism', 'OpenWispCitySdkTourism'),
('nodeshot.interoperability.synchronizers.ProvinciaWifi', 'Provincia WiFi'),
('nodeshot.interoperability.synchronizers.ProvinciaWifiCitySdkTourism', 'ProvinciaWifiCitySdkTourism'),
('nodeshot.interoperability.synchronizers.ProvinciaWifiCitySdkMobility', 'Synchronize Provincia Wifi with CitySDK Mobility'),
('nodeshot.interoperability.synchronizers.CitySdkMobility', 'CitySDK Mobility (event driven)'),
('nodeshot.interoperability.synchronizers.GeoJsonCitySdkMobility', 'Import GeoJSON into CitySDK Mobility API'),
('nodeshot.interoperability.synchronizers.GeoJsonCitySdkTourism', 'Import GeoJSON into CitySDK Tourism API'),
('nodeshot.interoperability.synchronizers.OpenLabor', 'OpenLabor'),
('nodeshot.interoperability.synchronizers.Nodeshot.Nodeshot', 'Nodeshot'),
('nodeshot.interoperability.synchronizers.GeoJson.GeoJson', 'GeoJSON'),
('nodeshot.interoperability.synchronizers.GeoRss.GeoRss', 'GeoRSS'),
('nodeshot.interoperability.synchronizers.OpenWisp.OpenWisp', 'OpenWisp'),
('nodeshot.interoperability.synchronizers.OpenWispCitySdkTourism.OpenWispCitySdkTourism', 'OpenWispCitySdkTourism'),
('nodeshot.interoperability.synchronizers.ProvinciaWifi.ProvinciaWifi', 'Provincia WiFi'),
('nodeshot.interoperability.synchronizers.ProvinciaWifiCitySdkTourism.ProvinciaWifiCitySdkTourism', 'ProvinciaWifiCitySdkTourism'),
('nodeshot.interoperability.synchronizers.ProvinciaWifiCitySdkMobility.ProvinciaWifiCitySdkMobility', 'Synchronize Provincia Wifi with CitySDK Mobility'),
('nodeshot.interoperability.synchronizers.CitySdkMobility.CitySdkMobility', 'CitySDK Mobility (event driven)'),
('nodeshot.interoperability.synchronizers.GeoJsonCitySdkMobility.GeoJsonCitySdkMobility', 'Import GeoJSON into CitySDK Mobility API'),
('nodeshot.interoperability.synchronizers.GeoJsonCitySdkTourism.GeoJsonCitySdkTourism', 'Import GeoJSON into CitySDK Tourism API'),
('nodeshot.interoperability.synchronizers.OpenLabor.OpenLabor', 'OpenLabor'),
]

SYNCHRONIZERS = DEFAULT_SYNCHRONIZERS + getattr(settings, 'NODESHOT_SYNCHRONIZERS', [])
Expand Down
21 changes: 9 additions & 12 deletions nodeshot/interoperability/tasks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from celery import task
from importlib import import_module
from django.utils.module_loading import import_by_path
from django.core import management


Expand All @@ -20,7 +20,7 @@ def push_changes_to_external_layers(node, external_layer, operation):
Sync other applications through their APIs by performing updates, adds or deletes.
This method is designed to be performed asynchronously, avoiding blocking the user
when he changes data on the local DB.
:param node: the node which should be updated on the external layer.
:type node: Node model instance
:param operation: the operation to perform (add, change, delete)
Expand All @@ -30,19 +30,16 @@ def push_changes_to_external_layers(node, external_layer, operation):
# subsequent imports go and look into sys.modules before reimporting the module again
# so performance is not affected
from nodeshot.core.nodes.models import Node

# get node because for some reason the node instance object is not passed entirely,
# pheraphs because objects are serialized by celery or transport/queuing mechanism
if not isinstance(node, basestring):
node = Node.objects.get(pk=node.pk)

interop_module = import_module(external_layer.interoperability)
# retrieve class name (split and get last piece)
class_name = external_layer.interoperability.split('.')[-1]
# retrieve class
interop_class = getattr(interop_module, class_name)
instance = interop_class(external_layer.layer)


# import synchronizer
Synchronizer = import_by_path(external_layer.interoperability)
instance = Synchronizer(external_layer.layer)

# call method only if supported
if hasattr(instance, operation):
getattr(instance, operation)(node)
getattr(instance, operation)(node)
Loading

0 comments on commit 09fb740

Please sign in to comment.