Пример #1
0
class Models(models.Model):
    id = models.IntegerField(primary_key=True)  # AutoField?
    name = models.TextField(unique=True, blank=True)
    lstar = models.FloatField(blank=True, null=True)
    mdot = models.FloatField(blank=True, null=True)
    tstar = models.FloatField(blank=True, null=True)
    teff = models.FloatField(blank=True, null=True)
    logg = models.FloatField(blank=True, null=True)
    vel_law = models.IntegerField(blank=True, null=True)
    vinf = models.IntegerField(blank=True, null=True)
    cl_par_1 = models.FloatField(blank=True, null=True)
    cl_par_2 = models.FloatField(blank=True, null=True)
    hyd_mass_frac = models.FloatField(blank=True, null=True)
    hyd_rel_frac = models.FloatField(blank=True, null=True)
    carb_rel_frac = models.FloatField(blank=True, null=True)
    nit_rel_frac = models.FloatField(blank=True, null=True)
    oxy_rel_frac = models.FloatField(blank=True, null=True)
    iron_rel_frac = models.FloatField(blank=True, null=True)
    ions = models.TextField(blank=True)  # This field type is a guess.
    params = DictionaryField()
    vadat = DictionaryField()
    type = models.TextField(blank=True)

    class Meta:
        managed = False
        db_table = 'models'
Пример #2
0
class MeteorsNightsProcessed(models.Model):
    night = models.TextField(primary_key=True)
    comments = models.TextField(blank=True, null=True)
    params = DictionaryField()
    class Meta:
        db_table = 'meteors_nights_processed'
        app_label = 'favor2'
Пример #3
0
class BeholderStatus(models.Model):
    id = models.IntegerField(primary_key=True)
    time = models.DateTimeField(null=True, blank=True)
    status = DictionaryField()
    class Meta:
        db_table = 'beholder_status'
        app_label = 'favor2'
Пример #4
0
class RealtimeImages(models.Model):
    id = models.IntegerField(primary_key=True)
    object = models.ForeignKey('RealtimeObjects')
    record = models.ForeignKey('RealtimeRecords', null=True, blank=True)
    filename = models.TextField()
    time = models.DateTimeField(null=True, blank=True)
    keywords = DictionaryField()
    class Meta:
        db_table = 'realtime_images'
        app_label = 'favor2'
Пример #5
0
class RealtimeObjects(models.Model):
    id = models.IntegerField(primary_key=True)
    channel_id = models.IntegerField(null=True, blank=True)
    night = models.TextField(blank=True)
    time_start = models.DateTimeField(null=True, blank=True)
    time_end = models.DateTimeField(null=True, blank=True)
    state = models.ForeignKey('RealtimeObjectState', db_column='state')
    ra0 = models.FloatField(null=True, blank=True)
    dec0 = models.FloatField(null=True, blank=True)
    params = DictionaryField()
    class Meta:
        db_table = 'realtime_objects'
        app_label = 'favor2'
Пример #6
0
class Images(models.Model):
    id = models.IntegerField(primary_key=True)
    channel_id = models.IntegerField(null=True, blank=True)
    filter = models.ForeignKey(Filters, null=True, db_column='filter', blank=True)
    night = models.TextField(blank=True)
    filename = models.TextField(blank=True)
    time = models.DateTimeField(null=True, blank=True)
    type = models.TextField(blank=True)
    ra0 = models.FloatField(null=True, blank=True)
    dec0 = models.FloatField(null=True, blank=True)
    keywords = DictionaryField()
    class Meta:
        db_table = u'images'
        app_label = 'favor2'
Пример #7
0
class SatellitesView(models.Model):
    id = models.IntegerField(primary_key=True)
    catalogue = models.IntegerField(blank=True, null=True)
    catalogue_id = models.IntegerField(blank=True, null=True)
    name = models.TextField(blank=True)
    iname = models.TextField(blank=True)
    country = models.TextField(blank=True, null=True)
    type = models.IntegerField(blank=True, null=True)
    launch_date = models.DateTimeField(blank=True, null=True)
    variability = models.IntegerField(blank=True, null=True)
    variability_period = models.FloatField(blank=True, null=True)
    orbit_inclination = models.FloatField(blank=True, null=True)
    orbit_period = models.FloatField(blank=True, null=True)
    orbit_eccentricity = models.FloatField(blank=True, null=True)
    rcs = models.FloatField(blank=True, null=True)
    comments = models.TextField(blank=True)
    params = DictionaryField()
    ntracks = models.BigIntegerField(blank=True, null=True)
    nrecords = models.BigIntegerField(blank=True, null=True)
    penumbra = models.NullBooleanField()
    mean_clear = models.FloatField(blank=True, null=True)
    mean_b = models.FloatField(blank=True, null=True)
    mean_v = models.FloatField(blank=True, null=True)
    mean_r = models.FloatField(blank=True, null=True)
    sigma_clear = models.FloatField(blank=True, null=True)
    sigma_b = models.FloatField(blank=True, null=True)
    sigma_v = models.FloatField(blank=True, null=True)
    sigma_r = models.FloatField(blank=True, null=True)

    median_clear = models.FloatField(blank=True, null=True)
    median_b = models.FloatField(blank=True, null=True)
    median_v = models.FloatField(blank=True, null=True)
    median_r = models.FloatField(blank=True, null=True)

    min_clear = models.FloatField(blank=True, null=True)
    min_b = models.FloatField(blank=True, null=True)
    min_v = models.FloatField(blank=True, null=True)
    min_r = models.FloatField(blank=True, null=True)

    max_clear = models.FloatField(blank=True, null=True)
    max_b = models.FloatField(blank=True, null=True)
    max_v = models.FloatField(blank=True, null=True)
    max_r = models.FloatField(blank=True, null=True)

    time_last = models.DateTimeField(null=True, blank=True)
    time_first = models.DateTimeField(null=True, blank=True)
    class Meta:
        db_table = 'satellites_view'
        app_label = 'favor2'
Пример #8
0
class SchedulerTargets(models.Model):
    id = models.IntegerField(primary_key=True)
    external_id = models.IntegerField(null=True, blank=True)
    name = models.TextField(blank=True)
    type = models.TextField(blank=True)
    ra = models.FloatField(null=True, blank=True)
    dec = models.FloatField(null=True, blank=True)
    exposure = models.FloatField(null=True, blank=True)
    filter = models.TextField(blank=True)
    repeat = models.IntegerField(blank=True)
    status = models.ForeignKey(SchedulerTargetStatus, null=True, db_column='status', blank=True)
    time_created = models.DateTimeField(null=True, blank=True)
    time_completed = models.DateTimeField(null=True, blank=True)
    uuid = models.TextField(unique=True, blank=True)
    timetolive = models.FloatField(null=True, blank=True)
    params = DictionaryField()
    class Meta:
        db_table = 'scheduler_targets'
        app_label = 'favor2'
Пример #9
0
class Satellites(models.Model):
    id = models.IntegerField(primary_key=True)
    catalogue = models.IntegerField(blank=True, null=True)
    catalogue_id = models.IntegerField(blank=True, null=True)
    name = models.TextField(blank=True)
    iname = models.TextField(blank=True)
    country = models.TextField(blank=True)
    type = models.IntegerField(blank=True, null=True)
    launch_date = models.DateTimeField(null=True, blank=True)
    variability = models.IntegerField(blank=True, null=True)
    variability_period = models.FloatField(blank=True, null=True)
    orbit_inclination = models.FloatField(blank=True, null=True)
    orbit_period = models.FloatField(blank=True, null=True)
    orbit_eccentricity = models.FloatField(blank=True, null=True)
    rcs = models.FloatField(blank=True, null=True)
    comments = models.TextField(blank=True)
    params = DictionaryField()
    class Meta:
        db_table = 'satellites'
        app_label = 'favor2'
Пример #10
0
class RealtimeRecords(models.Model):
    id = models.IntegerField(primary_key=True)
    object = models.ForeignKey(RealtimeObjects, null=True, blank=True)
    time = models.DateTimeField(null=True, blank=True)
    ra = models.FloatField(null=True, blank=True)
    dec = models.FloatField(null=True, blank=True)
    x = models.FloatField(null=True, blank=True)
    y = models.FloatField(null=True, blank=True)
    a = models.FloatField(null=True, blank=True)
    b = models.FloatField(null=True, blank=True)
    theta = models.FloatField(null=True, blank=True)
    flux = models.FloatField(null=True, blank=True)
    flux_err = models.FloatField(null=True, blank=True)
    mag = models.FloatField(null=True, blank=True)
    mag_err = models.FloatField(null=True, blank=True)
    filter = models.ForeignKey(Filters, null=True, db_column='filter', blank=True)
    flags = models.IntegerField(null=True, blank=True)
    params = DictionaryField()
    class Meta:
        db_table = 'realtime_records'
        app_label = 'favor2'
