Пример #1
0
class AbstractOperation(models.Model):
    """Represents an area where a team is operating.  Could be a regular
    station posting, an incident, an exercise, or whatever makes sense.
    For a discussion of incident file naming conventions see
    http://gis.nwcg.gov/2008_GISS_Resource/student_workbook/unit_lessons/Unit_08_File_Naming_Review.pdf"""

    folder = models.ForeignKey(Folder, related_name='%(app_label)s_%(class)s_owningFolder', default=1)
    name = models.CharField(max_length=32, blank=True,
                            help_text="A descriptive name for this operation.  Example: 'beaver_pond'.")
    operationId = models.CharField(max_length=32, blank=True, verbose_name='operation id',
                                   help_text="A formal id for this operation.  For incidents, use the incident number.  Example: 'PA-DEWA-0001'")
    minTime = models.DateTimeField(blank=True, null=True, verbose_name='start date')
    maxTime = models.DateTimeField(blank=True, null=True, verbose_name='end date')
    minLat = models.FloatField(blank=True, null=True, verbose_name='minimum latitude') # WGS84 degrees
    minLon = models.FloatField(blank=True, null=True, verbose_name='minimum longitude') # WGS84 degrees
    maxLat = models.FloatField(blank=True, null=True, verbose_name='maximum latitude') # WGS84 degrees
    maxLon = models.FloatField(blank=True, null=True, verbose_name='maximum longitude') # WGS84 degrees
    notes = models.TextField(blank=True)
    tags = TagField(blank=True)
    contentType = models.ForeignKey(ContentType, editable=False, null=True)
    uuid = UuidField()
    extras = ExtrasField(help_text="A place to add extra fields if we need them but for some reason can't modify the table schema.  Expressed as a JSON-encoded dict.")
    objects = AbstractModelManager(parentModel=None)

    class Meta:
        abstract = True

    def __unicode__(self):
        return '%s %s %s' % (self.__class__.__name__, self.name, self.operationId)
Пример #2
0
class Snapshot(models.Model):
    """
    A snapshot is a subframe of an image with an associated comment.
    """
    imgType = models.ForeignKey(ContentType, editable=False)
    imgId = models.PositiveIntegerField()
    uuid = UuidField()
    xmin = models.FloatField()
    ymin = models.FloatField()
    xmax = models.FloatField()
    ymax = models.FloatField()
    title = models.CharField(max_length=64)
    comment = models.TextField()
    status = models.CharField(max_length=1,
                              choices=STATUS_CHOICES,
                              default=STATUS_ACTIVE)
    dateCreated = models.DateTimeField(null=True, blank=True)
    dateUpdated = models.DateTimeField(null=True, blank=True)

    # img is a virtual field, not actually present in the db.  it
    # specifies which image this snapshot is associated with based on
    # imgType (which db table to look in) and imgId (which row in the
    # table).
    img = generic.GenericForeignKey('imgType', 'imgId')

    def save(self, updateDate=True, **kwargs):
        if updateDate:
            self.dateUpdated = datetime.datetime.utcnow()
        super(Snapshot, self).save(**kwargs)

    def __unicode__(self):
        return self.title
Пример #3
0
class Assignment(models.Model):
    operation = models.ForeignKey(Operation)
    userProfile = models.ForeignKey('UserProfile')
    organization = models.CharField(max_length=64, blank=True, help_text="The organization or unit you are assigned to for this operation.")
    jobTitle = models.CharField(max_length=64, blank=True, help_text="Your job title for this operation.")
    contactInfo = models.CharField(max_length=128, blank=True, help_text="Your contact info for this operation. Cell phone number is most important.")
    uuid = UuidField()
    extras = ExtrasDotField(help_text="A place to add extra fields if we need them but for some reason can't modify the table schema.  Expressed as a JSON-encoded dict.")
