PABUG (Pt3) – Mobile Connection 1.1

<QUICKNOTE>
A coworker here has created a set of web forms, reports and database tables to collect all the conference information including presenters, sessions, tracks, attendance, exhibitors, vendors, etc.  I won’t go into any of that, other than to mention I now have at my disposal all the data I need to make an app, Yea !  Oh yea, and there’s some screenies way down below if you want to check out what I’ve done.  I’ve also made it further than this post as you can see in the screenie below, I’ll continue to work through posts to explain what I’m doing though so you can see the app mature over the next couple months.
</QUICKNOTE>

The Home Screen for the PABUG app

Sungard Higher Education released Mobile Connection 1.1 last week, which caused some great excitement in the developer world.  … ok, in my world it did.  I had obtained a pre-release version and started building out the PABUG app using that.  It was going well, but when the release version came out I decided to essentially start over due to some improved performance and bug fixes.  Enough had changed that it was worth doing.

So where do we start coding.  Firstly – we want to be able to list the conference sessions, tap on a session and view the session details.  Ideally, since this is a multi-day conference, I’d like to alternately be able to select a day and list only that days’ sessions.  To get even more granular – maybe list the tracks as well and list that tracks sessions.

So where to start.  Using Mobile Connection we need to create what is called an mApp … or MApp … or Mapp … not sure the proper spelling, but its pronounced – em-app <G>  Anyway an m-app (I’ll keep using creative ways to type it to keep your interest) – is essentially some functionality you pack up in a single directory in your apps code tree.  In our case, we create a directory called Session.  Rhodes (the underlying framework we’re using) uses the MVC architecture so we need a model (database – more on that soon), View – what the user sees, and the Controller – where all the magic happens.

When you’re at a conference, they typically have wireless throughout.  That said – this particular conference venue has partial wireless and the part that works is crap.  So we can assume you’ll be in sessions that have no connectivity.  Its an older building – so its also quite possible that you will not get a cell signal either (aka No Data).  You’re unplugged.  Not good for a mobile app.  So we need to plan accordingly.

Luckily – we can use the device to create a database for us, store the information we’ll need for later, we’ll call that caching – because its a much cooler term than storing.  So our app now needs a database, a model if you will.  We’ll need to grab the session list and all its data, presenters, tracks, level, etc and store it on the phone then render it to the user depending on what they tap on.

We create a session_controller.rb file and in the index method, check the Session model for data.  If the data count is <1 then we know its the first time the app has run, and we need to load ‘er up.

  def index     
    @sessions = Session.find(:all)
    if @sessions and @sessions.size<1
      sessions_result = fetch('Session Data',@@session_file_name, 'http://apps3.messiah.edu/mobile/pabug/session_load.php')
      insert_sessions  
    end
    @days = Session.find_by_sql("SELECT distinct(session_date) FROM Session ORDER BY session_date")
    render
  end

So we’ll go get the session list.  Hmmm, we’ll need to build a way to generate the session list.  Using PHP on a server here at Messiah, I quickly put together an SQL call to pull the session data I need from the database tables I mentioned above.  I concatenate that data in a format that my app will process when it creates the database on the device.  Below is an example of what that looks like -

"session_id"=>"321","session_title"=>"Testing a really long session title name.","session_date"=>"21-NOV-11","session_time"=>"10:10 AM - 11:10 AM","session_level"=>"Advanced","session_notes"=>"Session Notes","session_desc"=>"Session Description","session_room"=>"Regency","session_track"=>"--","primary_presenter"=>"Dorie Minich","other_presenters"=>""
"session_id"=>"113","session_title"=>"Mobile BOF","session_date"=>"24-NOV-11","session_time"=>"11:20 AM - 12:20 PM","session_level"=>"Beginner","session_notes"=>"","session_desc"=>"","session_room"=>"TBD","session_track"=>"Mobile |  Technical","primary_presenter"=>"Jonathan Wheat","other_presenters"=>""
"session_id"=>"114","session_title"=>"Imaging BOF","session_date"=>"24-NOV-11","session_time"=>"11:20 AM - 12:20 PM","session_level"=>"Beginner","session_notes"=>"","session_desc"=>"","session_room"=>"TBD","session_track"=>"Technical","primary_presenter"=>"Dorie Minich","other_presenters"=>""
"session_id"=>"141","session_title"=>"Mobile Connect : A Starting Point","session_date"=>"25-NOV-11","session_time"=>"1:30 PM - 2:30 PM","session_level"=>"Beginner","session_notes"=>"This is an intro to Mobile Connection and the framework.  Will allow users to get a feel for what you actually need to get started.","session_desc"=>"The Beginner&apos;s Guide to Mobile Connection and what you need to dig in","session_room"=>"TBD","session_track"=>"Mobile |  Technical","primary_presenter"=>"Jonathan Wheat","other_presenters"=>""
"session_id"=>"82","session_title"=>"BDMS Are the possibilities endless?","session_date"=>"24-NOV-11","session_time"=>"3:00 PM - 4:00 PM","session_level"=>"Intermediate","session_notes"=>"","session_desc"=>"Discussion of options available to load documents into imaging.","session_room"=>"Regency","session_track"=>"Technical","primary_presenter"=>"Dorie Minich","other_presenters"=>"Jonathan Wheat "
"session_id"=>"115","session_title"=>"Upgrade Planning","session_date"=>"25-NOV-11","session_time"=>"3:00 PM - 4:00 PM","session_level"=>"Intermediate","session_notes"=>"","session_desc"=>"","session_room"=>"TBD","session_track"=>"Accounts Receivable |  Accounts Receivable |  DBA |  Technical","primary_presenter"=>"Robert Getty","other_presenters"=>"Dorie Minich "

 