Пример #11
0
class SurveyTransients(models.Model):
    id = models.IntegerField(primary_key=True)
    channel_id = models.IntegerField(blank=True, null=True)
    frame = models.ForeignKey(Images, blank=True, null=True)
    time = models.DateTimeField(blank=True, null=True)
    night = models.TextField(blank=True)
    filter = models.ForeignKey(Filters, db_column='filter', blank=True, null=True)
    ra = models.FloatField(blank=True, null=True)
    dec = models.FloatField(blank=True, null=True)
    mag = models.FloatField(blank=True, null=True)
    mag_err = models.FloatField(blank=True, null=True)
    flux = models.FloatField(blank=True, null=True)
    flux_err = models.FloatField(blank=True, null=True)
    x = models.FloatField(blank=True, null=True)
    y = models.FloatField(blank=True, null=True)
    flags = models.IntegerField(blank=True, null=True)
    preview = models.TextField(blank=True)
    simbad = models.TextField(blank=True)
    mpc = models.TextField(blank=True)
    params = DictionaryField()
    class Meta:
        db_table = 'survey_transients'
        app_label = 'favor2'
Пример #12
0
class Instance(models.Model):
    """ Stores instances of multiple elastic models """

    schema = models.ForeignKey(Schema,
                               on_delete=models.CASCADE,
                               related_name='instances')
    data = DictionaryField()

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = hstore.HStoreManager()

    @classmethod
    def load_schema(cls, schema=None):
        """ Build model fields referencing hstore keys based on schema """

        hstore_dictionary_field = cls._meta.get_field('data')

        if schema is not None:
            hstore_dictionary_field.reload_schema(
                schema.get_hstore_field_definitions())
        else:
            hstore_dictionary_field.reload_schema(None)
Пример #13
0
class Device(BaseAccessLevel):
    """
    Device Model
    Represents a network device
    eg: an outdoor point-to-point wifi device, a BGP router, a server, and so on
    """
    name = models.CharField(_('name'), max_length=50)
    node = models.ForeignKey('nodes.Node', verbose_name=_('node'))
    type = models.CharField(_('type'),
                            max_length=50,
                            choices=DEVICE_TYPES_CHOICES)
    status = models.SmallIntegerField(_('status'),
                                      max_length=2,
                                      choices=DEVICE_STATUS_CHOICES,
                                      default=DEVICE_STATUS.get('unknown'))

    # geographic data
    location = models.PointField(
        _('location'),
        blank=True,
        null=True,
        help_text=_("""specify device coordinates (if different from node);
                                    defaults to node coordinates if node is a point,
                                    otherwise if node is a geometry it will default to che centroid of the geometry"""
                    ))
    elev = models.FloatField(_('elevation'), blank=True, null=True)

    # device specific
    routing_protocols = models.ManyToManyField('net.RoutingProtocol',
                                               blank=True)

    os = models.CharField(_('operating system'),
                          max_length=128,
                          blank=True,
                          null=True)
    os_version = models.CharField(_('operating system version'),
                                  max_length=128,
                                  blank=True,
                                  null=True)

    first_seen = models.DateTimeField(_('first time seen on'),
                                      blank=True,
                                      null=True,
                                      default=None)
    last_seen = models.DateTimeField(_('last time seen on'),
                                     blank=True,
                                     null=True,
                                     default=None)

    # text
    description = models.CharField(_('description'),
                                   max_length=255,
                                   blank=True,
                                   null=True)
    notes = models.TextField(_('notes'), blank=True, null=True)

    # extra data
    data = DictionaryField(
        _('extra data'),
        null=True,
        blank=True,
        help_text=_('store extra attributes in JSON string'))
    shortcuts = ReferencesField(null=True, blank=True)

    objects = DeviceManager()

    # list indicating if any other module has extended this model
    extended_by = []

    class Meta:
        app_label = 'net'

    def __unicode__(self):
        return '%s' % self.name

    def save(self, *args, **kwargs):
        """
        Custom save method does the following:
            * automatically inherit node coordinates and elevation
            * save shortcuts if HSTORE is enabled
        """
        custom_checks = kwargs.pop('custom_checks', True)

        super(Device, self).save(*args, **kwargs)

        if custom_checks is False:
            return

        changed = False

        if not self.location:
            self.location = self.node.point
            changed = True

        if not self.elev and self.node.elev:
            self.elev = self.node.elev
            changed = True

        original_user = self.shortcuts.get('user')

        if self.node.user:
            self.shortcuts['user'] = self.node.user

        if original_user != self.shortcuts.get('user'):
            changed = True

        if 'nodeshot.core.layers' in settings.INSTALLED_APPS:
            original_layer = self.shortcuts.get('layer')
            self.shortcuts['layer'] = self.node.layer

            if original_layer != self.shortcuts.get('layer'):
                changed = True

        if changed:
            self.save(custom_checks=False)

    @property
    def owner(self):
        if 'user' not in self.shortcuts:
            if self.node or self.node_id:
                self.save()
            else:
                raise Exception('Instance does not have a node set yet')
        return self.shortcuts['user']

    @property
    def layer(self):
        if 'nodeshot.core.layers' not in settings.INSTALLED_APPS:
            return False
        if 'layer' not in self.shortcuts:
            if self.node or self.node_id:
                self.save()
            else:
                raise Exception('Instance does not have a node set yet')
        return self.shortcuts['layer']

    if 'grappelli' in settings.INSTALLED_APPS:

        @staticmethod
        def autocomplete_search_fields():
            return ('name__icontains', )
Пример #14
0
class Node(BaseAccessLevel):
    """
    Nodes are generic geo-referenced records
    Can be assigned to 'Layers' if nodeshot.core.layers is installed
    Can belong to 'Users'
    """
    name = models.CharField(_('name'), max_length=75, unique=True)
    slug = models.SlugField(max_length=75,
                            db_index=True,
                            unique=True,
                            blank=True)
    status = models.ForeignKey(Status, blank=True, null=True)
    is_published = models.BooleanField(default=PUBLISHED_DEFAULT)

    # TODO: find a way to move this in layers
    if 'nodeshot.core.layers' in settings.INSTALLED_APPS:
        # layer might need to be able to be blank, would require custom validation
        layer = models.ForeignKey('layers.Layer')

    # owner, allow NULL
    user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True)

    # geographic information
    geometry = models.GeometryField(
        _('geometry'),
        help_text=_('geometry of the node (point, polygon, line)'))
    elev = models.FloatField(_('elevation'), blank=True, null=True)
    address = models.CharField(_('address'),
                               max_length=150,
                               blank=True,
                               null=True)

    # descriptive information
    description = models.TextField(_('description'),
                                   max_length=255,
                                   blank=True,
                                   null=True)
    notes = models.TextField(_('notes'),
                             blank=True,
                             null=True,
                             help_text=_('for internal use only'))

    data = DictionaryField(_('extra data'),
                           null=True,
                           blank=True,
                           editable=False,
                           schema=HSTORE_SCHEMA)

    # manager
    objects = NodeManager()

    # this is needed to check if the status is changing
    # explained here:
    # http://stackoverflow.com/questions/1355150/django-when-saving-how-can-you-check-if-a-field-has-changed
    _current_status = None

    # needed for extensible validation
    _additional_validation = []

    class Meta:
        db_table = 'nodes_node'
        app_label = 'nodes'

    def __unicode__(self):
        return '%s' % self.name

    def __init__(self, *args, **kwargs):
        """ Fill __current_status """
        super(Node, self).__init__(*args, **kwargs)
        # set current status, but only if it is an existing node
        if self.pk:
            self._current_status = self.status_id

    def _autofill_slug(self):
        slugified_name = slugify(self.name)
        # auto generate slug
        if not self.slug or self.slug != slugified_name:
            self.slug = slugified_name

    def clean(self, *args, **kwargs):
        """ call extensible validation """
        self._autofill_slug()
        self.extensible_validation()

    def save(self, *args, **kwargs):
        """
        Custom save method does the following things:
            * converts geometry collections of just 1 item to that item (eg: a collection of 1 Point becomes a Point)
            * intercepts changes to status and fires node_status_changed signal
            * set default status
        """
        # geometry collection check
        if isinstance(self.geometry,
                      GeometryCollection) and 0 < len(self.geometry) < 2:
            self.geometry = self.geometry[0]
        # if no status specified
        if not self.status and not self.status_id:
            try:
                self.status = Status.objects.filter(is_default=True)[0]
            except IndexError:
                pass
        super(Node, self).save(*args, **kwargs)
        # if status of a node changes
        if (self.status and self._current_status and self.status.id != self._current_status) or\
           (self.status_id and self._current_status and self.status_id != self._current_status):
            # send django signal
            node_status_changed.send(
                sender=self.__class__,
                instance=self,
                old_status=Status.objects.get(pk=self._current_status),
                new_status=self.status)
        # update _current_status
        self._current_status = self.status_id

    def extensible_validation(self):
        """
        Execute additional validation that might be defined elsewhere in the code.
        Additional validation is introduced through the class method Node.add_validation_method()
        """
        # loop over additional validation method list
        for validation_method in self._additional_validation:
            # call each additional validation method
            getattr(self, validation_method)()

    @classmethod
    def add_validation_method(class_, method):
        """
        Extend validation of Node by adding a function to the _additional_validation list.
        The additional validation function will be called by the clean method

        :method function: function to be added to _additional_validation
        """
        method_name = method.func_name

        # add method name to additional validation method list
        class_._additional_validation.append(method_name)

        # add method to this class
        setattr(class_, method_name, method)

    @property
    def owner(self):
        return self.user

    @property
    def point(self):
        """ returns location of node. If node geometry is not a point a center  point will be returned """
        if not self.geometry:
            raise ValueError(
                'geometry attribute must be set before trying to get point property'
            )
        if self.geometry.geom_type == 'Point':
            return self.geometry
        else:
            try:
                # point_on_surface guarantees that the point is within the geometry
                return self.geometry.point_on_surface
            except GEOSException:
                # fall back on centroid which may not be within the geometry
                # for example, a horseshoe shaped polygon
                return self.geometry.centroid

    if 'grappelli' in settings.INSTALLED_APPS:

        @staticmethod
        def autocomplete_search_fields():
            return ('name__icontains', 'slug__icontains', 'address__icontains')
