Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

embryonic you-eye

  • Loading branch information...
commit 10880e640c651c0f5b421103a6e7131db246756a 1 parent 66999f8
Jeremy Ashkenas authored August 30, 2009
2  .gitignore
... ...
@@ -1,6 +1,6 @@
1 1
 .autotest
2 2
 log
3  
-hidden
  3
+raws
4 4
 tmp
5 5
 db/*.sqlite3
6 6
 raw
7  cloud-crowd.gemspec
@@ -50,6 +50,7 @@ lib/cloud_crowd/app.rb
50 50
 lib/cloud_crowd/asset_store.rb
51 51
 lib/cloud_crowd/command_line.rb
52 52
 lib/cloud_crowd/daemon.rb
  53
+lib/cloud_crowd/exceptions.rb
53 54
 lib/cloud_crowd/helpers/authorization.rb
54 55
 lib/cloud_crowd/helpers/resources.rb
55 56
 lib/cloud_crowd/helpers.rb
@@ -60,6 +61,12 @@ lib/cloud_crowd/models.rb
60 61
 lib/cloud_crowd/runner.rb
61 62
 lib/cloud_crowd/schema.rb
62 63
 lib/cloud_crowd/worker.rb
  64
+public/css/admin_console.css
  65
+public/css/reset.css
  66
+public/images/queue_fill.png
  67
+public/js/admin_console.js
  68
+public/js/jquery-1.3.2.js
  69
+views/index.erb
63 70
 test/acceptance/test_failing_work_units.rb
64 71
 test/blueprints.rb
65 72
 test/config/config.yml
36  lib/cloud_crowd/app.rb
@@ -15,28 +15,46 @@ class App < Sinatra::Default
15 15
       login_required if CloudCrowd.config[:use_http_authentication]
16 16
     end
17 17
     
  18
+    # Render the admin console.
  19
+    get '/' do
  20
+      erb :index
  21
+    end
  22
+    
  23
+    # Get the JSON for every active job in the queue.
  24
+    get '/jobs' do
  25
+      json Job.all
  26
+    end
  27
+    
  28
+    # To monitor the central server with Monit, God, Nagios, or another 
  29
+    # monitoring tool, you can hit /heartbeat to make sure.
  30
+    get '/heartbeat' do
  31
+      "buh-bump"
  32
+    end
  33
+    
  34
+    # PUBLIC API:
  35
+    
18 36
     # Start a new job. Accepts a JSON representation of the job-to-be.
19 37
     post '/jobs' do
20  
-      Job.create_from_request(JSON.parse(params[:json])).to_json
  38
+      json Job.create_from_request(JSON.parse(params[:json]))
21 39
     end
22 40
     
23 41
     # Check the status of a job, returning the output if finished, and the
24 42
     # number of work units remaining otherwise. 
25 43
     get '/jobs/:job_id' do
26  
-      current_job.to_json
  44
+      json current_job
27 45
     end
28 46
     
29 47
     # Cleans up a Job's saved S3 files. Delete a Job after you're done 
30 48
     # downloading the results.
31 49
     delete '/jobs/:job_id' do
32 50
       current_job.cleanup
33  
-      ''
  51
+      json nil
34 52
     end
35 53
     
36 54
     # Internal method for worker daemons to fetch the work unit at the front
37 55
     # of the queue. Work unit is marked as taken and handed off to the worker.
38 56
     post '/work' do
39  
-      dequeue_work_unit
  57
+      json dequeue_work_unit
40 58
     end
41 59
     
42 60
     # When workers are done with their unit, either successfully on in failure,
@@ -47,22 +65,16 @@ class App < Sinatra::Default
47 65
         case params[:status]
48 66
         when 'succeeded'
49 67
           current_work_unit.finish(params[:output], params[:time])
50  
-          dequeue_work_unit
  68
+          json dequeue_work_unit
51 69
         when 'failed'
52 70
           current_work_unit.fail(params[:output], params[:time])
53  
-          dequeue_work_unit(1)
  71
+          json dequeue_work_unit(1)
54 72
         else             
55 73
           error(500, "Completing a work unit must specify status.")
56 74
         end
57 75
       end
58 76
     end
59 77
     
60  
-    # To monitor the central server with Monit, God, Nagios, or another 
61  
-    # monitoring tool, you can hit /heartbeat to make sure.
62  
-    get '/heartbeat' do
63  
-      "buh-bump"
64  
-    end
65  
-    
66 78
   end
67 79
   
68 80
 end
1  lib/cloud_crowd/command_line.rb
@@ -144,7 +144,6 @@ def ensure_config
144 144
     end
145 145
     
146 146
     # Parse all options for all actions.
147  
-    # TODO: Think about parsing options per sub-command separately.
148 147
     def parse_options
149 148
       @options = {
150 149
         :port         => 9173,
7  lib/cloud_crowd/exceptions.rb
... ...
@@ -0,0 +1,7 @@
  1
+module CloudCrowd
  2
+  
  3
+  class Error < RuntimeError;       end;
  4
+  class ActionNotFound < Error;     end;
  5
+  class StatusUnspecified < Error;  end;
  6
+  
  7
+end
11  lib/cloud_crowd/helpers/resources.rb
@@ -2,6 +2,13 @@ module CloudCrowd
2 2
   module Helpers
3 3
     module Resources
4 4
       
  5
+      # Convenience for responding with JSON.
  6
+      def json(obj)
  7
+        content_type :json
  8
+        return status(204) && '{}' unless obj
  9
+        obj.to_json
  10
+      end
  11
+      
5 12
       def current_job
6 13
         @job ||= Job.find_by_id(params[:job_id]) or raise Sinatra::NotFound
7 14
       end
@@ -15,9 +22,7 @@ def current_work_unit
15 22
       def dequeue_work_unit(offset=0)
16 23
         handle_conflicts do
17 24
           actions = params[:enabled_actions].split(',')
18  
-          unit = WorkUnit.dequeue(actions, offset)
19  
-          return status(204) && '' unless unit
20  
-          unit.to_json
  25
+          WorkUnit.dequeue(actions, offset)
21 26
         end
22 27
       end
23 28
       
21  lib/cloud_crowd/models/job.rb
@@ -99,9 +99,11 @@ def mergeable?
99 99
       self.processing? && self.action_class.public_instance_methods.include?('merge')
100 100
     end
101 101
     
102  
-    # Retrieve the class for this Job's Action, loading it if necessary.
  102
+    # Retrieve the class for this Job's Action.
103 103
     def action_class
104  
-      CloudCrowd.actions[self.action]
  104
+      klass = CloudCrowd.actions[self.action]
  105
+      return klass if klass
  106
+      raise ActionNotFound, "no action named: '#{self.action}' could be found"
105 107
     end
106 108
     
107 109
     # When the WorkUnits are all finished, gather all their outputs together
@@ -124,6 +126,12 @@ def percent_complete
124 126
       return 99  if merging?
125 127
       (work_units.complete.count / work_units.count.to_f * 100).round
126 128
     end
  129
+    
  130
+    # How long has this Job taken?
  131
+    def time_taken
  132
+      return self.time if self.time
  133
+      Time.now - self.created_at
  134
+    end
127 135
         
128 136
     # When starting a new job, or moving to a new stage, split up the inputs 
129 137
     # into WorkUnits, and queue them.
@@ -141,9 +149,14 @@ def queue_for_workers(input)
141 149
     # A JSON representation of this job includes the statuses of its component
142 150
     # WorkUnits, as well as any completed outputs.
143 151
     def to_json(opts={})
144  
-      atts = {'id' => self.id, 'status' => self.display_status, 'percent_complete' => self.percent_complete}
  152
+      atts = {
  153
+        'id'                => self.id, 
  154
+        'status'            => self.display_status, 
  155
+        'percent_complete'  => self.percent_complete,
  156
+        'work_units'        => self.work_units.count,
  157
+        'time_taken'        => self.time_taken
  158
+      }
145 159
       atts.merge!({'outputs' => JSON.parse(self.outputs)}) if self.outputs
146  
-      atts.merge!({'time' => self.time}) if self.time
147 160
       atts.to_json
148 161
     end
149 162
     
2  lib/cloud_crowd/worker.rb
@@ -76,7 +76,7 @@ def run_work_unit
76 76
         when PROCESSING then @action.process
77 77
         when SPLITTING  then @action.split
78 78
         when MERGING    then @action.merge
79  
-        else raise "Work units must specify their status."
  79
+        else raise StatusUnspecified, "work units must specify their status"
80 80
         end
81 81
         complete_work_unit({'output' => result}.to_json)
82 82
       rescue Exception => e
35  public/css/admin_console.css
... ...
@@ -0,0 +1,35 @@
  1
+body {
  2
+  background: #c7c7c7;
  3
+  font-family: Arial;
  4
+  font-size: 12px;
  5
+}
  6
+
  7
+#queue {
  8
+  position: absolute;
  9
+  top: 100px; left: 100px; right: 100px;
  10
+  height: 77px;
  11
+}
  12
+  #queue_fill {
  13
+    position: absolute;
  14
+    width: 100%; height: 75px;
  15
+    border: 1px solid #5c5c5c;
  16
+    -moz-border-radius: 10px; -webkit-border-radius: 10px;
  17
+    background: transparent url(/images/queue_fill.png) repeat-x 0px -1px;
  18
+  }
  19
+  #queue .job {
  20
+    position: relative;
  21
+    margin-top: 1px;
  22
+    height: 75px;
  23
+    background: blue;
  24
+    float: left;
  25
+    overflow: hidden;
  26
+    -moz-border-radius: 10px;
  27
+    -webkit-border-radius: 10px;
  28
+  }
  29
+    #queue .job_id {
  30
+      color: #c7c7c7;
  31
+      font-size: 14px;
  32
+      position: absolute;
  33
+      bottom: 8px; left: 8px;
  34
+      z-index: 10;
  35
+    }
52  public/css/reset.css
... ...
@@ -0,0 +1,52 @@
  1
+/*------------------------------ RESET + DEFAULT STYLES ---------------------------------*/
  2