Пример #4
0
class AbstractMapNode(models.Model):
    """
    Abstract Map Node for an entry in the map tree, which can have a parent.
    """
    uuid = UuidField(primary_key=True)
    name = models.CharField('name', max_length=128, db_index=True)
    description = models.CharField('description', max_length=1024, blank=True)
    creator = models.CharField('creator', max_length=200)
    modifier = models.CharField('modifier',
                                max_length=200,
                                null=True,
                                blank=True)
    creation_time = models.DateTimeField(null=True, blank=True, db_index=True)
    modification_time = models.DateTimeField(null=True,
                                             blank=True,
                                             db_index=True)
    deleted = models.BooleanField(blank=True, default=False)
    minLat = models.FloatField(blank=True, null=True)
    minLon = models.FloatField(blank=True, null=True)
    maxLat = models.FloatField(blank=True, null=True)
    maxLon = models.FloatField(blank=True, null=True)
    region = models.ForeignKey('geocamUtil.SiteFrame', null=True)

    @property
    def parent(self):
        """ child classes must define parent"""
        return None

    def getEditHref(self):
        """ child classes must define edit href
        """
        return None

    def __unicode__(self):
        return self.name

    def getTreeJson(self):
        """ Get the json block that the fancy tree needs to render this node """
        result = {
            "title": self.name,
            "key": self.uuid,
            "tooltip": self.description,
            "data": {
                "type": self.__class__.__name__,
                "parentId": None,
                "href": self.getEditHref()
            }
        }
        if self.parent:
            result['data']['parentId'] = self.parent.uuid
        return result

    class Meta:
        abstract = True
        ordering = ['name']
Пример #5
0
class Sensor(models.Model):
    name = models.CharField(max_length=40, blank=True,
                            help_text='Your name for the instrument. Example: "MicroImager" or "GeoCam"')
    make = models.CharField(max_length=40, blank=True,
                            help_text='The organization that makes the sensor.  Example: "Canon"')
    model = models.CharField(max_length=40, blank=True,
                             help_text='The model of sensor.  Example: "Droid" or "PowerShot G9"')
    software = models.CharField(max_length=160, blank=True,
                                help_text='Software running on the sensor, including any known firmware and version details. Example: "GeoCam Mobile 1.0.10, Android firmware 2.1-update1 build ESE81"')
    serialNumber = models.CharField(max_length=80, blank=True,
                                    verbose_name='serial number',
                                    help_text='Information that uniquely identifies this particular sensor unit. Example: "serialNumber:HT851N002808 phoneNumber:4126573579" ')
    notes = models.TextField(blank=True)
    tags = TagField(blank=True)
    uuid = UuidField()
    extras = ExtrasField(help_text="A place to add extra fields if we need them but for some reason can't modify the table schema.  Expressed as a JSON-encoded dict.")
Пример #6
0
class GroupProfile(models.Model):
    group = models.OneToOneField(Group, help_text='Reference to corresponding Group object of built-in Django authentication system.')
    context = models.ForeignKey(Context, blank=True, null=True,
                                help_text='Default context associated with this group.')

    password = models.CharField(max_length=128, blank=True, null=True)

    uuid = UuidField()
    extras = ExtrasDotField(help_text="A place to add extra fields if we need them but for some reason can't modify the table schema.  Expressed as a JSON-encoded dict.")

    def password_required(self):
        return (self.password is None)

    def set_password(self, raw_password):
        # Make sure the password field isn't set to none
        if raw_password is not None:
            import hashlib
            import random

            # Compute the differnt parts we'll need to secure store the password
            algo = 'sha1'
            salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
            hsh = hashlib.sha1(salt + raw_password).hexdigest()

            # Set the password value
            self.password = '******' % (algo, salt, hsh)

    def authenticate(self, user_password):
        # Make sure the password field isn't set to none
        if self.password is not None:
            import hashlib

            # Get the parts of the group password
            parts = self.password.split('$')
            _algo, salt, hsh = parts[:2]

            # Compute the hash of the user password
            #user_hsh = get_hexdigest(algo, salt, user_password)
            user_hash = hashlib.sha1(salt + user_password).hexdigest()

            # Retrun the resulting comparison
            return (hsh == user_hash)

        else:
            return True