Since I now know what fields I’ll have in my device’s database, I need to set up the database in the app.  With Rhodes there are two types of database you can use.  PropertyBag, and Fixed Schema (more on those here).  Anyway, let me say, I’ve chosen Fixed Schema so I can perform real SQL on the table.  PropertyBag has a different type of syntax, and doesn’t allow for a distinct(x) ability, which I’ll need shortly.

With Fixed Schema, I have to specify the structure because, well, its fixed.  I do that in the session.rb file like this -

  property :session_id, :integer
  property :session_title, :string
  property :session_date, :date
  property :session_time, :string
  property :session_level, :string
  property :session_notes, :string
  property :session_desc, :string
  property :session_room, :string
  property :session_track, :string
  property :primary_presenter, :string
  property : other_presenters, :string

That just sets up the fields and types in the Session model.

Back in session_controller.rb I have a method I created called fetch, that pulls that data into the phone and stores the file on the device.  it looks like this -

  def fetch(desc,local_filename,remote_url)
    ## Use to pull down .JSON files for system loading
    if File.exists?(local_filename)
         File.delete(local_filename)
     end
     Rho::AsyncHttp.download_file(
       :url => remote_url,
       :filename => local_filename,
       :headers => {},
       :callback => {},
       :callback_param => "" )
  end

I use this method many times and in different apps I’ve worked on.  Sending it a URL and a temporary filename, the file is downloaded to the phone.  I can then process the file using my insert_session method like this to build my model -

  def insert_sessions
       Alert.show_popup :message=> 'Indexing Session Data',:title => 'One Moment Please'
       sleep 2
       Alert.hide_popup

      #READ FILE AND ASSEMBLE 'data'
         f = File.new(@@session_file_name)     
          f.each_line do |line|
             #take line and convert it into a hash
             h = {}
             line.split(',').each do |substr|
                ary = substr.strip.split('=>')
                h[ary.first.tr('"','')] = ary.last.tr('"','')
             end
             app_info "\n\n\n PROCESSING : " + line + "\n\n\n"

             @params['session'] = h
             @session = Session.new(@params['session'])
             @session.save
         end
    end

That builds a record for each session in the file I’ve pulled down (our model is complete and now loaded with data). Since this can take 3-5 seconds, I pop up an alert message to let the user know what’s going on and that the app didn’t lock up.  Once we have the sessions in a database on the phone, we can do just about anything with them.

I’ll create an array called @days and fill it with the distinct days from the database that sessions occur, and display that to the user so they can choose one day of sessions to look at.

@days = Session.find_by_sql("SELECT distinct(session_date) FROM Session ORDER BY session_date")

The find_by_sql method is NOT available with a Property Bag type database, thus the Fixed Schema decision.

On to the view, and I don’t mean that stupid talk show that bashes men all the time.  Our view (in this case index.erb) is rendered by the index method in our session_controler.rb file.  The overall function of view.erb is to render the data to the screen, this is where your HTML is used, along with some ruby code.  Below is my index.erb file, and you’ll notice there is some default Mobile Connection code still in there as well as necessary JQuery Mobile code.