+
  3
+/* 
  4
+Eric Meyer's final reset.css
  5
+Source: http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/ 
  6
+*/
  7
+html, body, div, span, applet, object, iframe,
  8
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  9
+a, abbr, acronym, address, big, cite, code,
  10
+del, dfn, em, font, img, ins, kbd, q, s, samp,
  11
+small, strike, strong, sub, sup, tt, var,
  12
+dl, dt, dd, ol, ul, li,
  13
+fieldset, form, label, legend,
  14
+table, caption, tbody, tfoot, thead, tr, th, td {
  15
+	margin: 0;
  16
+	padding: 0;
  17
+	border: 0;
  18
+	outline: 0;
  19
+	font-weight: inherit;
  20
+	font-style: inherit;
  21
+	font-size: 100%;
  22
+	font-family: inherit;
  23
+	vertical-align: baseline;
  24
+}
  25
+/* remember to define focus styles! */
  26
+:focus {
  27
+	outline: 0;
  28
+}
  29
+body {
  30
+	line-height: 1;
  31
+	color: black;
  32
+	background: white;
  33
+}
  34
+ol, ul {
  35
+	list-style: none;
  36
+}
  37
+/* tables still need 'cellspacing="0"' in the markup */
  38
+table {
  39
+	border-collapse: separate;
  40
+	border-spacing: 0;
  41
+}
  42