Пример #7
0
class Context(models.Model):
    """
    A context is a collection of settings that it makes sense to share
    between members of a group or people on an incident.
    """
    name = models.CharField(max_length=40,
                            help_text="A descriptive name.")
    uploadFolder = models.ForeignKey(Folder,
                                     related_name='%(app_label)s_%(class)s_uploadingContextSet',
                                     help_text="Folder to upload data to.")
    timeZone = models.CharField(max_length=32,
                                choices=TIME_ZONE_CHOICES,
                                default=DEFAULT_TIME_ZONE,
                                help_text="Time zone used to display timestamps and to interpret incoming timestamps that aren't labeled with a time zone.")
    layerConfigUrl = models.CharField(max_length=256,
                                      help_text='URL pointing to a JSON config file specifying what layers to show')
    uuid = UuidField()
    extras = ExtrasDotField(help_text="A place to add extra fields if we need them but for some reason can't modify the table schema.  Expressed as a JSON-encoded dict.")
Пример #8
0
class AbstractUserProfile(models.Model):
    """
    Adds some extended fields to the django built-in User type.
    """
    user = models.OneToOneField(User, help_text='Reference to corresponding User object of built-in Django authentication system.', related_name='%(app_label)s_%(class)s')
    displayName = models.CharField(max_length=40, blank=True,
                                   help_text="The 'uploaded by' name that will appear next to data you upload.  Defaults to 'F. Last', but if other members of your unit use your account you might want to show your unit name instead.")
    homeOrganization = models.CharField(max_length=64, blank=True, help_text="The home organization you usually work for.")
    homeJobTitle = models.CharField(max_length=64, blank=True, help_text="Your job title in your home organization.")
    contactInfo = models.CharField(max_length=128, blank=True, help_text="Your contact info in your home organization.")
    uuid = UuidField()
    extras = ExtrasDotField(help_text="A place to add extra fields if we need them but for some reason can't modify the table schema.  Expressed as a JSON-encoded dict.")

    class Meta:
        ordering = ['user']
        abstract = True

    def __unicode__(self):
        return u'<User %s "%s %s">' % (self.user.username, self.user.first_name, self.user.last_name)
Пример #9
0
class UserProfile(models.Model):
    """Adds some extended fields to the django built-in User type."""
    user = models.OneToOneField(User, help_text='Reference to corresponding User object of built-in Django authentication system.')
    displayName = models.CharField(max_length=40, blank=True,
                                   help_text="The 'uploaded by' name that will appear next to data you upload.  Defaults to 'F. Last', but if other members of your unit use your account you might want to show your unit name instead.")
    contactInfo = models.CharField(max_length=128, blank=True,
                                   help_text="Your contact info.  If at an incident, we suggest listing cell number and email address.")
    # user's overall folder permissions are the union of userPermissions and
    # the permissions granted to units the user is posted to.  if user has 'admin'
    # privileges to any folder, they can also create new folders.
    userPermissions = models.ManyToManyField(Permission)
    assignments = models.ManyToManyField(Assignment)
    homeOrganization = models.CharField(max_length=64, blank=True, help_text="The organization you normally work for.")
    homeTitle = models.CharField(max_length=64, blank=True, help_text="Your normal job title.")
    uuid = UuidField()
    extras = ExtrasField(help_text="A place to add extra fields if we need them but for some reason can't modify the table schema.  Expressed as a JSON-encoded dict.")

    class Meta:
        ordering = ['user']

    def __unicode__(self):
        return u'<User %s "%s %s">' % (self.user.username, self.user.first_name, self.user.last_name)