<div id="session-page" data-role="page">

    <div data-role="header" data-position="fixed" data-theme="f" id='header'>
        <a href="<%= Mshell.show_url %>" data-direction="reverse"><img src="<%= Mshell.path %>/images/icon-back.png"/></a>
        <h1>&nbsp;</h1>
        <img src="<%= Mshell.path %>/images/icon-menu.png">
    </div><!-- /header -->

    <div data-role="content">
        <div><img src="<%= Mshell.path %>/images/icon-menu.png"></div>
        <div>    
            <ul>
                <% if Mshell.is_logged_in? %>
                    <li><a href="<%= url_for({ :controller => :Mshell, :action => :async_logout}) %>" data-transition="pop" data-direction="reverse"><%= Locale::Mshell[:label_logout] %></a></li>
                <% else %>
                    <li><a href="<%= url_for({ :controller => :Mshell, :action => :show_login}) %>" data-transition="pop"><%= Locale::Mshell[:label_login] %></a></li>
                <% end %>
                <li><a href="<%= Mshell.show_url %>" data-direction="reverse"><%= Locale::Mc[:label_home] %></a></li>
            </ul>
            <br/>
        </div>

        <ul data-role="listview">
            <div id='mappTitle'><%= Mshell.current_mapp.title %></div>
            <!-- ul style='margin-left:10px;margin-right:10px;'-->
            <li data-role="list-divider" role="heading">
                Full Session List
            </li>
            <li data-theme="c">
              <div>
                  <div>
                      <a href ="<%= url_for :action => :list_sessions %>">
                        List All Sessions
                    </a>    
                </div>
                <span></span>
              </div>
            </li>
        </ul>
        <br/><br/><br/>
        <ul data-role="listview">    
            <li data-role="list-divider" role="heading">
                Sessions By Day
            </li>
            <% @days.each do |session| %>
            <li data-theme="c">
              <div>
                  <div style='height:24px;'>
                      <% @session_date = session.session_date  %>
                      <a href ="<%= url_for :action => :list_sessions_byday, :query => {:session_date => session.session_date} %>">
                        <%= session.session_date %> <br/>
                    </a>    
                </div>
                <span></span>
              </div>
            </li>
            <% end %>        
        </ul>
    </div><!-- /content -->
</div><!-- /page -->

We’re using an unordered list to create this display.  JQuery Mobile has some fantastic theme-able elements and we’re exploiting them here.

The blue-ish code code is creating a list item with a link to list sessions

<a href ="<%= url_for :action => :list_sessions %>">

This is the Rhodes way of creating internal links to other methods.  Takes a little getting used to, but works great.  This method (list_sessions) will lists ALL of the sessions for the conference alphabetically.  We’ll add a way to filter those quickly using JQuery later.

The green bits of code are where we spin through the @days array, assign each element to an object called session and then we can access each one with dot (.) notation.  For example session.session_date displays 21NOV11 for us (only because I haven’t formatted the date yet). Each day then becomes a link off to our list_sessions_byday method that list all the sessions for one day.

<a href ="<%= url_for :action => :list_sessions_byday, :query => {:session_date => session.session_date} %>">

You’ll notice to pass a parameter in the querystring we’re using :query and setting up key/value pairs.  This equates to ?session_date=21NOV11. Our list_session_byday method pulls out the session_date parameter which we then use to query the Session model (database) to grab the sessions for that particular day

    def list_sessions_byday
      @session_date =  @params['session_date']
      @sessions = Session.find(:all,:conditions => {'session_date' => @session_date},:order => 'session_date')
      render
    end

Then we render the list_sessions_byday.erb view file, where we display all of the sessions for that particular day.  Be aware, the syntax for the find seems a bit goofy, since you’re telling it – find all of the records, and then applying the condition.  You’d think you would leave off the :all portion, and just say find records with this condition.

Without boring you with even more code, the list_sessions_byday.erb file spins through the sessions and lists the session.session_title and some other bits (See screenies below) and creates a link like this for each session -

<a href ="<%= url_for :action => :list_details, :query => {:session_id => session.session_id} %>">

This is passing the session_id off to list_details method

    def list_details
      @session_id = @params['session_id']
      @sessions = Session.find(:all,:conditions => {'session_id' => @session_id})
      render
    end

This again grabs that parameter from the query string, does a quick find against the Session model where session_id = @session_id and calls the view, which like the other views displays the session information, although this time we display everything we have about a session in a nice detail page.

Next up – I’ll either tackle the “By Track” functionality or Exhibitor information.  Only time will tell :)

Till then -keep coding and enjow the screenies below.

 


 

Code it right
theCodeDog
theCodeDog

posted in Mobile Connection, PABUG, rhomobile by Jonathan

Follow comments via the RSS Feed | Leave a comment | Trackback URL

1 Comment to "PABUG (Pt3) – Mobile Connection 1.1"

  1. Mark wrote:

    Thanks for this great article. Can I get the code? It’s for learning purposes only. Thanks.

Leave Your Comment

 
Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org