+caption, th, td {
  43
+	text-align: left;
  44
+	font-weight: normal;
  45
+}
  46
+blockquote:before, blockquote:after,
  47
+q:before, q:after {
  48
+	content: "";
  49
+}
  50
+blockquote, q {
  51
+	quotes: "" "";
  52
+}
BIN  public/images/queue_fill.png
34  public/js/admin_console.js
... ...
@@ -0,0 +1,34 @@
  1
+window.Console = {
  2
+  
  3
+  initialize : function() {
  4
+    this._queue = $('#jobs');
  5
+    this.getJobs();
  6
+  },
  7
+  
  8
+  getJobs : function() {
  9
+    $.get('/jobs', null, function(resp) {
  10
+      Console._jobs = resp;
  11
+      Console.renderJobs();
  12
+    }, 'json');
  13
+  },
  14
+  
  15
+  renderJobs : function() {
  16
+    var queue = this._queue;
  17
+    var totalUnits = 0;
  18
+    var totalWidth = queue.width();
  19
+    $.each(this._jobs, function() { 
  20
+      totalUnits += this.work_units; 
  21
+    });
  22
+    $.each(this._jobs, function() {
  23
+      this.width  = (this.work_units / totalUnits) * 100;
  24
+      this.color  = [Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255)];
  25
+    });
  26
+    queue.html('');
  27
+    $.each(this._jobs.reverse(), function() {
  28
+      queue.append('<div class="job" style="width:' + this.width + '%; background:rgb(' + this.color.join(',') + ');"><div class="job_id">#' + this.id + '</div></div>');
  29
+    });
  30
+  }
  31
+  
  32
+};
  33
+
  34
+$(document).ready(function() { Console.initialize(); });
4,376  public/js/jquery-1.3.2.js
4376 additions, 0 deletions not shown
22  views/index.erb
... ...
@@ -0,0 +1,22 @@
  1
+<!DOCTYPE html>
  2
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  3
+<head>
  4
+	<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  5
+	<title>Operations Center | CloudCrowd</title>
  6
+	<link href="/css/reset.css" media="screen" rel="stylesheet" type="text/css" />
  7
+	<link href="/css/admin_console.css" media="screen" rel="stylesheet" type="text/css" />
  8
+	<script src="/js/jquery-1.3.2.js" type="text/javascript"></script>
  9
+	<script src="/js/admin_console.js" type="text/javascript"></script>
  10
+</head>
  11
+
  12
+<body>
  13
+	<div id="container">
  14
+		<div id="queue">
  15
+			<div id="jobs">
  16
+				<%# Render target for jobs. %>
  17
+			</div>
  18
+			<div id="queue_fill"></div>
  19
+		</div>
  20
+	</div>
  21
+</body>
  22
+</html>

0 notes on commit 10880e6

Please sign in to comment.
Something went wrong with that request. Please try again.