Пример #10
0
class Folder(models.Model):
    """Every piece of data in Share belongs to a folder which records both the
    operation the data is associated with and who should be able to access it."""
    name = models.CharField(max_length=32)
    operation = models.ForeignKey("Operation", blank=True, null=True,
                                  related_name='activeOperation',
                                  help_text='Operation that gathered the data in this folder, if applicable.  (Once a folder has an operation and contains data, it should not be switched to a new operation; create a new folder instead.)')
    timeZone = models.CharField(max_length=32,
                                choices=TIME_ZONE_CHOICES,
                                default=DEFAULT_TIME_ZONE,
                                help_text="Time zone used to display timestamps on data in this folder.")
    isArchive = models.BooleanField(default=False,
                                    help_text='If true, disable editing data in this folder.')
    notes = models.TextField(blank=True)
    uuid = UuidField()
    extras = ExtrasField(help_text="A place to add extra fields if we need them but for some reason can't modify the table schema.  Expressed as a JSON-encoded dict.")

    def __unicode__(self):
        if self.name:
            name = self.name
        else:
            name = '[untitled]'
        return '%s id=%d' % (name, self.id)
Пример #11
0
class Change(models.Model):
    """The concept workflow is like this: there are two roles involved,
    the author (the person who collected the data and who knows it best)
    and validators (who are responsible for signing off on it before it
    is considered 'valid' by the rest of the team).  When the author
    uploads new data, it is marked 'submitted for validation', so it
    appears in the queue of things to be validated.  Any validator
    examining the queue has three choices with each piece of data: she
    can mark it 'valid' (publishing it to the team), 'needs edits'
    (putting it on the author's queue to be fixed), or
    'rejected' (indicating it's not worth fixing, and hiding it to avoid
    confusion).  If the author fixes something on his queue to be fixed,
    he can then submit it to be validated again.  if the author notices
    a problem with the data after it is marked 'valid', he can change
    its status back to 'needs author fixes', edit, and then resubmit.
    Each status change in the workflow is recorded as a Change object."""
    timestamp = models.DateTimeField()
    featureUuid = models.CharField(max_length=48)
    user = models.ForeignKey(User)
    action = models.CharField(max_length=40, blank=True,
                              help_text='Brief human-readable description like "upload" or "validation check"')
    workflowStatus = models.PositiveIntegerField(choices=WORKFLOW_STATUS_CHOICES,
                                                 default=DEFAULT_WORKFLOW_STATUS)
    uuid = UuidField()
