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)
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
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.")
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']
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.")
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
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.")
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)
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)
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)
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()
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
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']
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()))
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()
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']