Пример #15
0
class Interface(BaseAccessLevel):
    """ Interface model """
    device = models.ForeignKey('net.Device')
    type = models.IntegerField(_('type'),
                               max_length=2,
                               choices=INTERFACE_TYPE_CHOICES,
                               blank=True)
    name = models.CharField(_('name'), max_length=10, blank=True, null=True)
    mac = MACAddressField(_('mac address'),
                          max_length=17,
                          unique=True,
                          default=None,
                          null=True,
                          blank=True)
    mtu = models.IntegerField(_('MTU'),
                              blank=True,
                              null=True,
                              default=1500,
                              help_text=_('Maximum Trasmission Unit'))
    tx_rate = models.IntegerField(_('TX Rate'),
                                  null=True,
                                  default=None,
                                  blank=True)
    rx_rate = models.IntegerField(_('RX Rate'),
                                  null=True,
                                  default=None,
                                  blank=True)

    # extra data
    data = DictionaryField(
        _('extra data'),
        null=True,
        blank=True,
        help_text=_('store extra attributes in JSON string'))
    shortcuts = ReferencesField(null=True, blank=True)

    objects = InterfaceManager()

    class Meta:
        app_label = 'net'

    def __unicode__(self):
        return '%s %s' % (self.get_type_display(), self.mac)

    def save(self, *args, **kwargs):
        """
        Custom save method does the following:
            * save shortcuts if HSTORE is enabled
        """
        if 'node' not in self.shortcuts:
            self.shortcuts['node'] = self.device.node

        if 'user' not in self.shortcuts and self.device.node.user:
            self.shortcuts['user'] = self.device.node.user

        if 'layer' not in self.shortcuts and 'nodeshot.core.layers' in settings.INSTALLED_APPS:
            self.shortcuts['layer'] = self.device.node.layer

        super(Interface, self).save(*args, **kwargs)

    @property
    def owner(self):
        if 'user' not in self.shortcuts:
            if self.device or self.device_id:
                self.save()
            else:
                raise Exception('Instance does not have a device set yet')

        return self.shortcuts['user']

    @property
    def node(self):
        if 'node' not in self.shortcuts:
            if self.device or self.device_id:
                self.save()
            else:
                raise Exception('Instance does not have a device set yet')
        return self.shortcuts['node']

    @property
    def layer(self):
        if 'nodeshot.core.layers' not in settings.INSTALLED_APPS:
            return False
        if 'layer' not in self.shortcuts:
            if self.device or self.device_id:
                self.save()
            else:
                raise Exception('Instance does not have a device set yet')
        return self.shortcuts['layer']

    @property
    def ip_addresses(self):
        try:
            addresses = self.data.get('ip_addresses', '')
        # self.data might be none, hence self.data['ip_addresses'] will raise an exception
        except AttributeError:
            addresses = ''
        return addresses.replace(' ', '').split(',') if addresses else []

    @ip_addresses.setter
    def ip_addresses(self, value):
        """ :param value: a list of ip addresses """
        if not isinstance(value, list):
            raise ValueError('ip_addresses value must be a list')
        # in soem cases self.data might be none, so let's instantiate an empty dict
        if self.data is None:
            self.data = {}
        # update field
        self.data['ip_addresses'] = ', '.join(value)

    if 'grappelli' in settings.INSTALLED_APPS:

        @staticmethod
        def autocomplete_search_fields():
            return ('mac__icontains', 'data__icontains')
Пример #16
0
class Layer(BaseDate):
    """ Layer Model """
    name = models.CharField(_('name'), max_length=50, unique=True)
    slug = models.SlugField(max_length=50, db_index=True, unique=True)
    description = models.CharField(_('description'), max_length=250, blank=True, null=True,
                                   help_text=_('short description of this layer'))
    text = models.TextField(_('extended text'), blank=True, null=True,
                            help_text=_('extended description, specific instructions, links, ecc.'))

    # record management
    is_published = models.BooleanField(_('published'), default=True)
    is_external = models.BooleanField(_('is it external?'), default=False)

    # geographic related fields
    center = models.PointField(_('center coordinates'), null=True, blank=True)
    area = models.PolygonField(_('area'), null=True, blank=True)
    zoom = models.SmallIntegerField(_('default zoom level'), choices=MAP_ZOOM_CHOICES, default=ZOOM_DEFAULT)

    # organizational
    organization = models.CharField(_('organization'), help_text=_('Organization which is responsible to manage this layer'), max_length=255)
    website = models.URLField(_('Website'), blank=True, null=True)
    email = models.EmailField(_('email'),
                              help_text=_("""possibly an email address that delivers messages to all the active participants;
                                          if you don't have such an email you can add specific users in the "mantainers" field"""),
                              blank=True)
    mantainers = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                        verbose_name=_('mantainers'),
                                        help_text=_('you can specify the users who are mantaining this layer so they will receive emails from the system'),
                                        blank=True)

    # settings
    # TODO: rename minimum_distance to nodes_minimum_distance
    minimum_distance = models.IntegerField(default=NODE_MINIMUM_DISTANCE,
                                           help_text=_('minimum distance between nodes in meters, 0 means feature disabled'))
    new_nodes_allowed = models.BooleanField(_('new nodes allowed'), default=True, help_text=_('indicates whether users can add new nodes to this layer'))
    # TODO: HSTORE_SCHEMA setting
    data = DictionaryField(_('extra data'), null=True, blank=True,\
                           help_text=_('store extra attributes in JSON string'))

    # default manager
    objects = LayerManager()

    # this is needed to check if the is_published is changing
    # explained here:
    # http://stackoverflow.com/questions/1355150/django-when-saving-how-can-you-check-if-a-field-has-changed
    _current_is_published = None

    class Meta:
        db_table = 'layers_layer'
        app_label= 'layers'

    def __unicode__(self):
        return '%s' % self.name

    def __init__(self, *args, **kwargs):
        """ Fill __current_is_published """
        super(Layer, self).__init__(*args, **kwargs)
        # set current is_published, but only if it is an existing layer
        if self.pk:
            self._current_is_published = self.is_published

    def save(self, *args, **kwargs):
        """
        intercepts changes to is_published and fires layer_is_published_changed signal
        """
        super(Layer, self).save(*args, **kwargs)

        # if is_published of an existing layer changes
        if self.pk and self.is_published != self._current_is_published:
            # send django signal
            layer_is_published_changed.send(
                sender=self.__class__,
                instance=self,
                old_is_published=self._current_is_published,
                new_is_published=self.is_published
            )
            # unpublish nodes
            self.update_nodes_published()

        # update _current_is_published
        self._current_is_published = self.is_published

    def update_nodes_published(self):
        """ publish or unpublish nodes of current layer """
        if self.pk:
            self.node_set.all().update(is_published=self.is_published)

    if 'grappelli' in settings.INSTALLED_APPS:
        @staticmethod
        def autocomplete_search_fields():
            return ('name__icontains', 'slug__icontains')
