GHC Participant Training


Published on

  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide
  • S3 is a codename for Sahana Eden Server & Client can be on the same machine Each part needs to be debugged separately: Eclipse works well for the Server-side Python, whilst Firebug is excellent for the generated HTML/CSS/JavaScript JavaScript libraries we use include jQuery & ExtJS
  • There are notes on the Wiki for installing a developer environment on Windows, Mac or Linux machines if you wish to run in other environments later.
  • Look around the filesystem in web2py/applications/eden folder Similar layout structure to other MVC frameworks, such as Ruby-on-Rails & Django
  • JavaScript which doesn’t require server-side parsing put into static Python Libraries available: System Python, Web2Py, Eden (S3*)
  • The problems faced by both the victims of and responders to disasters are numerous. Beyond the trauma, this involves having to coordinate, manage and track things. In countries at risk of being affected by large scale sudden onset disasters, these problems can be massive and overwhelming.
  • Normally you work in a local branch on your PC. You Merge changes in regularly from Trunk. You Push your changes to your branch on LaunchPad (You Pull from LaunchPad to update your server sites)
  • Minimise the time taken to process Manage dependencies No need to import most of the libraries in ‘gluon/’
  • Create a new file in the models folder called & type in the text above – be careful to use the correct case for Field!
  • Create a new file in the controllers folder called & type in the text above – be careful to get the indentation correct!
  • Note that you’ll need to Register yourself as a user on the system before you’re allowed to create a resource. The 1 st user to register gets the Administrator role by default.
  • Create a new folder in the views folder called ‘ inc ’ & inside that put a new file ‘ incident_list_create.html’ . Type in the text above. Refresh the incident page to see the new text: incident No need to learn another macro language Defaults (non-REST) to views/controller/function.html Open the About page ( ) & browse the variables We store our S3 variables in response.s3.*, session.s3.* so as not to pollute the Web2Py namespace
  • unique=True is a SQL-level constraint. Open your from the models directory & add the extra code above. Validators are DAL-level constraints which produce nice error messages instead of tickets! Validators provide server-side validation & some also provide client-side rendering & validation Web2Py automatically adds a Validator when the SQL constraint is set.
  • Fields default to type ‘string’. ‘ date’ is the SQL-level field type & also provides the class in the HTML which means we get a date widget Add the extra code above to your file & refresh the page, now enter a date.
  • Add the extra code above to your file & refresh the page to see the default value
  • Add this to your model (underneath the table definition) & try it out
  • You can check out the languages file, if you wish: Update all languages
  • Add this to your model (underneath the table definition) & try it out Comment is rendered as ‘column3’ (although can be moved using ‘formstyle’) We also use the same space for Help buttons & ‘Add New Resource’ links. NB For optimal performance, we normally add such comments in the Controller instead & put them inside a ‘prep’ to tale effect just when ‘r.representation in shn_interactive_view_formats’
  • Add this to your model (underneath the table definition) & try it out Comment is rendered as ‘column3’ (although can be moved using ‘formstyle’) We also use the same space for Help buttons & ‘Add New Resource’ links. NB For optimal performance, we normally add such comments in the Controller instead & put them inside a ‘prep’ to tale effect just when ‘r.representation in shn_interactive_view_formats’
  • Add this to your model (underneath the table definition) & try it out Comment is rendered as ‘column3’ (although can be moved using ‘formstyle’) We also use the same space for Help buttons & ‘Add New Resource’ links. NB For optimal performance, we normally add such comments in the Controller instead & put them inside a ‘prep’ to tale effect just when ‘r.representation in shn_interactive_view_formats’
  • This should be available as a script or an alias ‘w2p’ iPython needs to be installed for object exploring.
  • Add this text to the file in the models folder. Navigate to the home page to see the module appear both there & on the menu. Try navigating to the module. Module Type 10 means appears in the ‘more’ section of the default modules menu. (Most deployments will create a fully-customised menu anyway)
  • Add this text to your controller file & now open the module. “ Custom View” is a Doc String One should be added to all functions for automatic documentation generators & interactive browsing of docs You can try this out in the Interactive Shell: w2p execfile("applications/eden/controllers/", globals()) help(index)
  • Create a file called ‘index.html’ inside the views/inc folder See plain HTML being interspersed with the server-side parsed Python Look at another module’s index.html What’s bad about this index?
  • Add this text to your controller (outside the functions – e.g. at the top) & see what effect this has on your module.
  • Copt this text into your model. Note the use of string substitution to define the tablename
  • Tells SQL that these fields are ‘reference’ fields (i.e. Foreign Keys) - They store integers which are a pointer to the ID field in the other tables.
  • Copy this text into your Model & refresh the presence page Reusable field defined in models/ Includes additional useful configuration such as .requires & .represent
  • Copt this text into your controller. Try it out. Note the automation of the resource name to minimise the editing after copy/paste of new functions
  • Copy this text into your model & see what it does for the page IS_ONE_OF() will provide us a dropdown showing license plates, even though we actually store the ID. This is an S3 validator which extends Web2Py’s IS_IN_DB() to support the ‘deleted’ status & also allow the use of lambdas as formatting options.
  • We can add additional fields into the link table.
  • IS_ONE_OF provides the representation in the dropdown table.field.represent provides the representation in Display/List views A lambda is a Python feature for defining anonymous functions on the fly:
  • Optimise the query – we know we only want a single record & a single field from that record
  • Add the optimised query to your model.
  • Add this to your model underneath the definition of the Presence table & try out the URL. No need for dedicated presence() controller
  • Add this Resource Header definition to your Controller. Be careful to get the end brackets & commas right! ‘ r’ is the resource
  • Then plug it into the Controller
  • Take a look at the HTML structure of the page in Firebug.
  • Web2Py source code very accessible – small enough to be able to grasp the whole thing after not too long FORM defined in gluon/ Dumb HTML elements SQLFORM defined in gluon/ Self-processing forms which update the database CRUD defined in gluon/ Wrappers around SQLFORM - We actually change CRUD a lot & are looking to remove it completely – building straight on top of SQLFORM() REST defined in modules/ RESTful interface & XML representations If you drop down a level, then need to do things more manually & need to take care of framework issues yourself.
  • Whilst we spend a lot of time updating developer documentation, the actual codebase always moves faster, so the source is always the most authoritative source of information.
  • GHC Participant Training

    1. 1. Sahana Eden: Emergency Development Environment 30 September 2010, Grace Hopper Celebration Fran Boon [email_address]
    2. 2. Agenda <ul><li>Stack Overview </li></ul><ul><li>Getting Set up </li></ul><ul><li>Model View Controller </li></ul><ul><ul><li>Filesystem Layout </li></ul></ul><ul><li>Build a New Module </li></ul><ul><ul><li>S3 REST Controller </li></ul></ul>
    3. 3. Stack Overview <ul><li>Server </li></ul>Client Browser Eclipse Firebug Sahana Eden (S3) Web2Py Python HTML JavaScript CSS
    4. 4. Installation: Virtual Machine <ul><li>Simplest way to start: </li></ul><ul><li> </li></ul><ul><li>Full Developer Environment: </li></ul><ul><li>Eclipse </li></ul><ul><li>Codebase (Web2Py/Sahana Eden) </li></ul><ul><li>Launch Eclipse to get started… </li></ul>
    5. 5. Model-View-Controller <ul><li>web2py/applications/ eden / </li></ul><ul><li>controllers </li></ul><ul><li>models </li></ul><ul><li>views </li></ul>
    6. 6. Model-View-Controller <ul><li>Models </li></ul><ul><ul><li>Define Tables in the Database </li></ul></ul><ul><li>Controllers </li></ul><ul><ul><li>Workflow, Logic </li></ul></ul><ul><li>Views </li></ul><ul><ul><li>HTML / JS Templates parsed server-side </li></ul></ul><ul><ul><li>JS functions then run client-side in browser </li></ul></ul>
    7. 7. Model-View-Controller <ul><li>Static </li></ul><ul><ul><li>no server-side processing </li></ul></ul><ul><ul><li>Images </li></ul></ul><ul><ul><li>CSS </li></ul></ul><ul><ul><li>JavaScript </li></ul></ul><ul><li>Modules </li></ul><ul><ul><li>Python libraries </li></ul></ul>
    8. 8. Modules <ul><li>Sahana concept </li></ul><ul><ul><li>Logical grouping of user-facing functionality </li></ul></ul><ul><ul><li>Not to be confused with Python modules </li></ul></ul><ul><ul><ul><li>i.e. not web2py/applications/eden/modules </li></ul></ul></ul><ul><li>Consist of: </li></ul><ul><ul><li>Model(s) </li></ul></ul><ul><ul><li>Controller </li></ul></ul><ul><ul><li>Views </li></ul></ul>
    9. 9. Sahana Modules <ul><li>Organisation Registry : org </li></ul><ul><li>Person Finder : pr, mpr, dvi </li></ul><ul><li>Request Management : rms </li></ul><ul><li>Inventory : inv </li></ul><ul><li>Shelter Registry : cr </li></ul><ul><li>Mapping : gis </li></ul><ul><li>Messaging : msg </li></ul>
    10. 10. Branches LaunchPad Merge proposal Trunk Local Branch Your Branch bzr merge bzr push
    11. 11. Request processing <ul><li>Web2Py environment setup </li></ul><ul><li>All Models executed (alphabetical order) </li></ul><ul><li>Controller executed </li></ul><ul><li>Controller Function executed </li></ul><ul><li>View parsed (explicit variable passing) </li></ul><ul><li>HTML/JS returned to browser </li></ul>
    12. 12. Emergency <ul><li>We have to build a module before bed! </li></ul>
    13. 13. Software Development Cycle
    14. 14. Incident Reporting System <ul><li>Data : </li></ul><ul><li>Location </li></ul><ul><li>Date </li></ul><ul><li>Reporter </li></ul><ul><li>Details </li></ul><ul><li>Screens : </li></ul><ul><li>Report Incident </li></ul><ul><li>View Incidents </li></ul><ul><li>Update Incident </li></ul>
    15. 15. Define Data <ul><li>models/ inc .py </li></ul><ul><li>table = db.define_table(&quot;inc_incident&quot;, </li></ul><ul><ul><ul><li>location_id(), </li></ul></ul></ul><ul><ul><ul><li>Field(&quot;date&quot;, &quot;date&quot;), </li></ul></ul></ul><ul><ul><ul><li>person_id(), </li></ul></ul></ul><ul><ul><ul><li>comments() </li></ul></ul></ul><ul><li>) </li></ul>
    16. 16. Forms for CRUD <ul><li>controllers/ inc .py </li></ul><ul><li>def incident(): </li></ul><ul><li>return shn_ rest _controller(&quot;inc&quot;, </li></ul><ul><li> &quot;incident&quot;) </li></ul>
    17. 17. We’re done! <ul><li> </li></ul><ul><li>Create an incident. </li></ul><ul><li>View in different formats: </li></ul><ul><ul><li>.json, .xml, .xls, .pdf </li></ul></ul><ul><ul><li>http:// </li></ul></ul>
    18. 18. Views: Custom REST <ul><li>views/ inc /incident_ list_create .html </li></ul><ul><li>{{extend &quot; layout .html&quot;}} </li></ul><ul><li><p>A Normal HTML Paragraph</p> </li></ul><ul><li>{{rheader=&quot;A Paragraph inserted inside the page&quot;}} </li></ul><ul><li>{{include &quot;_ list_create .html&quot;}} </li></ul><ul><li>{{include &quot;gis/location_autocomplete.js&quot;}} </li></ul><ul><li>Python code inside {{ }} is parsed server-side </li></ul><ul><li>Views can extend (one) & include (many) other views </li></ul><ul><li>Extend ‘ layout ’ for overall look/feel </li></ul><ul><li>Variables only visible to views if: </li></ul><ul><ul><li>explicitly passed in return dict() </li></ul></ul><ul><ul><li>stored in global variables: request , response , session </li></ul></ul>
    19. 19. SQL constraints / Validators <ul><li>models/ </li></ul><ul><li>table = db.define_table(&quot;inc_incident&quot;, </li></ul><ul><li>Field(&quot;code&quot;, unique=True ), </li></ul><ul><li>… </li></ul><ul><li>) </li></ul><ul><li>DAL supports Live Migrations </li></ul>
    20. 20. Field Types <ul><li>models/ </li></ul><ul><li>table = db.define_table (&quot;inc_incident&quot;, </li></ul><ul><li>… </li></ul><ul><li>Field(&quot;date&quot;, &quot; date &quot;), </li></ul><ul><li>… </li></ul><ul><li>) </li></ul>
    21. 21. Default Values <ul><li>models/ </li></ul><ul><li>table = db.define_table(&quot;inc_incident&quot;, </li></ul><ul><li>… </li></ul><ul><li>Field(&quot;date_date&quot;, &quot;date&quot;, default=request.utcnow ), </li></ul><ul><li>… </li></ul><ul><li>) </li></ul>
    22. 22. Labels <ul><li>models/ </li></ul><ul><li>table.person_id. label = &quot;Reporter&quot; </li></ul>
    23. 23. Internationalisation <ul><li>models/ </li></ul><ul><li>table.person_id. label = T(&quot;Reporter“) </li></ul>
    24. 24. Comments <ul><li>models/ </li></ul><ul><li>table.code. comment = SPAN(&quot;*&quot;, _class=&quot;req&quot;) </li></ul>
    25. 25. Requires <ul><li>models/ </li></ul><ul><li>db.gis_location. comment = … </li></ul><ul><li>db.gis_location. requires = IS_ONE_OF (…) </li></ul>
    26. 26. Represent <ul><li>models/ </li></ul><ul><li>db.gis_location. represent = shn_location_represent </li></ul>
    27. 27. Exploring: Web2Py shell <ul><li>Python is great for interactive exploring! </li></ul><ul><ul><li>Web2Py allows this too  </li></ul></ul><ul><li>w2p </li></ul><ul><li>python –S eden –M </li></ul><ul><li>Explore objects with tab </li></ul><ul><li>db. </li></ul><ul><li>gis. </li></ul>
    28. 28. DAL <ul><li>Try these in the shell: w2p </li></ul><ul><li>db.define_table(&quot;person&quot;, Field(&quot;name&quot;)) </li></ul><ul><li>id = db.person.insert(name=&quot;max&quot;) query = ( == id) </li></ul><ul><li>db(query).count() </li></ul><ul><li>db(query).update(name=&quot;Max&quot;) </li></ul><ul><li>rows = db(query).select( for row in rows: </li></ul><ul><li>print </li></ul><ul><li>db(query).delete() </li></ul>
    29. 29. End
    30. 30. Enable Module <ul><li>models/ </li></ul><ul><li>deployment_settings.modules = Storage( </li></ul><ul><li>… </li></ul><ul><li>#irs = Storage( </li></ul><ul><li>inc = Storage( </li></ul><ul><li>name_nice = &quot;Incident Reporting System&quot;, description = &quot;Report Incidents&quot;, </li></ul><ul><li> module_type = 3 </li></ul><ul><li>), </li></ul><ul><li>… </li></ul><ul><li>) </li></ul>
    31. 31. Index page <ul><li>controllers/ </li></ul><ul><li>module = request.controller </li></ul><ul><li>def index (): </li></ul><ul><li>&quot;Custom View&quot; </li></ul><ul><li>module_name = deployment_settings.modules[module].name_nice </li></ul><ul><li>return dict(module_name=module_name) </li></ul>
    32. 32. View <ul><li>views/inc/index.html </li></ul><ul><li>{{extend &quot;layout.html&quot;}} </li></ul><ul><li>{{=H2(module_name)}} </li></ul><ul><li><p>This module allows users to track their vehicles</p> </li></ul><ul><li>{{=LI(A(&quot;List Vehicles&quot;, _href=URL(r=request, f=&quot;vehicle&quot;)))}} </li></ul>
    33. 33. Controller: Menu <ul><li>controllers/ </li></ul><ul><li>response.menu_options = [ </li></ul><ul><li>[T(&quot;Incidents&quot;), False, URL(r=request, f=&quot;incident&quot;),[ [ T(&quot;List&quot;), False, URL(r=request, f=&quot;incident&quot;)], [T(&quot;Add&quot;), False, URL(r=request, f=&quot;incident&quot;, args=&quot;create&quot;)] ]]] </li></ul>
    34. 34. Joined Resources <ul><li>So far: </li></ul><ul><ul><li>Resource = Single Table </li></ul></ul><ul><li>Reality: </li></ul><ul><ul><li>Resource spread out over multiple Tables </li></ul></ul>
    35. 35. Joined Resources: Model <ul><li>Link Vehicle to Location </li></ul><ul><ul><li>vts_presence </li></ul></ul>
    36. 36. Joined Resources: Model <ul><li>models/ </li></ul><ul><li>module = &quot;vts&quot; </li></ul><ul><li>resource = &quot;presence&quot; </li></ul><ul><li>tablename = &quot;%s_%s&quot; % (module, resource) </li></ul><ul><li>table = db.define_table(tablename, </li></ul><ul><li>Field(&quot;vehicle_id&quot;), </li></ul><ul><li>Field(&quot;location_id&quot;), </li></ul><ul><li>) </li></ul>
    37. 37. Joined Resources: Model <ul><li>models/ </li></ul><ul><li>table = db.define_table(tablename, </li></ul><ul><li>Field(&quot;vehicle_id&quot;, db.vts_vehicle ), </li></ul><ul><li>Field(&quot;location_id&quot;, db.gis_location ), </li></ul><ul><li>) </li></ul>
    38. 38. Joined Resources: Model <ul><li>models/ </li></ul><ul><li>table = db.define_table(tablename, </li></ul><ul><li>Field(&quot;vehicle_id&quot;, db.vts_vehicle), </li></ul><ul><li>location_id() , </li></ul><ul><li>) </li></ul>
    39. 39. Joined Resources: Controller <ul><li>controllers/ </li></ul><ul><li>def presence (): </li></ul><ul><li>resource = request.function </li></ul><ul><li>return shn_rest_controller(module, resource) </li></ul><ul><li> </li></ul>
    40. 40. Joined Resources: Model <ul><li>models/ </li></ul><ul><li>table = db.define_table(tablename, </li></ul><ul><li>Field(&quot;vehicle_id&quot;, db.vts_vehicle), </li></ul><ul><li>location_id(), </li></ul><ul><li>) </li></ul><ul><li>table.vehicle_id. requires = IS_ONE_OF (db, </li></ul><ul><li>&quot;;, </li></ul><ul><li>&quot;vts_vehicle.registration&quot;) </li></ul>
    41. 41. Joined Resources: Model <ul><li>models/ </li></ul><ul><li>table = db.define_table(tablename, </li></ul><ul><li>Field(&quot;vehicle_id&quot;, db.vts_vehicle), </li></ul><ul><li>location_id(), </li></ul><ul><li>Field(&quot;timestmp&quot;, &quot;datetime&quot;) , </li></ul><ul><li>) </li></ul><ul><li>table.vehicle_id.requires = IS_ONE_OF(db, &quot;;, &quot;vts_vehicle.registration&quot;) </li></ul><ul><li>table.timestmp.requires = IS_DATETIME() </li></ul>
    42. 42. Represent <ul><li>table.vehicle_id. represent = lambda id: function </li></ul>
    43. 43. DAL: Optimised SQL queries <ul><li>“ Find the vehicle with this ID”: </li></ul><ul><li>db( == id).select() </li></ul><ul><li>db( == id).select().first() </li></ul><ul><li>“ Return the Registration”: </li></ul><ul><li>db( == id).select().first().registration </li></ul><ul><li>db( == id). </li></ul><ul><li>select( limitby =(0,1)).first().registration </li></ul><ul><li>db( == id). </li></ul><ul><li>select( db.vts_vehicle.registration , </li></ul><ul><li>limitby=(0,1)).first().registration </li></ul>
    44. 44. JR: Represent <ul><li>models/ </li></ul><ul><li>table.vehicle_id. represent = lambda id: db( == id). select(db.vts_vehicle.registration, limitby=(0,1)).first().registration </li></ul>
    45. 45. Components <ul><li>models/ </li></ul><ul><li># Presence as component of vehicle </li></ul><ul><li>s3xrc.model.add_component(module, </li></ul><ul><li>resource, </li></ul><ul><li>multiple=True, </li></ul><ul><li>joinby=dict(vts_vehicle=&quot;vehicle_id&quot;), </li></ul><ul><li>deletable=True, </li></ul><ul><li>editable=True) </li></ul><ul><li> </li></ul>
    46. 46. RHeader <ul><li>controllers/ </li></ul><ul><li>def shn_vts_rheader(r, tabs=[]): </li></ul><ul><li>if r.representation == &quot;html&quot;: </li></ul><ul><li>rheader_tabs = shn_rheader_tabs(r, tabs) </li></ul><ul><li>vehicle = r.record </li></ul><ul><li>rheader = DIV(TABLE( </li></ul><ul><li>TR(TH(T(&quot;Vehicle: &quot;)), vehicle.registration) </li></ul><ul><li>), </li></ul><ul><li>rheader_tabs </li></ul><ul><li>) </li></ul><ul><li>return rheader </li></ul><ul><li>return None </li></ul>
    47. 47. RHeader Tabs <ul><li>controllers/ </li></ul><ul><li>def vehicle(): </li></ul><ul><li>… </li></ul><ul><li>output = shn_rest_controller(module, &quot;vehicle&quot;, </li></ul><ul><li>rheader=lambda r: shn_vts_rheader(r, </li></ul><ul><li>tabs = [(T(&quot;Basic Details&quot;), None), </li></ul><ul><li> (T(&quot;Presence&quot;), &quot;presence&quot;) </li></ul><ul><li> ])) </li></ul><ul><li>return output </li></ul><ul><li> </li></ul>
    48. 48. RHeader Tabs
    49. 49. S3 is built on Web2Py REST CRUD SQLFORM FORM
    50. 50. Form submission <ul><li>Use Firebug’s Net Panel to look at a form submission. </li></ul><ul><li>Each field has an entry in form.vars </li></ul><ul><li>Can add additional vars to the form in a custom View </li></ul><ul><li>Can process these within our Controller </li></ul><ul><ul><li>onvalidation: Before DB I/O </li></ul></ul><ul><ul><li>onaccept: After DB I/O </li></ul></ul>
    51. 51. Documentation <ul><li>Examples from other modules </li></ul><ul><li>Developer Guidelines on Wiki: </li></ul><ul><li> </li></ul><ul><li>But the best…? </li></ul>
    52. 52. Use the Source, Luke! <ul><li>Many resources and tricks on the Internet find you will, but the ultimate answers only in the source lie </li></ul>