Пример #12
0
class AbstractPlan(models.Model):
    uuid = UuidField(unique=True, db_index=True)
    name = models.CharField(max_length=128, db_index=True)
    dateModified = models.DateTimeField(db_index=True)
    creator = models.ForeignKey(User, null=True, blank=True, db_index=True)

    # the canonical serialization of the plan exchanged with javascript clients
    jsonPlan = ExtrasDotField()

    # a place to put an auto-generated summary of the plan
    summary = models.CharField(max_length=4096)

    # allow users to mark plans as deleted.  remember to use this field!
    deleted = models.BooleanField(blank=True, default=False)

    # allow users to mark plans as read only, so when they are opened they cannot be edited
    readOnly = models.BooleanField(blank=True, default=False)

    # cache commonly used stats derived from the plan (relatively expensive to calculate)
    numStations = models.PositiveIntegerField(default=0)
    numSegments = models.PositiveIntegerField(default=0)
    numCommands = models.PositiveIntegerField(default=0)
    lengthMeters = models.FloatField(null=True, blank=True)
    estimatedDurationSeconds = models.FloatField(null=True, blank=True)
    stats = ExtrasDotField(
    )  # a place for richer stats such as numCommandsByType
    namedURLs = GenericRelation(NamedURL)

    class Meta:
        ordering = ['-dateModified']
        abstract = True

    @property
    def acquisition_time(self):
        return self.dateModified

    def get_absolute_url(self):
        return reverse('planner2_plan_save_json', args=[self.pk, self.name])

    def extractFromJson(self,
                        overWriteDateModified=True,
                        overWriteUuid=True,
                        request=None):
        if overWriteUuid:
            if not self.uuid:
                self.uuid = makeUuid()
                self.jsonPlan.uuid = self.uuid
            self.jsonPlan.serverId = self.pk
        if overWriteDateModified:
            self.jsonPlan.dateModified = (datetime.datetime.now(
                pytz.utc).replace(microsecond=0).isoformat())
            self.jsonPlan.dateModified = self.jsonPlan.dateModified[:-6] + 'Z'

        self.name = self.jsonPlan.name
        self.jsonPlan.url = self.get_absolute_url()
        self.jsonPlan.serverId = self.pk
        self.dateModified = dateparser(
            self.jsonPlan.dateModified).replace(tzinfo=pytz.utc)
        plannerUsers = User.objects.filter(username=self.jsonPlan.creator)
        if plannerUsers:
            self.creator = plannerUsers[0]
        else:
            self.creator = None

        # fill in stats
        try:
            exporter = statsPlanExporter.StatsPlanExporter()
            #             print ' about to do stats'
            stats = exporter.exportDbPlan(self, request)
            for f in ('numStations', 'numSegments', 'numCommands',
                      'lengthMeters', 'estimatedDurationSeconds'):
                setattr(self, f, stats[f])
            self.stats.numCommandsByType = stats["numCommandsByType"]
            self.summary = statsPlanExporter.getSummary(stats)
        except:
            logging.warning(
                'extractFromJson: could not extract stats from plan %s',
                self.uuid)
            raise  # FIX
        return self

    def getSummaryOfCommandsByType(self):
        return statsPlanExporter.getSummaryOfCommandsByType(self.stats)

    # TODO test
    def toXpjson(self):
        platform = self.jsonPlan['platform']
        if platform:
            planSchema = getPlanSchema(platform[u'name'])
            return xpjson.loadDocumentFromDict(self.jsonPlan,
                                               schema=planSchema.getSchema())
        logging.warning(
            'toXpjson: could not convert to xpjson, probably no schema %s',
            self.uuid)
        raise  # FIX

    def escapedName(self):
        name = re.sub(r'[^\w]', '', self.name)
        if name == '':
            return 'plan'
        else:
            if self.jsonPlan and self.jsonPlan.planVersion:
                return name + "_" + self.jsonPlan.planVersion
            return name

    def getExportUrl(self, extension):
        return reverse('planner2_planExport',
                       kwargs={
                           'uuid': self.uuid,
                           'name': self.escapedName() + extension
                       })

    def getExporters(self):
        import choosePlanExporter  # delayed import avoids import loop
        result = []
        for exporterInfo in choosePlanExporter.PLAN_EXPORTERS:
            info = copy.deepcopy(exporterInfo)
            info.url = self.getExportUrl(info.extension)
            result.append(info)
        return result

    def getLinks(self):
        """
        The links tab wil be populated with the name, value contents of this dictionary as links,
        name is the string displayed and link is what will be opened
        """
        result = {
            "KML":
            reverse('planner2_planExport',
                    kwargs={
                        'uuid': self.uuid,
                        'name': self.name + '.kml'
                    })
        }
        kwargs = {
            'plan_id': self.pk,
            'crs': settings.XGDS_PLANNER_CRS_UNITS_DEFAULT
        }
        if settings.XGDS_PLANNER_CRS_UNITS_DEFAULT:
            result["SummaryCRS"] = reverse('plan_bearing_distance_crs',
                                           kwargs=kwargs)
        else:
            result["Summary"] = reverse('plan_bearing_distance', kwargs=kwargs)

        for exporter in self.getExporters():
            result[exporter.label] = exporter.url
        return result

    def getEscapedId(self):
        if self.jsonPlan and self.jsonPlan.id:
            result = re.sub(r'[^\w]', '', self.jsonPlan.id)
            result = re.sub('_PLAN$', '', result)
            return result
        else:
            return None

    def toMapDict(self):
        """
        Return a reduced dictionary that will be turned to JSON for rendering in a map
        Here we are just interested in the route plan and not in activities
        We just include stations
        """
        result = {}
        result['id'] = self.uuid
        result['author'] = self.jsonPlan.creator
        result['name'] = self.jsonPlan.name
        result['type'] = 'Plan'
        if self.jsonPlan.notes:
            result['notes'] = self.jsonPlan.notes
        else:
            result['notes'] = ''

        stations = []
        seq = self.jsonPlan.sequence
        for el in seq:
            if el.type == "Station":
                sta = {}
                sta['id'] = el.id
                sta['coords'] = el.geometry.coordinates
                sta['notes'] = ''
                if hasattr(el, 'notes'):
                    if el.notes:
                        sta['notes'] = el.notes
                stations.append(sta)
        result['stations'] = stations
        return result

    def get_tree_json(self):
        result = {
            "title": self.name,
            "key": self.uuid,
            "tooltip": self.jsonPlan.notes,
            "data": {
                "type":
                "PlanLink",  # we cheat so this will be 'live'
                "json":
                reverse('planner2_mapJsonPlan',
                        kwargs={'uuid': str(self.uuid)}),
                "kmlFile":
                reverse('planner2_planExport',
                        kwargs={
                            'uuid': str(self.uuid),
                            'name': self.name + '.kml'
                        }),
                "href":
                reverse('planner2_edit', kwargs={'plan_id': str(self.pk)})
            }
        }
        return result

    @property
    def executions(self):
        return LazyGetModelByName(
            settings.XGDS_PLANNER_PLAN_EXECUTION_MODEL).get().objects.filter(
                plan=self)

    def __unicode__(self):
        if self.name:
            return self.name
        else:
            return 'Unnamed plan ' + self.uuid
