Sahana introduction to the code v2
Transcript of Sahana introduction to the code v2
Stack OverviewServer
Sahana Eden (S3)
Web2Py
Python
HTML
JavaScript
CSS
Client Browser
Eclipse Firebug
Set yourself uphttp://61.67.7.140/
•Linux:• sudo su -• setup_ubuntu.sh
•Windows:• unzip Sahana-Eden.zip to c:\• Eden-Python-Installer-Dev.exe
• c:\bin\python26
• jre-6u21-windows-i586.exe• putty-0.60-installer.exe
Windows: Layout• c:\bin\python26• c:\bin\bzr.bat• c:\bin\grep.exe• c:\bin\ssh.exe• c:\bin\eclipse• c:\bin\w2p.cmd• c:\bin\web2py\applications\eden
Add C:\Bin to the system PATH• Control Panel | System | Advanced | Environment Variables• C:\Bin;…
Configure Eclipse• Perspectives
• Python Interpreter
• New PyDev Project• Web2Py folder
• Debug Configuration• web2py.py• -a password
Tea
Morning Session
• Model View Controller– Filesystem Layout
• Web2Py execution model
• S3 REST Controller
• Sahana Modules– Build a New Module
Model-View-Controller
• web2py/applications/eden/– controllers– models– modules– static– views
Model-View-Controller• Models
– Define Tables in the Database
• Controllers– Workflow, Logic
• Views– HTML / JS Templates parsed server-side– JS functions then run client-side in browser
Model-View-Controller
• Static– no server-side processing– Images– CSS– JavaScript
• Modules– Python libraries
JavaScript
Models
• All executed every request
• In alphabetical order
• Within web2py environment
Models
• Define Tables in Database– Live Migrations:
Tables created/altered in database
• Utility functions & variables which are used by multiple controllers
Core Models
• 000_config.py– deployment settings for easy customisation
• 00_db.py– connect to the database– instantiate classes
• 00_settings.py– read deployment settings & action them
Core Models (continued)• 00_tables.py
– define some re-usable fields for tables– define admin tables
• 00_utils.py– patch session– utility functions
• 01_crud.py– REST Controller front-end
Core Models (continued)
• 01_menu.py– build the Menus
• zzz_1st_run.py– Import default data on 1st run
(needs all tables defined 1st)
Modules• Sahana concept
– Logical grouping of user-facing functionality– Not to be confused with Python modules
• i.e. Not web2py/applications/eden/modules
• Consist of:– Model(s)– Controller– Views
Sahana Modules• org - Organisation Registry
• pr - Person Registry
• cr - Shelter Registry
• hms - Hospital Management
• rms - Request Management
• vol – Volunteer Management
• msg - Messaging
• gis - Mapping
Resources• person (pr)• location (gis)• organisation (org)• hospital (hms)• shelter (cr)
S3 REST Controller• Args:
– /update, /create, /delete
• HTTP verbs:– GET, PUT (POST), DELETE
• Representations:– .html, .json, .xml, .xls, .pdf
eden module resource
Emergency• We have to build a module by lunchtime!
Adding a new module
• Vehicle Tracking System
• Name: vts• Resources:
– vehicle– driver– location
Model: Define Table
models/vts.py
# Vehicle resource
table = db.define_table("vts_vehicle",
Field("registration"),
Field("make"),
Field("model"),
Field("supplier"),
)
Controllers
• Entire Controller is executed– Manipulation done outside of functions is visible
to all functions
• Function which has been called is executed
• Functions with no arguments are automatically visible via URLs
Controller: S3 REST
controllers/vts.py
def vehicle():
return shn_rest_controller("vts", "vehicle")
View
None needed at 1st – can RAD without them
& then polish later
Try:
http://127.0.0.1:8000/eden/vts/vehicle
1.Create a vehicle.
2.View in different formats:– .json, .xml, .xls, .pdf
Morning Session
• Model View Controller– Filesystem Layout
• Web2Py execution model
• S3 REST Controller
• Sahana Modules– Build a New Module
Lunch
Afternoon Session
• Web2Py Shell• Views• Extend our New Module• Internationalisation• Menus• Joined Resources• DAL: Database Abstraction Layer• Mapping
Exploring: Web2Py shell• Python is great for interactive exploring!
– Web2Py allows this too
w2p
python web2py.py –S eden –M
• Explore objects with tabdb.
gis.
Views: Web2Py• Defaults to views/controller/function.html
• Python code inside {{ }} is parsed server-side
• Views can extend & include other views
• Extend ‘layout’ for overall look/feel{{extend "layout.html"}}
Views: S3 REST
• REST has defaults– create.html– display.html– list.html– List_create.html– update.html
Views: Custom
• They can also be customised: views/vts/vehicle_list_create.html
{{extend "layout.html"}}
{{rheader="Register a new vehicle in the system:"}}
{{include "_list_create.html"}}
Internationalisation
{{rheader=T("Register a new vehicle in the system:")}}
http://127.0.0.1:8000/admin/default/design/eden#languages
Update all languages
InternationalisationTry these in the shell: w2p
•print T("My Text")
•print T("<P>My HTML</P>")
•print "<P>" + T("My HTML") + "</P>"
•print "<P>" + str(T("My HTML")) + "</P>"
•print "<P>" + Tstr("My HTML") + "</P>“
ViewsVariables only visible to views if:
•explicitly passed in return dict()
•stored in global variables:•request, response, session
http://127.0.0.1:8000/eden/default/about
Model: SQL constraints
models/vts.py
table = db.define_table("vts_vehicle",
Field("registration", unique=True),
Field("make"),
Field("model"),
Field("supplier"),
)
Model: Validators
models/vts.py
table = db.define_table("vts_vehicle",
Field("registration", unique=True),
Field("make"),
Field("model"),
Field("supplier"),
)
table.registration.requires = NOT NEEDED!
IS_NOT_IN_DB(db, table.registration)
Model: Field Types
models/vts.py
table = db.define_table("vts_vehicle",
Field("registration", unique=True),
Field("make"),
Field("model"),
Field("purchase_date", "date"),
Field("supplier"),
)
Model: Default Values
models/vts.py
table = db.define_table("vts_vehicle",
Field("registration", unique=True),
Field("make"),
Field("model"),
Field("purchase_date", "date", default=request.utcnow),
Field("supplier"),
)
Controller: Labels controllers/vts.py
def vehicle():
db.vts_vehicle.registration.label = "License Plate"
return shn_rest_controller("vts", "vehicle")
Controller: Labels controllers/vts.py
def vehicle():
db.vts_vehicle.registration.label = T("License Plate")
return shn_rest_controller("vts", "vehicle")
Controller: Comment controllers/vts.py
def vehicle():
db.vts_vehicle.registration.label = T("License Plate")
db.vts_vehicle.registration.comment = \
SPAN("*", _class="req")
return shn_rest_controller("vts", "vehicle")
Controller: CRUD Strings controllers/vts.pydef vehicle():
…
s3.crud_strings["vts_vehicle"] = Storage(
title_create = T("Add Vehicle"),
title_display = T("Vehicle Details"),
title_list = T("List Vehicles"),
title_update = T("Edit Vehicle"),
label_create_button = T("Add Vehicle"),
msg_record_created = T("Vehicle added"),
…
)
…
Controller: Action buttons
controllers/vts.pydef vehicle():
…
def veh_postp(jr, output): shn_action_buttons(jr)
return output
response.s3.postp = veh_postp
output = shn_rest_controller("vts", "vehicle")
return output
How do we Navigate?• Modules menu
• Modules list on frontpage
• Menu within Module
URL(r=request, a=application, c=controller, f=function, args=[], vars={})
Enable Module models/000_config.py
deployment_settings.modules = Storage(
…
vts = Storage(
name_nice = "Vehicle Tracking System", description = "Track vehicles",
module_type = 10
),
…
)
Index page controllers/vts.py
module = request.controller
def index():
"Custom View"
module_name = \ deployment_settings.modules[module].name_nice
return dict(module_name=module_name)
View
views/vts/index.html
{{extend "layout.html"}}
{{=H2(T(module_name))}}
<p>This module allows users to track their vehicles</p>
{{=LI(A("List Vehicles", _href=URL(r=request, f="vehicle")))}}
Controller: Menu controllers/vts.py
response.menu_options = [
[T("Vehicles"), False, URL(r=request, f="vehicle"),[[T("List"), False, URL(r=request, f="vehicle")],
[T("Add"), False, URL(r=request, f="vehicle", args="create")] ]]]
Tea
Joined Resources
• So far:– Resource = Single Table
• Reality:– Resource spread out over multiple Tables
Joined Resources: Model
• Link Vehicle to Location– vts_presence
Joined Resources: Model
models/vts.pymodule = "vts"
resource = "presence"
tablename = "%s_%s" % (module, resource)
table = db.define_table(tablename,
Field("vehicle_id"),
Field("location_id"),
)
Joined Resources: Model
models/vts.py
table = db.define_table(tablename,
Field("vehicle_id", db.vts_vehicle),
Field("location_id", db.gis_location),
)
Joined Resources: Model
models/vts.py
table = db.define_table(tablename,
Field("vehicle_id", db.vts_vehicle),
location_id,
)
Joined Resources: Controller controllers/vts.py
def presence():
resource = request.function
return shn_rest_controller(module, resource)
http://127.0.0.1:8000/eden/vts/presence
Joined Resources: Model
models/vts.py
table = db.define_table(tablename,
Field("vehicle_id", db.vts_vehicle),
location_id,
)
table.vehicle_id.requires = IS_ONE_OF(db,
"vts_vehicle.id",
"vts_vehicle.registration")
Joined Resources: Model
models/vts.py
table = db.define_table(tablename,
Field("vehicle_id", db.vts_vehicle),
location_id,
Field("time", "datetime"),
)
table.vehicle_id.requires = IS_ONE_OF(db, "vts_vehicle.id", "vts_vehicle.registration")
table.time.requires = IS_DATETIME()
DAL:Database Abstraction Layer• SQLite
• out of the box
• MySQL• well-tested with Eden
• PostgreSQL• what we want to use for Spatial support
• Oracle, DB2, MS SQL, Firebird, GAE
DALTry these in the shell: w2p
db.define_table("person", Field("name"))
id = db.person.insert(name="max")query = (db.person.id == id)
db(query).count()
db(query).update(name="Max")
rows = db(query).select(orderby=db.person.name)for row in rows:
print row.name
db(query).delete()
DAL
• For shell scripts (e.g. Cron tasks):
db.commit()
• Beware locking (SQLite)
JR: Represent models/vts.py
table.vehicle_id.represent = lambda id: \db(db.vts_vehicle.id == id).\ select().first().registration
table.vehicle_id.represent = lambda id: db(db.vts_vehicle.id == id).\ select(limitby=(0,1)).first().registration
table.vehicle_id.represent = lambda id: db(db.vts_vehicle.id == id).\ select(db.vts_vehicle.registration, limitby=(0,1)).first().registration
JR: Components models/vts.py
# Presence as component of vehicle
s3xrc.model.add_component(module,
resource,
multiple=True,
joinby=dict(vts_vehicle="vehicle_id"),
deletable=True,
editable=True)
http://127.0.0.1:8000/eden/vts/vehicle/1/presence
Components: RHeader controllers/vts.pydef shn_vts_rheader(jr, tabs=[]):
if jr.representation == "html":
rheader_tabs = shn_rheader_tabs(jr, tabs)
vehicle = jr.record
rheader = DIV(TABLE(
TR(TH(T("Vehicle: ")), vehicle.registration,
TH(T("Make: ")), vehicle.make),
TR(TH(T("Purchase Date: ")),
vehicle.purchase_date,
TH(T("Model: ")), vehicle.model)),
rheader_tabs)
return rheader
return None
Components: Rheader Tabs
controllers/vts.pydef vehicle():
…
output = shn_rest_controller(module, "vehicle",
rheader=lambda jr: shn_vts_rheader(jr,
tabs = [(T("Basic Details"), None),
(T("Presence"), "presence")
]),
sticky=True)
return output
http://127.0.0.1:8000/eden/vts/vehicle/1/presence
Show me the Map!
Map
Different Layers:
•Base
•Overlay– Internal– External (Earthquakes)
Display on Map
• Map displays Feature Groups
• Feature Groups contain Feature Classes
• Locations have Feature Classes
• “Vehicles” are set up ready to go
Simplify data entry:GIS Controller
controllers/gis.py
def location()…
if "vts_presence" in caller:
fc = db(db.gis_feature_class.name == "Vehicle").\ select(db.gis_feature_class.id, limitby=(0, 1)).first()
…
Simplify data entry:GIS View
views/gis/location_popup.html…
{{elif "vts_presence" in request.vars.caller:}}
// If coming from the VTS then populate defaults for the // fields & Hide unnecessary rows
var location_name = self.parent.\ $('#vts_presence_vehicle_id__row > td.w2p_fw').html();
if (location_name) { $("#gis_location_name").val(location_name); //$("#gis_location_name__row").hide();
}
…
HTML5 Geolocation
views/gis/location_popup.html
…
{{elif "vts_presence" in request.vars.caller:}}
…
if (navigator.geolocation){
navigator.geolocation.getCurrentPosition(getCurrentPosition);
}
…
Documentation
• Examples from other modules
• Developer Guidelines on Wiki:http://eden.sahanafoundation.org/wiki/DeveloperGuidelines
• But the best…?
Use the Source, Luke!Many resources and tricks on the Internet find you will, but the ultimate answers only in the source lie
S3 is built on Web2Py
REST
CRUD
SQLFORM
FORM
Form submission
• Use Firebug’s Net Panel to look at a form submission.
• Each field has an entry in form.vars
• Can add additional vars to the form in a custom View
• Can process these within our Controller– onvalidation: Before DB I/O– onaccept: After DB I/O
Web2Py CRUD
web2py/gluon/tools.pyself.settings.create_onvalidation = None self.settings.update_onvalidation = None self.settings.delete_onvalidation = None self.settings.create_onaccept = None self.settings.update_onaccept = None self.settings.update_ondelete = None self.settings.delete_onaccept = None
•onvalidation: Before DB I/O
•onaccept: After DB I/O
Afternoon Session
• Web2Py Shell• Views• Extend our New Module• Internationalisation• Menus• Joined Resources• DAL: Database Abstraction Layer• Mapping
End