Пример #17
0
class Layer(BaseDate):
    """
    Layer Model
    A layer represent a categorization of nodes.
    Layers might have geographical boundaries and might be managed by certain organizations.
    """
    name = models.CharField(_('name'), max_length=50, unique=True)
    slug = models.SlugField(max_length=50, db_index=True, unique=True)
    description = models.CharField(
        _('description'),
        max_length=250,
        blank=True,
        null=True,
        help_text=_('short description of this layer'))
    text = models.TextField(
        _('extended text'),
        blank=True,
        null=True,
        help_text=_(
            'extended description, specific instructions, links, ecc.'))
    # record management
    is_published = models.BooleanField(_('published'), default=True)
    is_external = models.BooleanField(_('is it external?'), default=False)
    # geographic related fields
    area = models.GeometryField(
        _('area'),
        help_text=
        _('If a polygon is used nodes of this layer will have to be contained in it.\
                                                        If a point is used nodes of this layer can be located anywhere. Lines are not allowed.'
          ))
    # organizational
    organization = models.CharField(
        _('organization'),
        max_length=255,
        blank=True,
        help_text=_('Organization which is responsible to manage this layer'))
    website = models.URLField(_('Website'), blank=True, null=True)
    email = models.EmailField(
        _('email'),
        blank=True,
        help_text=
        _("""possibly an email address that delivers messages to all the active participants;
                                          if you don't have such an email you can add specific users in the "mantainers" field"""
          ))
    mantainers = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        verbose_name=_('mantainers'),
        blank=True,
        help_text=
        _('you can specify the users who are mantaining this layer so they will receive emails from the system'
          ))
    # settings
    nodes_minimum_distance = models.IntegerField(
        default=NODES_MINIMUM_DISTANCE,
        help_text=
        _('minimum distance between nodes in meters, 0 means there is no minimum distance'
          ))
    new_nodes_allowed = models.BooleanField(
        _('new nodes allowed'),
        default=True,
        help_text=_('indicates whether users can add new nodes to this layer'))
    data = DictionaryField(_('extra data'),
                           schema=HSTORE_SCHEMA,
                           null=True,
                           editable=False)

    # default manager
    objects = LayerManager()

    # this is needed to check if the is_published is changing
    # explained here:
    # http://stackoverflow.com/questions/1355150/django-when-saving-how-can-you-check-if-a-field-has-changed
    _current_is_published = None

    class Meta:
        db_table = 'layers_layer'
        app_label = 'layers'

    def __unicode__(self):
        return self.name

    def __init__(self, *args, **kwargs):
        """ Fill _current_is_published """
        super(Layer, self).__init__(*args, **kwargs)
        # set current is_published, but only if it is an existing layer
        if self.pk:
            self._current_is_published = self.is_published

    def save(self, *args, **kwargs):
        """
        intercepts changes to is_published and fires layer_is_published_changed signal
        """
        super(Layer, self).save(*args, **kwargs)

        # if is_published of an existing layer changes
        if self.pk and self.is_published != self._current_is_published:
            # send django signal
            layer_is_published_changed.send(
                sender=self.__class__,
                instance=self,
                old_is_published=self._current_is_published,
                new_is_published=self.is_published)
            # unpublish nodes
            self.update_nodes_published()

        # update _current_is_published
        self._current_is_published = self.is_published

    def clean(self):
        """
        Ensure area is either a Point or a Polygon
        """
        if not isinstance(self.area, (Polygon, Point)):
            raise ValidationError('area can be only of type Polygon or Point')

    @property
    def center(self):
        # if area is point just return that
        if isinstance(self.area, Point) or self.area is None:
            return self.area
        # otherwise return point_on_surface or centroid
        try:
            # point_on_surface guarantees that the point is within the geometry
            return self.area.point_on_surface
        except GEOSException:
            # fall back on centroid which may not be within the geometry
            # for example, a horseshoe shaped polygon
            return self.area.centroid

    def update_nodes_published(self):
        """ publish or unpublish nodes of current layer """
        if self.pk:
            self.node_set.all().update(is_published=self.is_published)

    if 'grappelli' in settings.INSTALLED_APPS:

        @staticmethod
        def autocomplete_search_fields():
            return ('name__icontains', 'slug__icontains')
Пример #18
0
class Link(BaseAccessLevel):
    """
    Link Model
    Designed for both wireless and wired links
    """
    type = models.SmallIntegerField(_('type'),
                                    max_length=10,
                                    null=True,
                                    blank=True,
                                    choices=choicify(LINK_TYPES),
                                    default=LINK_TYPES.get('radio'))

    # in most cases these two fields are mandatory, except for "planned" links
    interface_a = models.ForeignKey(
        Interface,
        verbose_name=_('from interface'),
        related_name='link_interface_from',
        blank=True,
        null=True,
        help_text=
        _('mandatory except for "planned" links (in planned links you might not have any device installed yet)'
          ))
    interface_b = models.ForeignKey(
        Interface,
        verbose_name=_('to interface'),
        related_name='link_interface_to',
        blank=True,
        null=True,
        help_text=
        _('mandatory except for "planned" links (in planned links you might not have any device installed yet)'
          ))

    # in "planned" links these two fields are necessary
    # while in all the other status they serve as a shortcut
    node_a = models.ForeignKey(
        Node,
        verbose_name=_('from node'),
        related_name='link_node_from',
        blank=True,
        null=True,
        help_text=
        _('leave blank (except for planned nodes) as it will be filled in automatically'
          ))
    node_b = models.ForeignKey(
        Node,
        verbose_name=_('to node'),
        related_name='link_node_to',
        blank=True,
        null=True,
        help_text=
        _('leave blank (except for planned nodes) as it will be filled in automatically'
          ))
    # shortcut
    layer = models.ForeignKey(
        Layer,
        verbose_name=_('layer'),
        blank=True,
        null=True,
        help_text=_('leave blank - it will be filled in automatically'))

    # geospatial info
    line = models.LineStringField(
        blank=True,
        null=True,
        help_text=_('leave blank and the line will be drawn automatically'))

    # monitoring info
    status = models.SmallIntegerField(_('status'),
                                      choices=choicify(LINK_STATUS),
                                      default=LINK_STATUS.get('planned'))
    first_seen = models.DateTimeField(_('first time seen on'),
                                      blank=True,
                                      null=True,
                                      default=None)
    last_seen = models.DateTimeField(_('last time seen on'),
                                     blank=True,
                                     null=True,
                                     default=None)

    # technical info
    metric_type = models.CharField(_('metric type'),
                                   max_length=6,
                                   choices=choicify(METRIC_TYPES),
                                   blank=True,
                                   null=True)
    metric_value = models.FloatField(_('metric value'), blank=True, null=True)
    max_rate = models.IntegerField(_('Maximum BPS'),
                                   null=True,
                                   default=None,
                                   blank=True)
    min_rate = models.IntegerField(_('Minimum BPS'),
                                   null=True,
                                   default=None,
                                   blank=True)

    # wireless specific info
    dbm = models.IntegerField(_('dBm average'),
                              null=True,
                              default=None,
                              blank=True)
    noise = models.IntegerField(_('noise average'),
                                null=True,
                                default=None,
                                blank=True)

    # additional data
    data = DictionaryField(
        _('extra data'),
        null=True,
        blank=True,
        help_text=_('store extra attributes in JSON string'))
    shortcuts = ReferencesField(null=True, blank=True)

    # django manager
    objects = LinkManager()

    class Meta:
        app_label = 'links'

    def __unicode__(self):
        return _(u'%s <> %s') % (self.node_a_name, self.node_b_name)

    def clean(self, *args, **kwargs):
        """
        Custom validation
            1. interface_a and interface_b mandatory except for planned links
            2. planned links should have at least node_a and node_b filled in
            3. dbm and noise fields can be filled only for radio links
            4. interface_a and interface_b must differ
            5. interface a and b type must match
        """
        if self.status != LINK_STATUS.get('planned') and (
                self.interface_a is None or self.interface_b is None):
            raise ValidationError(
                _('fields "from interface" and "to interface" are mandatory in this case'
                  ))

        if self.status == LINK_STATUS.get('planned') and (
                self.node_a is None or self.node_b is None):
            raise ValidationError(
                _('fields "from node" and "to node" are mandatory for planned links'
                  ))

        if self.type != LINK_TYPES.get('radio') and (self.dbm is not None or
                                                     self.noise is not None):
            raise ValidationError(
                _('Only links of type "radio" can contain "dbm" and "noise" information'
                  ))

        if (self.interface_a_id
                == self.interface_b_id) or (self.interface_a
                                            == self.interface_b):
            raise ValidationError(
                _('link cannot have same "from interface" and "to interface"'))

        if (self.interface_a and self.interface_b
            ) and self.interface_a.type != self.interface_b.type:
            format_tuple = (self.interface_a.get_type_display(),
                            self.interface_b.get_type_display())
            raise ValidationError(
                _('link cannot be between of interfaces of different types:\
                                    interface a is "%s" while b is "%s"') %
                format_tuple)

    def save(self, *args, **kwargs):
        """
        Custom save does the following:
            * determine link type if not specified
            * automatically fill 'node_a' and 'node_b' fields if necessary
            * draw line between two nodes
            * fill shortcut properties node_a_name and node_b_name
        """
        if not self.type:
            if self.interface_a.type == INTERFACE_TYPES.get('wireless'):
                self.type = LINK_TYPES.get('radio')
            elif self.interface_a.type == INTERFACE_TYPES.get('ethernet'):
                self.type = LINK_TYPES.get('ethernet')
            else:
                self.type = LINK_TYPES.get('virtual')

        if self.interface_a_id:
            self.interface_a = Interface.objects.get(pk=self.interface_a_id)
        if self.interface_b_id:
            self.interface_b = Interface.objects.get(pk=self.interface_b_id)

        # fill in node_a and node_b
        if self.node_a is None and self.interface_a is not None:
            self.node_a = self.interface_a.node
        if self.node_b is None and self.interface_b is not None:
            self.node_b = self.interface_b.node

        # fill layer from node_a
        if self.layer is None:
            self.layer = self.node_a.layer

        # draw linestring
        if not self.line:
            self.line = LineString(self.node_a.point, self.node_b.point)

        # fill properties
        if self.data is None or self.data.get('node_a_name', None) is None:
            self.data = self.data or {}  # in case is None init empty dict
            self.data['node_a_name'] = self.node_a.name
            self.data['node_b_name'] = self.node_b.name

        if self.data.get('node_a_slug', None) is None or self.data.get(
                'node_b_slug', None) is None:
            self.data['node_a_slug'] = self.node_a.slug
            self.data['node_b_slug'] = self.node_b.slug

        if self.data.get('interface_a_mac', None) is None or self.data.get(
                'interface_b_mac', None) is None:
            self.data['interface_a_mac'] = self.interface_a.mac
            self.data['interface_b_mac'] = self.interface_b.mac

        if self.data.get('layer_slug') != self.layer.slug:
            self.data['layer_slug'] = self.layer.slug

        super(Link, self).save(*args, **kwargs)

    @property
    def node_a_name(self):
        self.data = self.data or {}
        return self.data.get('node_a_name', None)

    @property
    def node_b_name(self):
        self.data = self.data or {}
        return self.data.get('node_b_name', None)

    @property
    def node_a_slug(self):
        self.data = self.data or {}
        return self.data.get('node_a_slug', None)

    @property
    def node_b_slug(self):
        self.data = self.data or {}
        return self.data.get('node_b_slug', None)

    @property
    def interface_a_mac(self):
        self.data = self.data or {}
        return self.data.get('interface_a_mac', None)

    @property
    def interface_b_mac(self):
        self.data = self.data or {}
        return self.data.get('interface_b_mac', None)

    @property
    def layer_slug(self):
        self.data = self.data or {}
        return self.data.get('layer_slug', None)

    @property
    def quality(self):
        """
        Quality is a number between 1 and 6 that rates the quality of the link.
        The way quality is calculated might be overridden by settings.
        0 means unknown
        """
        if self.metric_value is None:
            return 0
        # PLACEHOLDER
        return 6