Пример #13
0
class AbstractFlight(models.Model):
    objects = NameManager()

    uuid = UuidField(unique=True, db_index=True)
    name = models.CharField(
        max_length=128,
        blank=True,
        unique=True,
        help_text='it is episode name + asset role. i.e. 20130925A_ROV',
        db_index=True)
    locked = models.BooleanField(blank=True, default=False)
    start_time = models.DateTimeField(null=True, blank=True, db_index=True)
    end_time = models.DateTimeField(null=True, blank=True, db_index=True)
    timezone = models.CharField(null=True,
                                blank=False,
                                max_length=128,
                                default=settings.TIME_ZONE)

    vehicle = 'set to DEFAULT_VEHICLE_FIELD() or similar in derived classes'
    notes = models.TextField(blank=True)
    group = 'set to DEFAULT_GROUP_FLIGHT_FIELD() or similar in derived classes'

    def natural_key(self):
        return (self.name)

    @classmethod
    def cls_type(cls):
        return 'Flight'

    def hasStarted(self):
        return (self.start_time != None)

    def hasEnded(self):
        if self.hasStarted():
            return (self.end_time != None)
        return False

    def startFlightExtras(self, request):
        pass

    def stopFlightExtras(self, request):
        pass

    def thumbnail_time_url(self, event_time):
        return self.thumbnail_url()

    def thumbnail_url(self):
        return ''

    def view_time_url(self, event_time):
        return self.view_url()

    def view_url(self):
        return ''

    def __unicode__(self):
        return self.name

    def getTreeJsonChildren(self):
        children = []
        if self.track:
            children.append({
                "title": settings.GEOCAM_TRACK_TRACK_MONIKIER,
                "selected": False,
                "tooltip": "Tracks for " + self.name,
                "key": self.uuid + "_tracks",
                "data": {
                    "json":
                    reverse('geocamTrack_mapJsonTrack',
                            kwargs={'uuid': str(self.track.uuid)}),
                    "sseUrl":
                    "",
                    "type":
                    'MapLink',
                }
            })
        if self.plans:
            myplan = self.plans[0].plan
            children.append({
                "title": settings.XGDS_PLANNER2_PLAN_MONIKER,
                "selected": False,
                "tooltip": "Plan for " + self.name,
                "key": self.uuid + "_plan",
                "data": {
                    "json":
                    reverse('planner2_mapJsonPlan',
                            kwargs={'uuid': str(myplan.uuid)}),
                    "sseUrl":
                    "",
                    "type":
                    'MapLink',
                }
            })
        return children

    def getTreeJson(self):
        result = {
            "title": self.name,
            "lazy": True,
            "key": self.uuid,
            "tooltip": self.notes,
            "folder": True,
            "data": {
                "type":
                self.__class__.__name__,
                "vehicle":
                self.vehicle.name,
                "href":
                '',  # TODO add url to the flight summary page when it exists
                "childNodesUrl":
                reverse('planner2_flightTreeNodes',
                        kwargs={'flight_id': self.id})
            }
            #"children": self.getTreeJsonChildren()
        }

        return result

    @property
    def plans(self):
        return LazyGetModelByName(
            settings.XGDS_PLANNER2_PLAN_EXECUTION_MODEL).get().objects.filter(
                flight=self)

    def stopTracking(self):
        if settings.PYRAPTORD_SERVICE is True:
            pyraptord = getPyraptordClient()
            serviceName = self.vehicle.name + "TrackListener"
            stopPyraptordServiceIfRunning(pyraptord, serviceName)

        #TODO remove the current position for that track
        if self.track:
            if self.track.currentposition_set:
                try:
                    position = self.track.currentposition_set.first()
                    if position:
                        position.delete()
                except:
                    pass

    def startTracking(self):
        #TODO define
        pass

    class Meta:
        abstract = True
        ordering = ['-name']
