Version 17 (modified by 12 years ago) ( diff ) | ,
---|
Simple Generic Location Tracking System
Table of Contents
S3 provides a simple generic location tracking system.
This is implemented in modules/s3db/sit.py
and modules/s3/s3track.py
and can be applied to virtually all entities.
Background
Location tracking means that an instance of an entity can have:
- a base location
- a current location X at time Y (a so-called "presence log" preserving all locations over time)
The tracking system provides the data model and an API to handle both cases.
Base Location
The base location can be implemented for an entity by adding a field location_id
to the table which references the gis_location
table.
As this is a pre-defined re-usable field, simply add it to your model:
table = self.define_table(tablename, ... self.gis_location_id(), # <= add this to give the entity a base location ...)
Presence Log
To capture the current location of an instance at several timepoints, you need to make your entity "trackable".
This can be done by defining sit_trackable
as super-entity:
table = self.define_table(tablename, ... self.super_link("track_id", "sit_trackable"), # <= add the super-entity key ...) s3db.configure(table, super_entity="sit_trackable", # <= configure sit_trackable as super-entity ...)
There is no problem to have this together with other super-entities (the number of super-entities per table is not limited):
table = self.define_table(tablename, ... super_link("site_id", "org_site"), super_link("track_id", "sit_trackable"), ...) s3db.configure(table, super_entity=("org_site", "sit_trackable"), # <= multiple allowed! ...)
API
S3Trackable
The API uses a wrapper class for database records - S3Trackable
.
You can create an S3Trackable by using the s3tracker
function with the table and the record ID:
trackable = s3tracker(db.my_table, 1)
Instead of the table, you can specify the tablename:
trackable = s3tracker(tablename="my_table", record_id=1)
You can also specify a query:
query = (db.my_table.uuid == "9207324b-5dc6-439b-9410-8920cbaf8a34") trackable = s3tracker(query=query)
or a record (must be a Row instance):
record = db(db.my_table.uuid == "9207324b-5dc6-439b-9410-8920cbaf8a34").select(limitby=(0, 1)).first() trackable = s3tracker(record=record)
It is possible to specify multiple records for the same S3Trackable
. If multiple records are specified or found, then they will be handled as one trackable object with multiple locations, i.e. if you retrieve its location, you will get a list of locations back. If you update its location, all of its records will be updated the same way.
It is possible to specify a super-entity for an S3Trackable
. In this case, the tracker will automatically find the respective records in the instance table(s) and use them instead:
trackable = s3tracker(db.pr_pentity, 1) # <= the super-entity is automatically replaced by the respective instance record (e.g. in pr_person)
Base Location
To retrieve the base location of an object, use get_base_location()
:
base_location = s3tracker(db.pr_person, 1).get_base_location()
This will return the full gis_location record(s) for the respective base location(s).
To set the base location of an object, use set_base_location():
s3tracker(db.pr_person, 1).set_base_location(25) # <= use the record ID of a gis_location
Other ways to specify the location:
# Using a location record location = db(db.gis_location.id == 25).select(limitby=(0, 1)).first() s3tracker(db.pr_person, 1).set_base_location(location) # Using another object containing a location_id hospital = db(db.hms_hospital.id == 3).select(limitby=(0, 1)).first() s3tracker(db.pr_person, 1).set_base_location(hospital) # Using another trackable: trackable = s3tracker(db.hms_hospital, 3) s3tracker(db.pr_person, 1).set_base_location(trackable)
Current location
When setting/retrieving the current location of an object, you can additionally specify a time point:
from datetime import datetime, timedelta one_hour_ago = datetime.utcnow() - timedelta(hours=1) location = s3tracker(db.pr_person, 1).get_location(timestmp=one_hour_ago)
NOTE: all times in the tracking system are considered to be in UTC!
To set a current location, you can use set_location()
.
s3tracker(db.pr_person, 1).set_location(25, timestmp=datetime.utcnow())
If you omit the timestmp
parameter, then the current server time is used.
To specify the location, you have the same options in set_location()
as in set_base_location()
.
Check-in/out
It might be desirable to track one object together with another. For example, if a person is in a vehicle, and the vehicle's location changes, then the location of the person should change as well.
This can be achieved by using the check_in()
and check_out()
methods.
>>> s3tracker(db.vt_vehicle, 1).set_location(40) # <= set a current location for the vehicle >>> s3tracker(db.pr_person, 1).check_in(db.vt_vehicle, 1) # <= check in the person to the vehicle >>> location = s3tracker(db.pr_person, 1).get_location() # <= get the location of the person >>> location.id 40 >>> s3tracker(db.vt_vehicle, 1).set_location(73) # <= set a new location for the vehicle >>> location = s3tracker(db.pr_person, 1).get_location() # <= get the location of the person >>> location.id 73 >>> s3tracker(db.pr_person, 1).check_out(db.vt_vehicle, 1) # <= check out the person from the vehicle >>> s3tracker(db.vt_vehicle, 1).set_location(21) # <= set a new location for the vehicle >>> location = s3tracker(db.pr_person, 1).get_location() # <= get the location of the person >>> location.id 73
When you set the location of a checked-in object explicitly using set_location()
, then the object will automatically be checked-out from wherever it has been checked-in before.
Both check_in()
as well as check_out()
accept the timestmp
parameter to set the time of the check-in/out explicitly. If omitted, then the current server time is used.
If you specify a super-entity instance to check-in to, then the respective instance record will automatically be used:
s3tracker(db.pr_person, 1).check_in(db.org_site, 4) # <= checks-in the person to the respective org_site instance, i.e. hospital or office...
Additional Options
When retrieving locations by get_location()
or get_base_location()
, you can specify additional parameters for the result:
- _fields (list/tuple of
Field
instances) to select fields fromgis_location
(None
for ALL) - _filter (
Query
instance) to filter the gis_location records - as_rows (boolean) return the result as
Rows
object (work like a DALselect()
)