Sahana introduction to the code v2

Post on 18-May-2015

1.439 views 7 download

Tags:

Transcript of Sahana introduction to the code v2

Sahana Eden:Emergency

Development Environment2 July 2010, Sahana Camp

Fran Boon

fran@sahanafoundation.org

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