Пример #19
0
class DeviceConnector(BaseDate, BaseOrdered):
    """
    DeviceConnector Model
    """
    backend = models.CharField(
        _('backend'),
        max_length=128,
        choices=settings.NODESHOT['NETENGINE_BACKENDS'],
        help_text=
        _('select the operating system / protocol to use to retrieve info from device'
          ))
    node = models.ForeignKey('nodes.Node', verbose_name=_('node'))
    host = models.CharField(_('host'), max_length=128)
    config = DictionaryField(
        _('config'),
        blank=True,
        null=True,
        help_text=
        _('backend specific parameters, eg: username/password (SSH), community (SNMP)'
          ))
    port = models.IntegerField(
        _('port'),
        blank=True,
        null=True,
        help_text=_(
            'leave blank to use the default port for the protocol in use'))
    store = models.BooleanField(
        _('store in DB?'),
        default=True,
        help_text=_('is adviced to store read-only credentials only'))
    device = models.ForeignKey(
        Device,
        verbose_name=_('device'),
        blank=True,
        null=True,
        help_text=_('leave blank, will be created automatically'))

    # django manager
    objects = HStoreNodeshotManager()

    __netengine = None
    __backend_class = None

    class Meta:
        ordering = ["order"]
        app_label = 'connectors'
        verbose_name = _('device connector')
        verbose_name_plural = _('device connectors')

    def __unicode__(self):
        if self.host:
            return self.host
        else:
            return _(u'Unsaved Device Connector')

    def save(self, *args, **kwargs):
        """
        Custom save does the following:
            * strip trailing whitespace from host attribute
            * create device and all other related objects
            * store connection config in DB if store attribute is True
        """
        self.host = self.host.strip()

        if not self.id:
            self.device = self.__create_device()

        if self.store is True:
            super(DeviceConnector, self).save(*args, **kwargs)

    def clean(self, *args, **kwargs):
        """ validation """
        self._validate_backend()
        self._validate_config()
        self._validate_netengine()
        self._validate_duplicates()

    @property
    def REQUIRED_CONFIG_KEYS(self):
        return self._get_netengine_arguments(required=True)

    @property
    def AVAILABLE_CONFIG_KEYS(self):
        return self._get_netengine_arguments()

    @property
    def backend_class(self):
        """
        returns python netengine backend class, importing it if needed
        """
        if not self.backend:
            return None

        if not self.__backend_class:
            self.__backend_class = self._get_netengine_backend()

        return self.__backend_class

    @property
    def netengine(self):
        """ access netengine instance """
        # return None if no backend chosen yet
        if not self.backend:
            return None

        # init instance of the netengine backend if not already done
        if not self.__netengine:
            NetengineBackend = self.backend_class
            arguments = self._build_netengine_arguments()

            self.__netengine = NetengineBackend(**arguments)

        # return netengine instance
        return self.__netengine

    def _validate_backend(self):
        """ ensure backend string representation is correct """
        try:
            self.backend_class
        # if we get an import error the specified path is wrong
        except (ImportError, AttributeError) as e:
            raise ValidationError(
                _('No valid backend found, got the following python exception: "%s"'
                  ) % e)

    def _validate_config(self):
        """ ensure REQUIRED_CONFIG_KEYS are filled """
        # exit if no backend specified
        if not self.backend:
            return
        # exit if no required config keys
        if len(self.REQUIRED_CONFIG_KEYS) < 1:
            return

        self.config = self.config or {}  # default to empty dict of no config
        required_keys_set = set(self.REQUIRED_CONFIG_KEYS)
        config_keys_set = set(self.config.keys())
        missing_required_keys = required_keys_set - config_keys_set
        unrecognized_keys = config_keys_set - required_keys_set

        # if any missing required key raise ValidationError
        if len(missing_required_keys) > 0:
            # converts list in comma separated string
            missing_keys_string = ', '.join(missing_required_keys)
            # django error
            raise ValidationError(
                _('Missing required config keys: "%s"') % missing_keys_string)
        elif len(unrecognized_keys) > 0:
            # converts list in comma separated string
            unrecognized_keys_string = ', '.join(unrecognized_keys)
            # django error
            raise ValidationError(
                _('Unrecognized config keys: "%s"') % unrecognized_keys_string)

    def _validate_netengine(self):
        """
        call netengine validate() method
        verifies connection parameters are correct
        """
        if self.backend:
            try:
                self.netengine.validate()
            except NetEngineError as e:
                raise ValidationError(e)

    def _validate_duplicates(self):
        """
        Ensure we're not creating a device that already exists
        Runs only when the DeviceConnector object is created, not when is updated
        """
        # if connector is being created right now
        if not self.id:
            duplicates = []
            self.netengine_dict = self.netengine.to_dict()
            # loop over interfaces and check mac address
            for interface in self.netengine_dict['interfaces']:
                # avoid checking twice for the same interface (often ifconfig returns duplicates)
                if interface['mac_address'] in duplicates:
                    continue
                # check in DB
                if Interface.objects.filter(
                        mac__iexact=interface['mac_address']).count() > 0:
                    duplicates.append(interface['mac_address'])

            # if we have duplicates raise validation error
            if len(duplicates) > 0:
                mac_address_string = ', '.join(duplicates)
                raise ValidationError(
                    _('interfaces with the following mac addresses already exist: %s'
                      ) % mac_address_string)

    def _get_netengine_arguments(self, required=False):
        """
        returns list of available config params
        returns list of required config params if required is True
        for internal use only
        """
        # inspect netengine class
        backend_class = self._get_netengine_backend()
        argspec = inspect.getargspec(backend_class.__init__)
        # store args
        args = argspec.args
        # remove known arguments
        for argument_name in ['self', 'host', 'port']:
            args.remove(argument_name)

        if required:
            # list of default values
            default_values = list(argspec.defaults)
            # always remove last default value, which is port number
            default_values = default_values[0:-1]

            # remove an amount of arguments equals to number of default values, starting from right
            args = args[0:len(args) - len(default_values)]

        return args

    def _get_netengine_backend(self):
        """
        returns the netengine backend specified in self.backend
        for internal use only
        """
        # extract backend class name, eg: AirOS or OpenWRT
        backend_class_name = self.backend.split('.')[-1]
        # convert to lowercase to get the path
        backend_path = self.backend.lower()
        # import module by its path
        module = import_module(backend_path)
        # get netengine backend class
        BackendClass = getattr(module, backend_class_name)

        return BackendClass

    def _build_netengine_arguments(self):
        """
        returns a python dictionary representing arguments
        that will be passed to a netengine backend
        for internal use only
        """
        arguments = {"host": self.host}

        if self.config is not None:
            for key, value in self.config.iteritems():
                arguments[key] = value

        if self.port:
            arguments["port"] = self.port

        return arguments

    def get_auto_order_queryset(self):
        """
        Overriding a method of BaseOrdered Abstract Model
        """
        return self.__class__.objects.filter(device=self.device)

    def __create_device(self):
        """
        creates device, internal use only
        """
        # retrieve netengine dictionary from memory or from network
        device_dict = getattr(self, 'netengine_dict', self.netengine.to_dict())
        device = Device()
        device.node_id = self.node_id
        device.name = device_dict['name']
        device.type = device_dict['type']
        device.status = DEVICE_STATUS.get('reachable')
        device.os = device_dict['os']
        device.os_version = device_dict['os_version']
        # this is the first time the device is seen by the system because we are just adding it
        device.first_seen = now()
        # and is also the latest
        device.last_seen = now()
        device.full_clean()
        device.save()

        # add routing protocols
        for routing_protocol in device_dict['routing_protocols']:
            # retrieve routing protocol from DB
            try:
                rp = RoutingProtocol.objects.filter(
                    name__iexact=routing_protocol['name'],
                    version__iexact=routing_protocol['version'])[0]
            # create if doesn't exist yet
            except IndexError:
                rp = RoutingProtocol(name=routing_protocol['name'],
                                     version=routing_protocol['version'])
                rp.full_clean()
                rp.save()
            # add to device
            device.routing_protocols.add(rp)

        for interface in device_dict['interfaces']:
            interface_object = False
            vap_object = False
            # create interface depending on type
            if interface['type'] == 'ethernet':
                interface_object = Ethernet(
                    **{
                        'device': device,
                        'name': interface['name'],
                        'mac': interface['mac_address'],
                        'mtu': interface['mtu'],
                        'standard': interface['standard'],
                        'duplex': interface['duplex'],
                        'tx_rate': interface['tx_rate'],
                        'rx_rate': interface['rx_rate']
                    })
            elif interface['type'] == 'wireless':
                interface_object = Wireless(
                    **{
                        'device': device,
                        'name': interface['name'],
                        'mac': interface['mac_address'],
                        'mtu': interface['mtu'],
                        'mode': interface['mode'],
                        'standard': interface['standard'],
                        'channel': interface['channel'],
                        'channel_width': interface['channel_width'],
                        'output_power': interface['output_power'],
                        'dbm': interface['dbm'],
                        'noise': interface['noise'],
                        'tx_rate': interface['tx_rate'],
                        'rx_rate': interface['rx_rate']
                    })

                for vap in interface['vap']:
                    vap_object = Vap(essid=vap['essid'],
                                     bssid=vap['bssid'],
                                     encryption=vap['encryption'])

            if interface_object:
                interface_object.full_clean()
                interface_object.save()

                if vap_object:
                    vap_object.interface = interface_object
                    vap_object.full_clean()
                    vap_object.save()

                for ip in interface['ip']:
                    ip_object = Ip(**{
                        'interface': interface_object,
                        'address': ip['address'],
                    })
                    ip_object.full_clean()
                    ip_object.save()

        if HARDWARE_INSTALLED:
            # try getting device model from db
            try:
                device_model = DeviceModel.objects.filter(
                    name__iexact=device_dict['model'])[0]
            # if it does not exist create it
            except IndexError as e:
                # try getting manufacturer from DB
                try:
                    manufacturer = Manufacturer.objects.filter(
                        name__iexact=device_dict['manufacturer'])[0]
                # or create
                except IndexError as e:
                    manufacturer = Manufacturer(
                        name=device_dict['manufacturer'])
                    manufacturer.full_clean()
                    manufacturer.save()

                device_model = DeviceModel(manufacturer=manufacturer,
                                           name=device_dict['model'])
                device_model.ram = device_dict['RAM_total']

            device_model.full_clean()
            device_model.save()

            # create relation between device model and device
            rel = DeviceToModelRel(device=device, model=device_model)
            rel.full_clean()
            rel.save()

        return device
