From 9cf5756a9a15ea38f044641b36b01de13cf6599f Mon Sep 17 00:00:00 2001 From: David Schoonover Date: Mon, 18 Jun 2012 16:00:32 -0700 Subject: [PATCH] Adds Dashboard controller. --- data/dashboards/reportcard.json | 18 +++++++ lib/dashboard/dashboard-model.co | 7 +-- lib/server/controllers/dashboard.co | 91 +++++++++++++++++++++++++++++++++ lib/server/file-controller.co | 95 +++++++++++++++++++++++++++++++++++ lib/server/server.co | 1 + www/modules.yaml | 3 + 6 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 data/dashboards/reportcard.json create mode 100644 lib/server/file-controller.co diff --git a/data/dashboards/reportcard.json b/data/dashboards/reportcard.json new file mode 100644 index 0000000..cdb0f21 --- /dev/null +++ b/data/dashboards/reportcard.json @@ -0,0 +1,18 @@ +{ + "name": "Reportcard", + "graphs": [ + "unique_visitors", + "reach", + "pageviews", + "pageviews_mobile", + "pageviews_mobile_target", + "commons", + "articles", + "articles_per_day", + "edits", + "new_editors", + "active_editors", + "active_editors_target", + "very_active_editors" + ] +} diff --git a/lib/dashboard/dashboard-model.co b/lib/dashboard/dashboard-model.co index 35b1d2f..4ae98dc 100644 --- a/lib/dashboard/dashboard-model.co +++ b/lib/dashboard/dashboard-model.co @@ -11,19 +11,18 @@ */ Dashboard = exports.Dashboard = BaseModel.extend do # {{{ urlRoot : '/dashboards' - graphs : [] constructor: function Dashboard BaseModel ... initialize: -> - @graphs = [] BaseModel::initialize ... - + defaults: -> - {} + name : null + graphs : [] # }}} diff --git a/lib/server/controllers/dashboard.co b/lib/server/controllers/dashboard.co index e69de29..ca6c844 100644 --- a/lib/server/controllers/dashboard.co +++ b/lib/server/controllers/dashboard.co @@ -0,0 +1,91 @@ +fs = require 'fs' +path = require 'path' +{existsSync:exists} = path + +_ = require 'underscore' +Seq = require 'seq' +yaml = require 'js-yaml' +{mkdirp, mkdirpAsync} = require '../mkdirp' + +{ readJSONFilesAsync, +} = require '../files' + +FileBackedController = require '../file-controller' + + +/** + * @class Resource controller for dashboard requests. + */ +class DashboardController extends FileBackedController + PROTECTED_IDS : <[ + main + reportcard + ]> + PROTECT : true + + name : 'dashboards' + dataDir : 'data/dashboards' + -> super ... + + + + # GET /dashboards.:format? + index: (req, res) -> + # if format is json, then return the dashboard JSON + switch req.format + case 'json' + pattern = @dataDir + "/*.json" + Seq() + # find list of data/dashboard/*.json files, send back the objects + .seq -> readJSONFilesAsync pattern, this + # remove filepath info + .seq (files) -> res.send _.values files + default + res.render "#{@id}/index" + + # GET /dashboards/:dashboard + show: (req, res) -> + if req.format is 'json' + res.send req.dashboard + else + res.render "#{@id}/view" + + # GET /dashboards/:dashboard/edit + edit: (req, res) -> + if req.format is 'json' + res.send req.dashboard + else + res.render "#{@id}/edit" + + # GET /dashboards/new + new: (req, res) -> + res.render "#{@id}/edit" + + # POST /dashboards + create: (req, res) -> + return unless data = @processBody req, res + file = @toFile data.id + if exists file + return res.send { result:"error", message:"Dashboard '#{data.id}' already exists!" } + else + fs.writeFile file, JSON.stringify(data), "utf8", @errorHandler(res, "Error writing Dashboard!") + + # PUT /dashboards/:dashboard + update: (req, res) -> + return unless data = @processBody req, res + if @PROTECT and _ @PROTECTED_IDS .contains data.id + return res.send {result:"error", message:"Dashboard '#{data.id}' is read-only."}, 403 + fs.writeFile @toFile(data.id), JSON.stringify(data), "utf8", @errorHandler(res, "Error writing Dashboard!") + + # DELETE /dashboards/:dashboard + destroy: (req, res) -> + id = req.param.dashboard + if @PROTECT and _ @PROTECTED_IDS .contains id + return res.send {result:"error", message:"Dashboard '#{id}' is read-only."}, 403 + fs.unlink @toFile(id), @errorHandler(res, "Dashboard '#{id}' does not exist!") + + + + + +module.exports = exports = DashboardController diff --git a/lib/server/file-controller.co b/lib/server/file-controller.co new file mode 100644 index 0000000..f4d29c7 --- /dev/null +++ b/lib/server/file-controller.co @@ -0,0 +1,95 @@ +fs = require 'fs' +path = require 'path' +{existsSync:exists} = path + +_ = require 'underscore' +Seq = require 'seq' +yaml = require 'js-yaml' +{mkdirp, mkdirpAsync} = require './mkdirp' + +{ readJSONFilesAsync, +} = require './files' + +Controller = require './controller' + + +/** + * @class Resource controller backed by flat json or yaml files. + */ +class FileBackedController extends Controller + + name : null + dataDir : null + + -> + @dataDir or= "data/#{@name}" + super ... + + + + /** + * Override to customize lookup of files by ID. + * + * @param {String} id ID of this resource. + * @returns {String} Path to file for this resource. + */ + toFile: (id) -> "#{@dataDir}/#id.json" + + + /** + * Auto-load :id for related requests. + * + * @param {String} id ID of the resource. + * @param {Function} cb Callback to invoke with the loaded object. + */ + autoload: (id, cb) -> + file = @toFile id + parser = JSON.parse + + yamlFile = file.replace /\.json$/i, '.yaml' + if exists yamlFile + file = yamlFile + parser = yaml.load + + err, data <- fs.readFile file, 'utf8' + + if 'ENOENT' is err?.code + return cb null, {} + if err + console.error "#{this}.autoload(#id, #{typeof cb}) -->\nerr" + return cb err + try + cb null, parser data + catch err + console.error "#{this}.autoload(#id, #{typeof cb}) -->\nerr" + cb err + + ### Helpers + + processBody: (req, res) -> + if not req.body + res.send {result:"error", message:"Data required!"}, 501 + return false + + data = req.body + data.slug or= data.id + data.id or= data.slug + + if not data.slug + res.send {result:"error", message:"Slug required!"}, 501 + return false + + mkdirp @dataDir if not exists @dataDir + return data + + errorHandler: (res, msg) -> + (err) -> + if err + msg or= err.message or String(err) + console.error msg + res.send { result:"error", message:msg }, 501 + else + res.send { result:"ok" } + + +module.exports = exports = FileBackedController diff --git a/lib/server/server.co b/lib/server/server.co index 67bc809..bb01baa 100755 --- a/lib/server/server.co +++ b/lib/server/server.co @@ -145,6 +145,7 @@ app.configure -> Controller = require './controller' app.controller require './controllers/graph' +app.controller require './controllers/dashboard' YAML_EXT_PAT = /\.ya?ml$/i app.get '/datasources/all', (req, res, next) -> diff --git a/www/modules.yaml b/www/modules.yaml index 371a1e2..c11206c 100644 --- a/www/modules.yaml +++ b/www/modules.yaml @@ -65,8 +65,10 @@ dev: - graph-list.jade - util: - underscore: + - function - array - object + - class - kv - string - index @@ -88,6 +90,7 @@ dev: - scaffold-model - scaffold-view - index + - base - base-mixin - base-model - base-view -- 1.7.0.4