add tutorials and references to home page
authorTom Clegg <tom@clinicalfuture.com>
Thu, 13 Jun 2013 19:30:55 +0000 (15:30 -0400)
committerTom Clegg <tom@clinicalfuture.com>
Thu, 13 Jun 2013 20:54:50 +0000 (16:54 -0400)
17 files changed:
apps/workbench/Gemfile
apps/workbench/Gemfile.lock
apps/workbench/app/controllers/application_controller.rb
apps/workbench/app/controllers/users_controller.rb
apps/workbench/app/helpers/application_helper.rb
apps/workbench/app/models/arvados_base.rb
apps/workbench/app/models/arvados_resource_list.rb
apps/workbench/app/models/user.rb
apps/workbench/app/views/application/_arvados_object.html.erb
apps/workbench/app/views/application/show.html.erb
apps/workbench/app/views/layouts/application.html.erb
apps/workbench/app/views/users/home.html.erb
doc/install/index.md
services/api/app/controllers/arvados/v1/users_controller.rb
services/api/app/helpers/application_helper.rb
services/api/config/routes.rb
services/api/lib/current_api_client.rb

index 1a516a5d71382afe703bf6ff8cace9d292b142ff..dde019e7d98cbb40f2fb8dfb9342b71da2b0bd89 100644 (file)
@@ -50,3 +50,4 @@ gem 'rvm-capistrano', :group => :test
 
 gem 'passenger', :group => :production
 gem 'andand'
+gem 'RedCloth'
index a52b11956b3b7dc5d9e8c8ae34dc4a33a1e87e8c..f1ec82a8558c13ed3099679ca7e4ccff07ac0fb7 100644 (file)
@@ -1,6 +1,7 @@
 GEM
   remote: https://rubygems.org/
   specs:
+    RedCloth (4.2.9)
     actionmailer (3.2.11)
       actionpack (= 3.2.11)
       mail (~> 2.4.4)
@@ -151,6 +152,7 @@ PLATFORMS
   ruby
 
 DEPENDENCIES
+  RedCloth
   andand
   anjlab-bootstrap-rails (>= 2.2)
   bootstrap-editable-rails
index 1cef3d947d6e2214761ecbd89709913f0e617de6..3cae2dcbd7021a164da41f583049584be632939d 100644 (file)
@@ -66,6 +66,12 @@ class ApplicationController < ActionController::Base
     end
   end
 
+  def render_content
+    if !@object
+      return render_not_found("object not found")
+    end
+  end
+
   def new
     @object = model_class.new
   end
index ef527a5591c45e1d1cc6abedc7754accf22e4af2..62007ff1c709cf697b40c5b630cc011e7f96697f 100644 (file)
@@ -10,8 +10,29 @@ class UsersController < ApplicationController
   end
 
   def home
-    @my_ssh_keys = AuthorizedKey.where(authorized_user: current_user.uuid)
+    @my_ssh_keys = AuthorizedKey.where(authorized_user_uuid: current_user.uuid)
     @my_vm_perms = Link.where(tail_uuid: current_user.uuid, head_kind: 'arvados#virtual_machine', link_class: 'permission', name: 'can_login')
     @my_repo_perms = Link.where(tail_uuid: current_user.uuid, head_kind: 'arvados#repository', link_class: 'permission', name: 'can_write')
+    @my_last_job = Job.
+      limit(1).
+      order(:created_at).
+      where(created_by: current_user.uuid).
+      last
+
+    # A Tutorial is a Link which has link_class "resources" and name
+    # "wants", and is owned by the Tutorials Group (i.e., named
+    # "Arvados Tutorials" and owned by the system user).
+    @tutorial_group = Group.where(owner: User.system.uuid,
+                                  name: 'Arvados Tutorials').first
+    if @tutorial_group
+      @tutorial_links = Link.where(tail_uuid: @tutorial_group.uuid,
+                                   link_class: 'resources',
+                                   name: 'wants')
+    else
+      @tutorial_links = []
+    end
+    @tutorial_complete = {
+      'Run a job' => @my_last_job
+    }
   end
 end
index 916e4b20db5f53549c950236426255471d943ccc..78504655a8e28d04321b017e2bddb4034e7fe689 100644 (file)
@@ -2,6 +2,11 @@ module ApplicationHelper
   def current_user
     controller.current_user
   end
+
+  def render_content_from_database(markup)
+    raw RedCloth.new(markup).to_html
+  end
+
   def human_readable_bytes_html(n)
     return h(n) unless n.is_a? Fixnum
     raw = n.to_s
index 267a4a9133ef1c9b5c05372488faa349d0ae332e..2aa91f735eb67e3299b09c055531d231e569f82c 100644 (file)
@@ -73,6 +73,9 @@ class ArvadosBase < ActiveRecord::Base
     end
     new.private_reload(uuid)
   end