Пример #20
0
class LayerExternal(models.Model):
    """
    External Layers, extend 'Layers' with additional files
    These are the layers that are managed by local groups or other organizations
    """
    layer = models.OneToOneField('layers.Layer', verbose_name=_('layer'), parent_link=True, related_name='external')
    synchronizer_path = models.CharField(_('synchronizer'), max_length=128, choices=SYNCHRONIZERS, default='None')
    config = DictionaryField(_('configuration'),
                             help_text=_('Synchronizer specific configuration (eg: API URL, auth info, ecc)'),
                             blank=True,
                             null=True,
                             editable=False,
                             schema=None)
    # private attributes that will hold synchronizer info
    _synchronizer = None
    _synchronizer_class = None

    class Meta:
        app_label = 'sync'
        db_table = 'layers_external'
        verbose_name = _('external layer')
        verbose_name_plural = _('external layer info')

    def __unicode__(self):
        try:
            return '%s external layer config' % self.layer.name
        except ObjectDoesNotExist:
            return 'LayerExternal object'

    def __init__(self, *args, **kwargs):
        """ custom init method """
        super(LayerExternal, self).__init__(*args, **kwargs)
        # avoid blocking page loading in case of missing required config keys in configuration
        try:
            synchronizer = self.synchronizer
        except ImproperlyConfigured:
            synchronizer = False
        # if synchronizer has get_nodes method
        # add get_nodes method to current LayerExternal instance
        if synchronizer is not False and hasattr(synchronizer, 'get_nodes'):
            self.get_nodes = synchronizer.get_nodes
        # load schema
        self._reload_schema()

    def _reload_schema(self):
        if self.synchronizer_class:
            schema = self.synchronizer_class.SCHEMA
        else:
            schema = None
        if self.config.field.schema is not schema:
            self.config.field.reload_schema(schema)
            # if schema is None set editable to False
            if schema is None:
                self.config.field.editable = False

    def clean(self, *args, **kwargs):
        """
        Call self.synchronizer.clean method
        """
        if self.synchronizer_path != 'None' and self.config:
            # call synchronizer custom clean
            try:
                self.synchronizer.load_config(self.config)
                self.synchronizer.clean()
            except ImproperlyConfigured as e:
                raise ValidationError(e.message)

    def save(self, *args, **kwargs):
        """
        call synchronizer "after_external_layer_saved" method
        for any additional operation that must be executed after save
        """
        after_save = kwargs.pop('after_save', True)
        super(LayerExternal, self).save(*args, **kwargs)
        # call after_external_layer_saved method of synchronizer
        if after_save:
            try:
                synchronizer = self.synchronizer
            except ImproperlyConfigured:
                pass
            else:
                if synchronizer:
                    synchronizer.after_external_layer_saved(self.config)
        # reload schema
        self._reload_schema()

    @property
    def synchronizer(self):
        """ access synchronizer """
        if not self.synchronizer_path or self.synchronizer_path == 'None' or not self.layer:
            return False
        # ensure data is up to date
        if (self._synchronizer is not None and self._synchronizer_class.__name__ not in self.synchronizer_path):
            self._synchronizer = None
            self._synchronizer_class = None
        # init synchronizer only if necessary
        if not self._synchronizer:
            self._synchronizer = (self.synchronizer_class)(self.layer)
        return self._synchronizer

    @property
    def synchronizer_class(self):
        """ returns synchronizer class """
        if not self.synchronizer_path or self.synchronizer_path == 'None' or not self.layer:
            return False
        # ensure data is up to date
        if (self._synchronizer_class is not None and self._synchronizer_class.__name__ not in self.synchronizer_path):
            self._synchronizer = None
            self._synchronizer_class = None
        # import synchronizer class only if not imported already
        if not self._synchronizer_class:
            self._synchronizer_class = import_by_path(self.synchronizer_path)
        return self._synchronizer_class
Пример #21
0
class Layer(BaseDate):
    """ Layer Model """
    name = models.CharField(_('name'), max_length=50, unique=True)
    slug = models.SlugField(max_length=50, db_index=True, unique=True)
    description = models.CharField(
        _('description'),
        max_length=250,
        blank=True,
        null=True,
        help_text=_('short description of this layer'))
    text = models.TextField(
        _('extended text'),
        blank=True,
        null=True,
        help_text=_(
            'extended description, specific instructions, links, ecc.'))

    # record management
    is_published = models.BooleanField(_('published'), default=True)
    is_external = models.BooleanField(_('is it external?'))

    # geographic related fields
    center = models.PointField(_('center coordinates'), null=True, blank=True)
    area = models.PolygonField(_('area'), null=True, blank=True)
    zoom = models.SmallIntegerField(
        _('default zoom level'),
        choices=MAP_ZOOM,
        default=settings.NODESHOT['DEFAULTS']['LAYER_ZOOM'])

    # organizational
    organization = models.CharField(
        _('organization'),
        help_text=_('Organization which is responsible to manage this layer'),
        max_length=255)
    website = models.URLField(_('Website'), blank=True, null=True)
    email = models.EmailField(
        _('email'),
        help_text=
        _("""possibly an email address that delivers messages to all the active participants;
                                          if you don't have such an email you can add specific users in the "mantainers" field"""
          ),
        blank=True)
    mantainers = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        verbose_name=_('mantainers'),
        help_text=
        _('you can specify the users who are mantaining this layer so they will receive emails from the system'
          ),
        blank=True)

    # settings
    minimum_distance = models.IntegerField(
        default=settings.NODESHOT['DEFAULTS']['LAYER_MINIMUM_DISTANCE'],
        help_text=_(
            'minimum distance between nodes in meters, 0 means feature disabled'
        ))
    new_nodes_allowed = models.BooleanField(
        _('new nodes allowed'),
        default=True,
        help_text=_('indicates whether users can add new nodes to this layer'))

    if HSTORE_ENABLED:
        data = DictionaryField(
            _('extra data'),
            null=True,
            blank=True,
            help_text=_('store extra attributes in JSON string'))

    # default manager
    objects = LayerManager()

    class Meta:
        db_table = 'layers_layer'
        app_label = 'layers'

    def __unicode__(self):
        return '%s' % self.name

    if 'grappelli' in settings.INSTALLED_APPS:

        @staticmethod
        def autocomplete_search_fields():
            return ('name__icontains', 'slug__icontains')