Пример #14
0
class Feature(models.Model):
    folder = models.ForeignKey(Folder, default=1)
    name = models.CharField(max_length=80, blank=True, default='')
    author = models.ForeignKey(User, null=True, related_name='%(app_label)s_%(class)s_authoredSet',
                               help_text='The user who collected the data (when you upload data, Share tags you as the author)')
    sensor = models.ForeignKey(Sensor, blank=True, null=True)
    isAerial = models.BooleanField(default=False, blank=True, verbose_name='aerial data', help_text="True for aerial data. Generally for non-aerial data we snap to terrain in 3D visualizations so that GPS errors can't cause features to be rendered underground.")
    notes = models.TextField(blank=True)
    tags = TagField(blank=True)
    icon = models.CharField(max_length=16, blank=True)

    # these fields help us handle changes to data products
    status = models.CharField(max_length=1, choices=STATUS_CHOICES,
                              default=STATUS_CHOICES[0][0])
    processed = models.BooleanField(default=False)
    version = models.PositiveIntegerField(default=0)
    purgeTime = models.DateTimeField(null=True, blank=True)
    workflowStatus = models.PositiveIntegerField(choices=WORKFLOW_STATUS_CHOICES,
                                                 default=DEFAULT_WORKFLOW_STATUS)
    mtime = models.DateTimeField(null=True, blank=True)

    uuid = UuidField()
    extras = ExtrasField(help_text="A place to add extra fields if we need them but for some reason can't modify the table schema.  Expressed as a JSON-encoded dict.")

    objects = AbstractModelManager(parentModel=None)

    class Meta:
        abstract = True

    def save(self, **kwargs):
        self.mtime = datetime.datetime.now()
        super(Feature, self).save(**kwargs)

    def deleteFiles(self):
        shutil.rmtree(self.getDir(), ignore_errors=True)

    def getCachedField(self, field):
        relatedId = getattr(self, '%s_id' % field)
        key = 'fieldCache-geocamCore-Feature-%s-%d' % (field, relatedId)
        result = cache.get(key)
        if not result:
            result = getattr(self, field)
            cache.set(key, result)
        return result

    def getCachedFolder(self):
        return self.getCachedField('folder')

    def getCachedAuthor(self):
        return self.getCachedField('author')

    def utcToLocalTime(self, dtUtc0):
        dtUtc = pytz.utc.localize(dtUtc0)
        localTz = pytz.timezone(self.getCachedFolder().timeZone)
        dtLocal = dtUtc.astimezone(localTz)
        return dtLocal

    def hasPosition(self):
        return self.minLat != None

    def getAuthor(self):
        pass

    def __unicode__(self):
        return '%s %d %s %s %s %s' % (self.__class__.__name__, self.id, self.name or '[untitled]', self.timestamp.strftime('%Y-%m-%d'), self.author.username, self.uuid)

    def getDateText(self):
        return self.utcToLocalTime(self.timestamp).strftime('%Y%m%d')

    def getDirSuffix(self, version=None):
        if version == None:
            version = self.version
        idStr = str(self.id) + 'p'
        idList = [idStr[i:(i+2)] for i in xrange(0, len(idStr), 2)]
        return [self.__class__.__name__.lower()] + idList + [str(version)]

    def getDir(self, version=None):
        return os.path.join(settings.DATA_DIR, *self.getDirSuffix(version))

    def getIconDict(self, kind=''):
        return dict(url=getIconUrl(self.icon + kind),
                    size=getIconSize(self.icon + kind))

    def getUserDisplayName(self, user):
        if user.last_name == 'group':
            return user.first_name
        else:
            return '%s %s' % (user.first_name.capitalize(),
                              user.last_name.capitalize())
            return result

    def getProperties(self):
        tagsList = tagging.utils.parse_tag_input(self.tags)
        author = self.getCachedAuthor()
        authorDict = dict(userName=author.username,
                          displayName=self.getUserDisplayName(author))
        return dict(name=self.name,
                    version=self.version,
                    isAerial=self.isAerial,
                    author=authorDict,
                    notes=self.notes,
                    tags=tagsList,
                    icon=self.getIconDict(),
                    localId=self.id,
                    subtype=self.__class__.__name__
                    )

    def cleanDict(self, d):
        return dict(((k, v)
                     for k, v in d.iteritems()
                     if v not in (None, '')))

    def getGeoJson(self):
        return dict(type='Feature',
                    id=self.uuid,
                    geometry=self.getGeometry(),
                    properties=self.cleanDict(self.getProperties()))

    def getDirUrl(self):
        return '/'.join([settings.DATA_URL] + list(self.getDirSuffix()))