+  def self.order(*args)
+    ArvadosResourceList.new(self).order(*args)
+  end
   def self.where(*args)
     ArvadosResourceList.new(self).where(*args)
   end
index c8e6de6302863dd8082cdeee7fd8882f46aad527..b1c15617878d5f15fb796db3767d7cb02c6bf1d8 100644 (file)
@@ -15,6 +15,11 @@ class ArvadosResourceList
     self
   end
 
+  def order(orderby_spec)
+    @orderby_spec = orderby_spec
+    self
+  end
+
   def where(cond)
     cond = cond.dup
     cond.keys.each do |uuid_key|
@@ -46,6 +51,7 @@ class ArvadosResourceList
     }
     api_params[:eager] = '1' if @eager
     api_params[:limit] = @limit if @limit
+    api_params[:order] = @orderby_spec if @orderby_spec
     res = $arvados_api_client.api @resource_class, '', api_params
     @results = $arvados_api_client.unpack_api_response res
     self
index 0dec2e0ba87936abc7c2550b2f941233a809d50b..47b0af38a87d3c315051efe138fed1831174e8cb 100644 (file)
@@ -10,6 +10,13 @@ class User < ArvadosBase
     $arvados_api_client.unpack_api_response(res)
   end
 
+  def self.system
+    $arvados_system_user ||= begin
+                               res = $arvados_api_client.api self, '/system'
+                               $arvados_api_client.unpack_api_response(res)
+                             end
+  end
+
   def full_name
     (self.first_name || "") + " " + (self.last_name || "")
   end
index 4f7570ea015d1aa3120c1082b837e32b4ce914b5..34f20b502f7c1e0fd74807f89f08f07d8f044918 100644 (file)
@@ -1,3 +1,4 @@
+<% content_for :arvados_object_table do %>
 <%= form_for @object do |f| %>
 <table class="table topalign">
   <thead>
   </tbody>
 </table>
 <% end %>
+<% end %>
+
+<% if content_for? :page_content %>
+<%= yield :page_content %>
+<% else %>
+<%= yield :arvados_object_table %>
+<% end %>
 
 <div>
   <ul class="nav nav-tabs">
+    <% if content_for? :page_content %>
+    <li><a href="#arvados-object-table" data-toggle="tab">Table</a></li>
+    <% end %>
     <li class="active"><a href="#arvados-object-json" data-toggle="tab">JSON</a></li>
     <% if @object.andand.uuid %>
     <li><a href="#arvados-object-curl" data-toggle="tab">CLI update</a></li>
     <% end %>
   </ul>
   <div class="tab-content">
+    <% if content_for? :page_content %>
+    <div id="arvados-object-table" class="tab-pane fade">
+      <%= yield :arvados_object_table %>
+    </div>
+    <% end %>
     <div id="arvados-object-json" class="tab-pane fade in active">
       <pre>
 <%= JSON.pretty_generate(@object.attributes.reject { |k,v| k == 'id' }) rescue nil %>
index 90790856ae17f155e1b9ffb9e0492951e732c3e6..ad7e53c1fe1d1c132113c02211a155e3bfbf3aca 100644 (file)
@@ -1 +1,25 @@
+<% if @object.respond_to? :properties %>
+
+<% content_for :page_title do %>
+<%= @object.properties[:page_title] || @object.uuid %>
+<% end %>
+
+<% if @object.properties[:page_content] %>
+<% content_for :page_content do %>
+<h1>
+<%= render_content_from_database(@object.properties[:page_title] || @object.uuid) %>
+</h1>
+
+<% if @object.properties[:page_subtitle] %>
+<h4>
+<%= render_content_from_database @object.properties[:page_subtitle] %>
+</h4>
+<% end %>
+
+<%= render_content_from_database @object.properties[:page_content] %>
+<% end %>
+<% end %>
+<% end %>
+
+
 <%= render :partial => 'application/arvados_object' %>
index 747f1d6fb256a41156fd9d4936fd49b8b206ee44..56ac2cbacb4dee8f31c7c3bb77772ad25a155a61 100644 (file)
@@ -2,7 +2,13 @@
 <html>
 <head>
   <meta charset="utf-8">
-  <title><%= Rails.configuration.site_name rescue Rails.application.class.parent_name %></title>
+  <title>
+    <% if content_for? :page_title %>
+    <%= yield :page_title %>
+    <% else %>
+    <%= Rails.configuration.site_name rescue Rails.application.class.parent_name %>
+    <% end %>
+  </title>
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta name="description" content="">
   <meta name="author" content="">