Пример #22
0
class UserDefinedCollectionValue(UserTrackable, models.Model):
    """
    UserDefinedCollectionValue does not inherit either the authorizable
    or auditable traits, however it does participate in those systems.

    In particular, the authorization for a collection UDF is based on
    the udf name and model. So if there is a collection udf called
    'Stewardship' on 'Plot' then the only field permission that matters
    is 'Plot'/'udf:Stewardship'

    Each UserDefinedCollectionValue represents a new entry in a
    particular collection field. We audit all of the fields on this
    object and expand the audits in the same way that scalar udfs work.
    """
    field_definition = models.ForeignKey('UserDefinedFieldDefinition')
    model_id = models.IntegerField()
    data = DictionaryField()

    objects = HStoreManager()

    def __unicode__(self):
        return repr(self.data)

    def __init__(self, *args, **kwargs):
        super(UserDefinedCollectionValue, self).__init__(*args, **kwargs)
        self._do_not_track.add('data')
        self.populate_previous_state()

    @property
    def tracked_fields(self):
        return super(UserDefinedCollectionValue, self).tracked_fields + \
            ['udf:' + name for name in self.udf_field_names]

    def validate_foreign_keys_exist(self):
        """
        This is used to check if a given foreign key exists as part of
        the audit system. However, this is no foreign key coupling to
        other auditable/pending models, so we can skip this validation
        step
        """
        pass

    @staticmethod
    def get_display_model_name(audit_name, instance=None):
        if audit_name.startswith('udf:'):
            try:
                # UDF Collections store their model names in the audit table as
                # udf:<pk of UserDefinedFieldDefinition>
                pk = int(audit_name[4:])
                if not instance:
                    udf_def = UserDefinedFieldDefinition.objects.get(pk=pk)
                    return udf_def.name
                else:
                    for udf_def in udf_defs(instance):
                        if udf_def.pk == pk:
                            return udf_def.name
            except (ValueError, UserDefinedFieldDefinition.DoesNotExist):
                pass  # If something goes wrong, just use the defaults
        return audit_name

    @classmethod
    def action_format_string_for_audit(cls, audit):
        if audit.field == 'id' or audit.field is None:
            lang = {
                Audit.Type.Insert:
                trans('created a %(model)s entry'),
                Audit.Type.Update:
                trans('updated the %(model)s entry'),
                Audit.Type.Delete:
                trans('deleted the %(model)s entry'),
                Audit.Type.PendingApprove:
                trans('approved an edit '
                      'to the %(model)s entry'),
                Audit.Type.PendingReject:
                trans('rejected an '
                      'edit to the %(model)s entry')
            }
            return lang[audit.action]
        return Auditable.action_format_string_for_audit(audit)

    @classmethod
    def short_descr(cls, audit):
        # model_id and field_definition aren't very useful changes to see
        if audit.field in {'model_id', 'field_definition'}:
            return None

        format_string = cls.action_format_string_for_audit(audit)

        model_name = audit.model
        field = audit.field
        if audit.field == 'id':
            model_name = cls.get_display_model_name(audit.model)

        if field.startswith('udf:'):
            field = field[4:]

        return format_string % {
            'field': field,
            'model': model_name,
            'value': audit.current_display_value
        }

    def get_cleaned_data(self):
        # Grab each datatype and assign the sub-name to the
        # definition. These are used to clean the data
        cleaned_data = {}
        for subfield_name in self.data:
            sub_value = self.data.get(subfield_name, None)

            datatype = self.field_definition.datatype_by_field[subfield_name]
            try:
                sub_value = self.field_definition.clean_value(
                    sub_value, datatype)
            except ValidationError:
                # If there was an error coming from the database
                # just continue with whatever the value was.
                pass

            cleaned_data[subfield_name] = sub_value

        cleaned_data['id'] = self.pk

        return cleaned_data

    def as_dict(self, *args, **kwargs):
        base_model_dict = super(UserDefinedCollectionValue,
                                self).as_dict(*args, **kwargs)

        for field, value in self.data.iteritems():
            base_model_dict['udf:' + field] = value

        return base_model_dict

    def apply_change(self, key, val):
        if key.startswith('udf:'):
            key = key[4:]
            self.data[key] = val
        else:
            try:
                super(UserDefinedCollectionValue, self)\
                    .apply_change(key, val)
            except ValueError:
                pass

    def save(self, *args, **kwargs):
        raise UserTrackingException(
            'All changes to %s objects must be saved via "save_with_user"' %
            (self._model_name))

    def save_with_user(self, user, *args, **kwargs):
        updated_fields = self._updated_fields()

        if self.pk is None:
            audit_type = Audit.Type.Insert
        else:
            audit_type = Audit.Type.Update

        field_perm = None
        model = self.field_definition.model_type
        field = 'udf:%s' % self.field_definition.name
        perms = permissions(user,
                            self.field_definition.instance,
                            model_name=model)
        for perm in perms:
            if perm.field_name == field and perm.allows_writes:
                field_perm = perm
                break

        if field_perm is None:
            raise AuthorizeException("Cannot save UDF field '%s.%s': "
                                     "No sufficient permission found." %
                                     (model, self.field_definition.name))

        if field_perm.permission_level == FieldPermission.WRITE_WITH_AUDIT:
            model_id = _reserve_model_id(UserDefinedCollectionValue)
            pending = True
            for field, (oldval, _) in updated_fields.iteritems():
                self.apply_change(field, oldval)
        else:
            pending = False
            super(UserDefinedCollectionValue,
                  self).save_with_user(user, *args, **kwargs)
            model_id = self.pk

        if audit_type == Audit.Type.Insert:
            updated_fields['id'] = [None, model_id]

        for field, (old_val, new_val) in updated_fields.iteritems():
            Audit.objects.create(current_value=new_val,
                                 previous_value=old_val,
                                 model='udf:%s' % self.field_definition.pk,
                                 model_id=model_id,
                                 field=field,
                                 instance=self.field_definition.instance,
                                 user=user,
                                 action=audit_type,
                                 requires_auth=pending)