Пример #15
0
class Assignment(models.Model):
    folder = models.ForeignKey(Folder)
    unit = models.ForeignKey(Unit,
                             help_text='The unit you are assigned to.')
    title = models.CharField(max_length=64, blank=True, help_text="Your title within unit.  Example: 'Sit Unit Leader'")
    uuid = UuidField()
Пример #16
0
class AbstractMapNode(AbstractNode):
    """
    Abstract Map Node for an entry in the map tree, which can have a parent.
    """
    modifier = models.CharField('modifier', max_length=200, null=True, blank=True)
    modification_time = models.DateTimeField(null=True, blank=True, db_index=True)
    deleted = models.BooleanField(blank=True, default=False)
    uuid = UuidField(primary_key=True)

    @property
    def parent(self):
        """ child classes must define parent"""
        return None

    def getEditHref(self):
        """ child classes must define edit href
        """
        return None

    def getStart(self):
        """ If this is a map layer with time, return the start time """
        return None

    def getEnd(self):
        """ If this is a map layer with time, return the end time """
        return None

    def getInterval(self):
        """ If this is a map layer with time, return the interval in decimal seconds """
        return None

    def getTimeUrl(self, theTime):
        """ If this is a map layer with time, return the url to get the data for that time """
        return None

    def get_tree_json(self):
        """ Get the json block that the fancy tree needs to render this node """
        result = {"title": self.name,
                  "key": self.uuid,
                  "tooltip": self.description,
                  "data": {"type": self.__class__.__name__,
                           "parentId": None,
                           "href": self.getEditHref()}
                  }
        if self.parent:
            result['data']['parentId'] = self.parent.uuid
        # this slowed the tree down ... for now just put in overlaytime
        # if self.getStart():
        #     result['data']['start'] = self.getStart()
        # if self.getEnd():
        #     result['data']['end'] = self.getEnd()
        # if self.getInterval():
        #     result['data']['interval'] = self.getInterval()
        if self.getKmlUrl():
            result['data']['kmlFile'] = self.getKmlUrl()

        return result

    def getKmlUrl(self):
        """ If this element has an url which returns kml, override this function to return that url. """
        return None

    def __unicode__(self):
        return self.name

    class Meta:
        abstract = True
        ordering = ['name']