index 2a69b0680441e01f895776ceeac74b1a00a338ff..6f52694070f64b4fd577544f33bb3db13998d774 100644 (file)
@@ -1,7 +1,56 @@
+<% if @tutorial_links.any? %>
+<% content_for :tutorials do %>
+<h4>Tutorials</h4>
+<% @tutorial_links.each do |tut| %>
+<div class="well clearfix">
+  <div>
+    <strong><%= tut.properties[:page_title] %></strong>
+    <% if @tutorial_complete[tut.properties[:page_title]] %>
+    <span class="badge badge-success pull-right">&#x2714;</span>
+    <% else %>
+    <span class="badge pull-right">&#x270d;</span>
+    <% end %>
+  </div>
+  <%= render_content_from_database tut.properties[:page_subtitle] %>
+  <br />
+  <%= link_to raw('Tutorial: ' + tut.properties[:page_title] + ' &#x279c;'), link_path(tut.uuid), class: "pull-right btn btn-primary" %>
+</div>
+<% end %>
+<% end %>
+<% end %>
+
+<% content_for :references do %>
+<h4>References</h4>
+<div class="well clearfix">
+  <div>
+    <strong>API Reference</strong>
+  </div>
+  API calls for running jobs, building pipelines, and developing apps.
+  <br />
+  <%= link_to raw('API Reference &#x279c;'), 'http://doc.arvados.org/api/', class: "pull-right btn" %>
+</div>
+<div class="well clearfix">
+  <div>
+    <strong>User Guide</strong>
+  </div>
+  Getting acquainted with the Arvados platform.
+  <br />
+  <%= link_to raw('User Guide &#x279c;'), 'http://doc.arvados.org/user/', class: "pull-right btn" %>
+</div>
+<div class="well clearfix">
+  <div>
+    <strong>arvados.org</strong>
+  </div>
+  Project home page: developer docs, source code, background.
+  <br />
+  <%= link_to raw('arvados.org &#x279c;'), 'https://arvados.org/', class: "pull-right btn" %>
+</div>
+<% end %>
+
 <div class="row-fluid">
   <div class="col span4">
     <h4>Setup</h4>
-    <div class="well">
+    <div class="well clearfix">
       <div>
         <strong>SSH keys</strong>
         <span class="badge <%= 'badge-success' if @my_ssh_keys.any? %> pull-right"><%= @my_ssh_keys.count %></span>
@@ -9,9 +58,9 @@
       You&rsquo;ll use public key authentication when logging in to a
       VM or use a hosted git repository.
       <br />
-      <%= link_to raw('Add/edit keys &#x279c;'), authorized_keys_path, class: "btn #{'btn-primary' if @my_ssh_keys.empty?}" %>
+      <%= link_to raw('Add/edit keys &#x279c;'), authorized_keys_path, class: "pull-right btn #{'btn-primary' if @my_ssh_keys.empty?}" %>
     </div>
-    <div class="well">
+    <div class="well clearfix">
       <div>
         <strong>Virtual machines</strong>
         <span class="badge <%= 'badge-success' if @my_vm_perms.any? %> pull-right"><%= @my_vm_perms.collect(&:head_uuid).uniq.count %></span>
       Arvados includes virtual machines with SDKs installed and ready to use.
       <br />
       <% if @my_vm_perms.any? %>
-      <%= link_to raw('Show VMs &#x279c;'), virtual_machines_path, class: "btn" %>
+      <%= link_to raw('Show VMs &#x279c;'), virtual_machines_path, class: "pull-right btn" %>
       <% elsif @my_ssh_keys.any? %>
-      <%= link_to raw('Request a VM &#x279c;'), virtual_machines_path, class: "btn btn-primary" %>
+      <%= link_to raw('Request a VM &#x279c;'), virtual_machines_path, class: "pull-right btn btn-primary" %>
       <% else %>
-      <%= link_to raw('Request a VM &#x279c;'), virtual_machines_path, { :class => "btn disabled", :"data-toggle" => "tooltip", :"data-placement" => "bottom", :title => "Add an SSH public key first!" } %>
+      <%= link_to raw('Request a VM &#x279c;'), virtual_machines_path, { :class => "pull-right btn disabled", :"data-toggle" => "tooltip", :"data-placement" => "bottom", :title => "Add an SSH public key first!" } %>
       <% end %>
     </div>
-    <div class="well">
+    <div class="well clearfix">
       <div>
         <strong>Git repositories</strong>
         <span class="badge <%= 'badge-success' if @my_repo_perms.any? %> pull-right"><%= @my_repo_perms.collect(&:head_uuid).uniq.count %></span>
       In order to run jobs using your own code, you need to push your code to a git repository. We provide hosted git repositories to make this easy.
       <br />
       <% if @my_repo_perms.any? %>
-      <%= link_to raw('Show repositories &#x279c;'), repositories_path, class: "btn" %>
+      <%= link_to raw('Show repositories &#x279c;'), repositories_path, class: "pull-right btn" %>
       <% elsif @my_ssh_keys.any? %>