Пример #23
0
class Link(BaseAccessLevel):
    """
    Link Model
    Designed for both wireless and wired links
    """
    type = models.SmallIntegerField(_('type'),
                                    null=True,
                                    blank=True,
                                    choices=choicify(LINK_TYPES),
                                    default=LINK_TYPES.get('radio'))

    # in most cases these two fields are mandatory, except for "planned" links
    interface_a = models.ForeignKey(
        Interface,
        verbose_name=_('from interface'),
        related_name='link_interface_from',
        blank=True,
        null=True,
        help_text=
        _('mandatory except for "planned" links (in planned links you might not have any device installed yet)'
          ))
    interface_b = models.ForeignKey(
        Interface,
        verbose_name=_('to interface'),
        related_name='link_interface_to',
        blank=True,
        null=True,
        help_text=
        _('mandatory except for "planned" links (in planned links you might not have any device installed yet)'
          ))

    topology = models.ForeignKey(
        Topology,
        blank=True,
        null=True,
        help_text=_('mandatory to draw the link dinamically'))

    # in "planned" links these two fields are necessary
    # while in all the other status they serve as a shortcut
    node_a = models.ForeignKey(
        Node,
        verbose_name=_('from node'),
        related_name='link_node_from',
        blank=True,
        null=True,
        help_text=
        _('leave blank (except for planned nodes) as it will be filled in automatically'
          ))
    node_b = models.ForeignKey(
        Node,
        verbose_name=_('to node'),
        related_name='link_node_to',
        blank=True,
        null=True,
        help_text=
        _('leave blank (except for planned nodes) as it will be filled in automatically'
          ))
    # shortcut
    layer = models.ForeignKey(
        Layer,
        verbose_name=_('layer'),
        blank=True,
        null=True,
        help_text=_('leave blank - it will be filled in automatically'))

    # geospatial info
    line = models.LineStringField(
        blank=True,
        null=True,
        help_text=_('leave blank and the line will be drawn automatically'))

    # monitoring info
    status = models.SmallIntegerField(_('status'),
                                      choices=choicify(LINK_STATUS),
                                      default=LINK_STATUS.get('planned'))
    first_seen = models.DateTimeField(_('first time seen on'),
                                      blank=True,
                                      null=True,
                                      default=None)
    last_seen = models.DateTimeField(_('last time seen on'),
                                     blank=True,
                                     null=True,
                                     default=None)

    # technical info
    metric_type = models.CharField(_('metric type'),
                                   max_length=6,
                                   choices=choicify(METRIC_TYPES),
                                   blank=True,
                                   null=True)
    metric_value = models.FloatField(_('metric value'), blank=True, null=True)
    max_rate = models.IntegerField(_('Maximum BPS'),
                                   null=True,
                                   default=None,
                                   blank=True)
    min_rate = models.IntegerField(_('Minimum BPS'),
                                   null=True,
                                   default=None,
                                   blank=True)

    # wireless specific info
    dbm = models.IntegerField(_('dBm average'),
                              null=True,
                              default=None,
                              blank=True)
    noise = models.IntegerField(_('noise average'),
                                null=True,
                                default=None,
                                blank=True)

    # additional data
    data = DictionaryField(
        _('extra data'),
        null=True,
        blank=True,
        help_text=_('store extra attributes in JSON string'))
    shortcuts = ReferencesField(null=True, blank=True)

    # django manager
    objects = LinkManager()

    class Meta:
        app_label = 'links'

    def __unicode__(self):
        return _(u'%s <> %s') % (self.node_a_name, self.node_b_name)

    def clean(self, *args, **kwargs):
        """
        Custom validation
            1. interface_a and interface_b mandatory except for planned links
            2. planned links should have at least node_a and node_b filled in
            3. dbm and noise fields can be filled only for radio links
            4. interface_a and interface_b must differ
            5. interface a and b type must match
        """
        if self.status != LINK_STATUS.get('planned'):
            if self.interface_a is None or self.interface_b is None:
                raise ValidationError(
                    _('fields "from interface" and "to interface" are mandatory in this case'
                      ))

            if (self.interface_a_id
                    == self.interface_b_id) or (self.interface_a
                                                == self.interface_b):
                msg = _(
                    'link cannot have same "from interface" and "to interface: %s"'
                ) % self.interface_a
                raise ValidationError(msg)

        if self.status == LINK_STATUS.get('planned') and (
                self.node_a is None or self.node_b is None):
            raise ValidationError(
                _('fields "from node" and "to node" are mandatory for planned links'
                  ))

        if self.type != LINK_TYPES.get('radio') and (self.dbm is not None or
                                                     self.noise is not None):
            raise ValidationError(
                _('Only links of type "radio" can contain "dbm" and "noise" information'
                  ))

    def save(self, *args, **kwargs):
        """
        Custom save does the following:
            * determine link type if not specified
            * automatically fill 'node_a' and 'node_b' fields if necessary
            * draw line between two nodes
            * fill shortcut properties node_a_name and node_b_name
        """
        if not self.type:
            if self.interface_a.type == INTERFACE_TYPES.get('wireless'):
                self.type = LINK_TYPES.get('radio')
            elif self.interface_a.type == INTERFACE_TYPES.get('ethernet'):
                self.type = LINK_TYPES.get('ethernet')
            else:
                self.type = LINK_TYPES.get('virtual')

        if self.interface_a_id:
            self.interface_a = Interface.objects.get(pk=self.interface_a_id)
        if self.interface_b_id:
            self.interface_b = Interface.objects.get(pk=self.interface_b_id)

        # fill in node_a and node_b
        if self.node_a is None and self.interface_a is not None:
            self.node_a = self.interface_a.node
        if self.node_b is None and self.interface_b is not None:
            self.node_b = self.interface_b.node

        # fill layer from node_a
        if self.layer is None:
            self.layer = self.node_a.layer

        # draw linestring
        if not self.line:
            self.line = LineString(self.node_a.point, self.node_b.point)

        # fill properties
        if self.data.get('node_a_name', None) is None:
            self.data['node_a_name'] = self.node_a.name
            self.data['node_b_name'] = self.node_b.name

        if self.data.get('node_a_slug', None) is None or self.data.get(
                'node_b_slug', None) is None:
            self.data['node_a_slug'] = self.node_a.slug
            self.data['node_b_slug'] = self.node_b.slug

        if self.interface_a and self.data.get('interface_a_mac', None) is None:
            self.data['interface_a_mac'] = self.interface_a.mac

        if self.interface_b and self.data.get('interface_b_mac', None) is None:
            self.data['interface_b_mac'] = self.interface_b.mac

        if self.data.get('layer_slug') != self.layer.slug:
            self.data['layer_slug'] = self.layer.slug

        super(Link, self).save(*args, **kwargs)

    @classmethod
    def get_link(cls, source, target, topology=None):
        """
        Find link between source and target, (or vice versa, order is irrelevant).
        :param source: ip or mac addresses
        :param target: ip or mac addresses
        :param topology: optional topology relation
        :returns: Link object
        :raises: LinkNotFound
        """
        a = source
        b = target
        # ensure parameters are coherent
        if not (valid_ipv4(a) and valid_ipv4(b)) and not (
                valid_ipv6(a) and valid_ipv6(b)) and not (valid_mac(a)
                                                          and valid_mac(b)):
            raise ValueError('Expecting valid ipv4, ipv6 or mac address')
        # get interfaces
        a = cls._get_link_interface(a)
        b = cls._get_link_interface(b)
        # raise LinkDataNotFound if an interface is not found
        not_found = []
        if a is None:
            not_found.append(source)
        if b is None:
            not_found.append(target)
        if not_found:
            msg = 'the following interfaces could not be found: {0}'.format(
                ', '.join(not_found))
            raise LinkDataNotFound(msg)
        # find link with interfaces
        # inverse order is also ok
        q = (Q(interface_a=a, interface_b=b) | Q(interface_a=b, interface_b=a))
        # add topology to lookup
        if topology:
            q = q & Q(topology=topology)
        link = Link.objects.filter(q).first()
        if link is None:
            raise LinkNotFound('Link matching query does not exist',
                               interface_a=a,
                               interface_b=b,
                               topology=topology)
        return link

    @classmethod
    def _get_link_interface(self, string_id):
        if valid_ipv4(string_id) or valid_ipv6(string_id):
            try:
                return Ip.objects.get(address=string_id).interface
            except Ip.DoesNotExist as e:
                return None
        else:
            try:
                return Interface.objects.get(mac=string_id)
            except Interface.DoesNotExist as e:
                return None

    @classmethod
    def get_or_create(cls, source, target, cost, topology=None):
        """
        Tries to find a link with get_link, creates a new link if link not found.
        """
        try:
            return cls.get_link(source, target, topology)
        except LinkNotFound as e:
            pass
        # create link
        link = Link(interface_a=e.interface_a,
                    interface_b=e.interface_b,
                    status=LINK_STATUS['active'],
                    metric_value=cost,
                    topology=topology)
        link.full_clean()
        link.save()
        return link

    @property
    def node_a_name(self):
        return self.data.get('node_a_name', None)

    @property
    def node_b_name(self):
        return self.data.get('node_b_name', None)

    @property
    def node_a_slug(self):
        return self.data.get('node_a_slug', None)

    @property
    def node_b_slug(self):
        return self.data.get('node_b_slug', None)

    @property
    def interface_a_mac(self):
        return self.data.get('interface_a_mac', None)

    @property
    def interface_b_mac(self):
        return self.data.get('interface_b_mac', None)

    @property
    def layer_slug(self):
        return self.data.get('layer_slug', None)

    @property
    def quality(self):
        """
        Quality is a number between 1 and 6 that rates the quality of the link.
        The way quality is calculated might be overridden by settings.
        0 means unknown
        """
        if self.metric_value is None:
            return 0
        # PLACEHOLDER
        return 6

    def ensure(self, status, cost):
        """
        ensure link properties correspond to the specified ones
        perform save operation only if necessary
        """
        changed = False
        status_id = LINK_STATUS[status]
        if self.status != status_id:
            self.status = status_id
            changed = True
        if self.metric_value != cost:
            self.metric_value = cost
            changed = True
        if changed:
            self.save()
Пример #24
0
class DataResource(Displayable):
    """Represents a file that has been uploaded to Geoanalytics for representation"""
    original_file = models.FileField(upload_to='geographica_resources',
                                     null=True,
                                     blank=True)
    resource_file = models.FileField(upload_to='geographica_resources',
                                     null=True,
                                     blank=True)
    resource_url = models.URLField(null=True, blank=True)
    metadata_url = models.URLField(null=True, blank=True)
    metadata_xml = models.TextField(null=True, blank=True)
    driver_config = DictionaryField(null=True, blank=True)
    metadata_properties = DictionaryField(null=True, blank=True)
    last_change = models.DateTimeField(null=True, blank=True, auto_now=True)
    last_refresh = models.DateTimeField(
        null=True, blank=True
    )  # updates happen only to geocms that were not uploaded by the user.
    next_refresh = models.DateTimeField(
        null=True, blank=True,
        db_index=True)  # will be populated every time the update manager runs
    refresh_every = TimedeltaField(null=True, blank=True)
    md5sum = models.CharField(max_length=64, blank=True,
                              null=True)  # the unique md5 sum of the data
    bounding_box = models.PolygonField(null=True, srid=4326, blank=True)
    import_log = models.TextField(null=True, blank=True)
    associated_pages = models.ManyToManyField("pages.Page",
                                              blank=True,
                                              null=True,
                                              related_name='data_resources')

    driver = models.CharField(
        default='terrapyn.geocms.drivers.spatialite',
        max_length=255,
        null=False,
        blank=False,
        choices=getattr(settings, 'INSTALLED_DATARESOURCE_DRIVERS', (
            ('terrapyn.geocms.drivers.spatialite',
             'Spatialite (universal vector)'),
            ('terrapyn.geocms.drivers.shapefile', 'Shapefile'),
            ('terrapyn.geocms.drivers.geotiff', 'GeoTIFF'),
            ('terrapyn.geocms.drivers.postgis', 'PostGIS'),
            ('terrapyn.geocms.drivers.kmz', 'Google Earth KMZ'),
            ('terrapyn.geocms.drivers.ogr', 'OGR DataSource'),
        )))

    big = models.BooleanField(
        default=False,
        help_text='Set this to be true if the dataset is more than 100MB'
    )  # causes certain drivers to optimize for datasets larger than memory

    def get_absolute_url(self):
        return reverse('resource-page', kwargs={'slug': self.slug})

    def get_admin_url(self):
        return reverse("admin:geocms_dataresource_change", args=(self.id, ))

    @property
    def srs(self):
        if not self.metadata.native_srs:
            self.driver_instance.compute_spatial_metadata()
        srs = osr.SpatialReference()
        srs.ImportFromProj4(self.metadata.native_srs.encode('ascii'))
        return srs

    @property
    def driver_instance(self):
        if not hasattr(self, '_driver_instance'):
            self._driver_instance = get_driver(self.driver)(self)
        return self._driver_instance

    def __unicode__(self):
        return self.title

    class Meta:
        permissions = (
            ('view_dataresource',
             "View data resource"),  # to add beyond the default
        )