-      <%= link_to raw('Request a repository &#x279c;'), repositories_path, class: "btn btn-primary" %>
+      <%= link_to raw('Request a repository &#x279c;'), repositories_path, class: "pull-right btn btn-primary" %>
       <% else %>
-      <%= link_to raw('Request a repository &#x279c;'), repositories_path, { :class => "btn disabled", :"data-toggle" => "tooltip", :"data-placement" => "bottom", :title => "Add an SSH public key first!" } %>
+      <%= link_to raw('Request a repository &#x279c;'), repositories_path, { :class => "pull-right btn disabled", :"data-toggle" => "tooltip", :"data-placement" => "bottom", :title => "Add an SSH public key first!" } %>
       <% end %>
     </div>
   </div>
+
+  <% if content_for? :tutorials %>
+  <div class="col span4">
+    <%= yield :tutorials %>
+  </div>
+  <% end %>
+
+  <div class="col span4">
+    <%= yield :references %>
+  </div>
 </div>
index ea7dca8a1e1298e8399857cb7dd37019d331ff62..95edd74d735588804d4335bfbca1b066081a95d5 100644 (file)
@@ -14,3 +14,4 @@ navorder: 0
 1. [Install the Arvados REST API server](install-api-server.html)
 1. [Install the Arvados workbench application](install-workbench-app.html)
 1. Set up the Job manager
+1. Create a Group named "Arvados Tutorials", owned by the system user. Create Links (link_class "resources", name "wants") from the tutorials group to sample data collections. Edit <code>page_content</code>, <code>page_title</code>, and <code>page_subtitle</code> properties to suit. These will be listed in the Tutorials section of your users' home pages.
index b6266f7047d0fcbf560ab4ea3653cafb529cf715..d8f6a98f422382593b0417fe55f80b6bd93a11e0 100644 (file)
@@ -3,4 +3,8 @@ class Arvados::V1::UsersController < ApplicationController
     @object = current_user
     show
   end
+  def system
+    @object = system_user
+    show
+  end
 end
index 385477562dd948fb4dbc4761ea79f03a4438f6e2..c5999b3714e08873ebc2e348355250b846bd794d 100644 (file)
@@ -1,25 +1,3 @@
 module ApplicationHelper
-  def current_user
-    controller.current_user
-  end
-
-  def act_as_system_user
-    if not $system_user
-      Thread.current[:user] = User.new(is_admin: true)
-      sysuser_id = [Server::Application.config.uuid_prefix,
-                    User.uuid_prefix,
-                    '000000000000000'].join('-')
-      $system_user = User.where('uuid=?', sysuser_id).first
-      if !$system_user
-        $system_user = User.new(uuid: sysuser_id,
-                                is_admin: true,
-                                email: 'root',
-                                first_name: 'root',
-                                last_name: '')
-        $system_user.save!
-        $system_user.reload
-      end
-    end
-    Thread.current[:user] = $system_user
-  end
+  include CurrentApiClient
 end
index 4ab7b60fd6d8bf190b5bc162c28d0fe234c5b83c..398645462a4b78a233ad921d910f12da02a60bfb 100644 (file)
@@ -82,6 +82,7 @@ Server::Application.routes.draw do
       match '/keep_disks/ping' => 'keep_disks#ping', :as => :ping_keep_disk
       match '/links/from/:tail_uuid' => 'links#index', :as => :arvados_v1_links_from
       match '/users/current' => 'users#current'
+      match '/users/system' => 'users#system'
       match '/jobs/queue' => 'jobs#queue'
       match '/virtual_machines/get_all_logins' => 'virtual_machines#get_all_logins'
       match '/repositories/get_all_permissions' => 'repositories#get_all_permissions'
index 24d8b3ada996e77820b00a7241930c39e918e085..ab6d624ea5ea3899ca4a4f8acaabb7e39badd819 100644 (file)
@@ -30,4 +30,33 @@ module CurrentApiClient
   def current_api_client_trusted
     Thread.current[:api_client_trusted]
   end
+
+  def system_user_uuid
+    [Server::Application.config.uuid_prefix,
+     User.uuid_prefix,
+     '000000000000000'].join('-')
+  end
+
+  def system_user
+    if not $system_user
+      real_current_user = Thread.current[:user]
+      Thread.current[:user] = User.new(is_admin: true)
+      $system_user = User.where('uuid=?', system_user_uuid).first
+      if !$system_user
+        $system_user = User.new(uuid: system_user_uuid,
+                                is_admin: true,
+                                email: 'root',
+                                first_name: 'root',
+                                last_name: '')
+        $system_user.save!
+        $system_user.reload
+      end
+      Thread.current[:user] = real_current_user
+    end
+    $system_user
+  end
+
+  def act_as_system_user
+    Thread.current[:user] = system_user
+  end
 end