├── .gitignore ├── .ruby-version ├── .travis.yml ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── api ├── .env.sample ├── API_DOC.md ├── app │ ├── controllers │ │ ├── Application.scala │ │ ├── Management.scala │ │ └── Swarms.scala │ └── lib │ │ ├── Elasticsearch.scala │ │ └── GeoJsonFormatter.scala ├── conf │ ├── application.conf │ └── routes ├── eb_name.txt └── package.sh ├── bombard ├── Gemfile ├── Gemfile.lock ├── README.md ├── bombard.rb └── postcodes.csv ├── case_studies ├── broadband_survey.md ├── broadband_survey │ ├── add-fields.gif │ ├── clone.gif │ ├── create.gif │ ├── csv.png │ ├── embed-code.gif │ ├── graphs.gif │ ├── login.gif │ ├── move-delete.gif │ ├── open.gif │ ├── permissions.gif │ ├── preview.png │ ├── table.png │ ├── thumb.png │ └── view-form.png ├── indyref.md ├── indyref │ ├── indyref-graph1.png │ ├── indyref-graph2.png │ ├── indyref-graph3.png │ ├── indyref1.gif │ └── thumb.png ├── realtime_clicker.md └── realtime_clicker │ ├── aggregate-field.gif │ ├── api-keys.gif │ ├── get-code.gif │ ├── map-demo.png │ ├── preview-form.png │ ├── results-over-time.png │ └── thumb.jpg ├── cloudformation ├── README.md ├── __example.template ├── all.template ├── base.template ├── bashlib │ └── shared.sh ├── cfn.conf ├── collector.template ├── create_all.sh ├── delete_all.sh ├── dist │ └── elasticsearch.yml ├── elasticsearch.template ├── processor.template ├── update_all.sh └── validate_all.sh ├── collector ├── README.md ├── app │ ├── Global.scala │ ├── controllers │ │ ├── Application.scala │ │ ├── Management.scala │ │ └── Swarms.scala │ └── views │ │ ├── index.scala.html │ │ └── main.scala.html ├── conf │ ├── application.conf │ └── routes ├── eb_name.txt ├── example_curl_live_form.sh ├── example_curl_live_json.sh ├── example_curl_local_form.sh ├── example_curl_local_json.sh ├── package.sh └── public │ ├── images │ └── favicon.png │ └── stylesheets │ └── main.css ├── copy_static_markdown.sh ├── deploy.sh ├── deploy ├── Gemfile ├── Gemfile.lock ├── README.md └── deploy.rb ├── examples ├── README.md ├── generic-backend │ ├── .gitignore │ ├── README.md │ ├── backend.js │ ├── package.json │ └── public │ │ └── index.html ├── livedebate-scotland │ ├── README.md │ ├── ad.jpg │ ├── as.jpg │ ├── cookies.js │ ├── debate.css │ ├── debate.js │ ├── index.html │ └── jquery.min.js ├── livedebate │ ├── .gitignore │ ├── Gemfile │ ├── Gemfile.lock │ ├── README.md │ ├── app.rb │ ├── config.rb │ ├── config.ru │ ├── lib │ │ └── swarm_api.rb │ ├── public │ │ ├── images │ │ │ ├── .gitkeep │ │ │ ├── dc.jpg │ │ │ ├── em.jpg │ │ │ └── nc.jpg │ │ ├── javascripts │ │ │ ├── application.js │ │ │ └── jquery.min.js │ │ └── stylesheets │ │ │ └── application.css │ ├── stylesheets │ │ └── application.scss │ └── views │ │ ├── index.haml │ │ └── layout.haml ├── mapdemo-backend │ ├── .gitignore │ ├── README.md │ ├── backend.js │ ├── package.json │ └── public │ │ └── index.html └── mapdemo-standalone │ ├── README.md │ └── index.html ├── geocoder ├── .gitignore ├── README.md ├── eb_name.txt ├── geocoder.js ├── package.json └── package.sh ├── processor ├── app │ ├── Global.scala │ ├── controllers │ │ ├── Application.scala │ │ ├── Management.scala │ │ └── Swarms.scala │ └── lib │ │ ├── Activities.scala │ │ ├── ActivityDispatcher.scala │ │ ├── Decider.scala │ │ ├── Elasticsearch.scala │ │ ├── ExternalActivity.scala │ │ └── StoreInElasticsearchActivity.scala ├── conf │ ├── application-logger.xml │ ├── application.conf │ └── routes ├── eb_name.txt ├── external_processors.md ├── logs │ └── processor.log ├── package.sh ├── start_prod_processor.sh ├── stop_prod_processor.sh └── test │ ├── ActivityRunOnce.scala │ ├── DeciderRunOnce.scala │ └── resources │ └── logback-test.xml ├── project ├── SwarmizeBuild.scala ├── build.properties └── plugins.sbt ├── sentiment-endpoint ├── .gitignore ├── Procfile ├── package.json └── sentiment-endpoint.js ├── shared-lib └── src │ ├── main │ ├── resources │ │ └── field_types.json │ └── scala │ │ └── swarmize │ │ ├── ClassLogger.scala │ │ ├── FieldTypes.scala │ │ ├── Swarm.scala │ │ ├── SwarmApiKeys.scala │ │ ├── SwarmField.scala │ │ ├── SwarmSubmissionValidator.scala │ │ ├── SwarmTable.scala │ │ ├── UniqueId.scala │ │ ├── aws │ │ ├── AWS.scala │ │ ├── SimpleWorkflowConfig.scala │ │ └── swf │ │ │ ├── SwfAsyncHelpers.scala │ │ │ ├── SwfDecision.scala │ │ │ └── SwfHistoryEvent.scala │ │ └── json │ │ ├── FieldTypeJson.scala │ │ ├── PlayJsonIsoDateFormat.scala │ │ ├── SubmittedData.scala │ │ └── SwarmDefinition.scala │ └── test │ └── scala │ └── swarmize │ ├── SwarmFieldTypesTest.scala │ ├── SwarmSubmissionValidatorTest.scala │ ├── SwarmTest.scala │ ├── TestSwarms.scala │ ├── aws │ └── swf │ │ └── SwfHistoryEventTest.scala │ └── json │ ├── SubmittedDataTest.scala │ └── SwarmDefinitionJsonTest.scala └── swarmize-dot-com ├── .ebextensions └── packages.config ├── .env.example ├── .gitignore ├── .rspec ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── README.md ├── Rakefile ├── app ├── assets │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── images │ │ ├── .keep │ │ └── favicon.ico │ ├── javascripts │ │ ├── application.js │ │ ├── backbone-min.js │ │ ├── bootstrap.min.js │ │ ├── c3.min.js │ │ ├── d3.min.js │ │ ├── embed.js │ │ ├── field_workspace.js │ │ ├── form_element.js │ │ ├── jquery.iecors.js │ │ ├── models │ │ │ ├── graph_data.js │ │ │ └── results_data.js │ │ ├── moment.min.js │ │ ├── palette_field.js │ │ ├── parsley-config.js │ │ ├── parsley.min.js │ │ ├── rickshaw.min.js │ │ ├── swarmize-embed.js │ │ ├── underscore-min.js │ │ └── views │ │ │ ├── pie_graph.js │ │ │ ├── results_table.js │ │ │ └── timeseries_graph.js │ └── stylesheets │ │ ├── application.scss │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.min.css │ │ ├── c3.css │ │ └── rickshaw.min.css ├── controllers │ ├── admin_controller.rb │ ├── api_keys_controller.rb │ ├── application_controller.rb │ ├── case_studies_controller.rb │ ├── concerns │ │ └── .keep │ ├── csv_controller.rb │ ├── documentation_controller.rb │ ├── graphs_controller.rb │ ├── home_controller.rb │ ├── oembed_controller.rb │ ├── permissions_controller.rb │ ├── search_controller.rb │ ├── sessions_controller.rb │ ├── swarm_import_controller.rb │ ├── swarms_controller.rb │ ├── users_controller.rb │ └── utils_controller.rb ├── helpers │ ├── application_helper.rb │ └── fields_helper.rb ├── mailers │ ├── .keep │ └── permission_mailer.rb ├── models │ ├── .keep │ ├── access_permission.rb │ ├── api_key.rb │ ├── concerns │ │ └── .keep │ ├── field_description.rb │ ├── graph.rb │ ├── swarm.rb │ ├── swarm_field.rb │ └── user.rb └── views │ ├── admin │ └── show.html.haml │ ├── api_keys │ └── index.html.haml │ ├── case_studies │ ├── _list.html.haml │ ├── index.html.haml │ └── show.html.haml │ ├── documentation │ ├── index.html.haml │ └── show.html.haml │ ├── form_partials │ ├── _bigtext_display_field_partial.html.haml │ ├── _check_box_display_field_partial.html.haml │ ├── _display_field_partial.html.haml │ ├── _edit_field_partial.html.haml │ ├── _element_controls.html.haml │ ├── _pick_one_display_field_partial.html.haml │ ├── _pick_several_display_field_partial.html.haml │ ├── _rating_display_field_partial.html.haml │ └── _yesno_display_field_partial.html.haml │ ├── graphs │ ├── _pie.html.haml │ ├── _timeseries.html.haml │ ├── delete.html.haml │ ├── edit.html.haml │ ├── index.html.haml │ └── new.html.haml │ ├── home │ ├── _logged_in.html.haml │ ├── _logged_out.html.haml │ ├── _project_status.html.haml │ └── show.html.haml │ ├── layouts │ ├── _flashes.html.haml │ ├── _nav.html.haml │ ├── application.html.haml │ └── embed.html.haml │ ├── permission_mailer │ └── permission_email.text.erb │ ├── permissions │ ├── _logged_out_permissions_page.html.haml │ ├── _owner_permissions_page.html.haml │ └── index.html.haml │ ├── search │ └── results.html.haml │ ├── sessions │ └── logout.html.haml │ ├── swarm_import │ └── new.html.haml │ ├── swarms │ ├── _close_swarm_modal.html.haml │ ├── _embed_closed.html.haml │ ├── _embed_live.html.haml │ ├── _field_code_update_js.html.haml │ ├── _open_swarm_modal.html.haml │ ├── _swarm_description_open_close.html.haml │ ├── _swarm_description_open_close_logged_in.html.haml │ ├── _swarm_dropdown_menu.html.haml │ ├── _swarm_graphs.html.haml │ ├── _swarm_header.html.haml │ ├── _swarm_page_js.html.haml │ ├── _swarm_results.html.haml │ ├── _swarm_table.html.haml │ ├── closed.html.haml │ ├── code.html.haml │ ├── delete.html.haml │ ├── draft.html.haml │ ├── edit.html.haml │ ├── embed.html.haml │ ├── fields.html.haml │ ├── index.html.haml │ ├── live.html.haml │ ├── new.html.haml │ ├── preview.html.haml │ └── show.html.haml │ └── users │ ├── _user_header.html.haml │ ├── closed.html.haml │ ├── delete.html.haml │ ├── draft.html.haml │ ├── index.html.haml │ ├── live.html.haml │ └── show.html.haml ├── bin ├── bundle ├── rails └── rake ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── amazon_ses.rb │ ├── aws.rb │ ├── backtrace_silencers.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── non_digest_assets.rb │ ├── omniauth.rb │ ├── secret_token.rb │ ├── session_store.rb │ ├── will_paginate.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml └── routes.rb ├── db ├── migrate │ ├── 20140712134916_create_swarms.rb │ ├── 20140716162431_add_fields_to_swarm.rb │ ├── 20140722135555_add_open_close_to_swarm.rb │ ├── 20140729093242_create_users.rb │ ├── 20140729123430_add_user_id_to_swarms.rb │ ├── 20140729133308_add_parent_swarm_id_to_swarms.rb │ ├── 20140801101148_add_is_fake_to_users.rb │ ├── 20140801134513_add_spiked_field_to_swarms.rb │ ├── 20140812130235_add_tokens_to_swarms.rb │ ├── 20140814134610_create_graph_configurations.rb │ ├── 20140814135824_rename_graph_configurations.rb │ ├── 20140814173018_create_swarm_fields.rb │ ├── 20140815095709_remove_serialized_fields.rb │ ├── 20140826092139_create_access_permissions.rb │ ├── 20140827092647_add_owner_field_to_permissions.rb │ ├── 20140827103714_add_creators_to_access_permissions.rb │ ├── 20140827162410_remove_creator_field_from_swarm.rb │ ├── 20140828114635_add_is_admin_to_users.rb │ ├── 20140912101553_add_deleted_at_to_swarms.rb │ ├── 20141010094402_remove_viz_type.rb │ ├── 20141017093840_add_other_to_swarmfield.rb │ ├── 20141017131200_add_toggles_to_swarm.rb │ ├── 20141017150238_change_other_to_allow_other.rb │ ├── 20141021114539_change_desscription_to_text_field.rb │ └── 20141204145601_allow_pick_one_to_be_select.rb ├── schema.rb └── seeds.rb ├── deploy-sample.json ├── deploy.rb ├── eb_name.txt ├── field_types.json ├── lib ├── assets │ └── .keep ├── dummy.rb ├── dynamo_sync.rb ├── email_validator.rb ├── json_importer.rb ├── swarm_csv_tool.rb ├── swarmize_oembed.rb ├── swarmize_search.rb ├── swarmize_search │ └── queries.rb └── tasks │ ├── .keep │ └── swarmize.rake ├── log └── .keep ├── package.sh ├── public ├── 404.html ├── 422.html ├── 500.html ├── case_studies │ ├── broadband_survey.md │ ├── broadband_survey │ │ ├── add-fields.gif │ │ ├── clone.gif │ │ ├── create.gif │ │ ├── csv.png │ │ ├── embed-code.gif │ │ ├── graphs.gif │ │ ├── login.gif │ │ ├── move-delete.gif │ │ ├── open.gif │ │ ├── permissions.gif │ │ ├── preview.png │ │ ├── table.png │ │ ├── thumb.png │ │ └── view-form.png │ ├── indyref.md │ ├── indyref │ │ ├── indyref-graph1.png │ │ ├── indyref-graph2.png │ │ ├── indyref-graph3.png │ │ ├── indyref1.gif │ │ └── thumb.png │ ├── realtime_clicker.md │ └── realtime_clicker │ │ ├── aggregate-field.gif │ │ ├── api-keys.gif │ │ ├── get-code.gif │ │ ├── map-demo.png │ │ ├── preview-form.png │ │ ├── results-over-time.png │ │ └── thumb.jpg ├── documentation │ └── api.md ├── favicon.ico ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── js │ └── swarmize-embed.js └── robots.txt ├── spec ├── controllers │ ├── admin_controller_spec.rb │ ├── api_tokens_controller_spec.rb │ ├── case_studies_controller_spec.rb │ ├── csv_controller_spec.rb │ ├── graphs_controller_spec.rb │ ├── home_controller_spec.rb │ ├── oembed_controller_spec.rb │ ├── permissions_controller_spec.rb │ ├── search_controller_spec.rb │ ├── sessions_controller_spec.rb │ ├── shared_examples_for_controllers.rb │ ├── swarm_import_controller_spec.rb │ ├── swarms_controller_spec.rb │ ├── users_controller_spec.rb │ └── utils_controller_spec.rb ├── factories │ ├── access_permissions.rb │ ├── graphs.rb │ ├── swarm_fields.rb │ ├── swarms.rb │ └── users.rb ├── helpers │ ├── admin_helper_spec.rb │ ├── graphs_helper_spec.rb │ ├── home_helper_spec.rb │ ├── permissions_helper_spec.rb │ ├── search_helper_spec.rb │ ├── sessions_helper_spec.rb │ ├── swarms_helper_spec.rb │ └── users_helper_spec.rb ├── lib │ ├── json_importer_spec.rb │ ├── sample.json │ ├── sample_field.json │ └── sample_field_possible_values.json ├── mailers │ └── permission_mailer_spec.rb ├── models │ ├── access_permission_spec.rb │ ├── email_validator_spec.rb │ ├── graph_spec.rb │ ├── swarm_field_spec.rb │ ├── swarm_spec.rb │ └── user_spec.rb ├── spec_helper.rb └── support │ └── factory_girl.rb └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea*/ 3 | 4 | collector/logs 5 | stasher/logs 6 | logs 7 | *.zip 8 | credentials.csv 9 | .rakeTasks 10 | .generators 11 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 1.9.3-p547 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | scala: 3 | - 2.11.2 4 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Swarmize 2 | Copyright 2014 Guardian Media Group 3 | 4 | Swarmize was created by Tom Armitage, Graham Tackley, Sean Clarke and Matt McAlister. 5 | 6 | Swarmize was initially funded by a grant from the Knight News Challenge with support from Guardian Media Group. 7 | 8 | http://www.swarmize.com/ 9 | 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | THIS PROJECT IS NOW DEPRECATED 2 | 3 | 4 | # Swarmize 5 | 6 | Swarmize is a stack of tools to make crowd-powered number-gathering a lot easier. 7 | 8 | ## The Stack 9 | 10 | Swarmize is built out of a number of components, written in either Ruby or Scala, and currently built around an AWS stack including RDS (hosting Postgres), Simple Workflow, DynamoDB, and deployed against Elastic Beanstalk. We use Elasticsearch as a data store. 11 | 12 | ## Contents 13 | 14 | Currently in this repository: 15 | 16 | * `api`: The Scala-based API [scala] 17 | * `bombard`: A small script for filling a Swarm with fake data [ruby] 18 | * `case_studies`: the case studies for the project, which are then copied into the Rails app for deployment (but will work fine on Github as they are) [markdown] 19 | * `cloudformation`: AWS CloudFormation config 20 | * `collector`: Receive data from an endpoint and forward it to the stream. [scala] 21 | * `deploy`: The deploy tools/scripts [ruby/bash] 22 | * `examples`: Examples of Collector and Retrieval API usage 23 | * `geocoder`: Simple Node geocoder endpoint [node] 24 | * `livedebate`: Sample app: click on the face you're liking most at any point during a prime ministerial debate; the data - along with some demographic information - will be pushed into Swarm. [ruby] 25 | * `processor`: Processes submitted data [scala] 26 | * `sentiment-endpoint`: Simple Node sentiment analysis endpoint [node] 27 | * `swarmize-dot-com`: the Swarmize alpha website. [ruby] 28 | 29 | [![Build Status](https://travis-ci.org/guardian/swarmize.svg?branch=master)](https://travis-ci.org/guardian/swarmize) 30 | -------------------------------------------------------------------------------- /api/.env.sample: -------------------------------------------------------------------------------- 1 | ELASTICSEARCH_HOST=localhost 2 | AWS_ACCESS_KEY_ID=xx 3 | AWS_SECRET_ACCESS_KEY=xx 4 | 5 | -------------------------------------------------------------------------------- /api/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api._ 4 | import play.api.mvc._ 5 | 6 | object Application extends Controller { 7 | 8 | def index = Action { 9 | Ok("swarmize-api") 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /api/app/controllers/Management.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api.mvc.{Controller, Action} 4 | 5 | object Management extends Controller { 6 | def healthCheck() = Action { 7 | Ok("All good") 8 | } 9 | } -------------------------------------------------------------------------------- /api/app/lib/GeoJsonFormatter.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import play.api.libs.json._ 4 | 5 | object GeoJsonFormatter { 6 | 7 | def format(src: List[JsValue], key: String): JsObject = { 8 | 9 | val features = src map { objvalue => 10 | val obj = objvalue.as[JsObject] 11 | val geoPoint = makeGeoPointDoubles(obj \ key) 12 | val props = obj - key 13 | 14 | Json.obj( 15 | "type" -> "Feature", 16 | "geometry" -> Json.obj( 17 | "type" -> "Point", 18 | "coordinates" -> geoPoint 19 | ), 20 | "properties" -> props 21 | ) 22 | 23 | } 24 | 25 | Json.obj( 26 | "type" -> "FeatureCollection", 27 | "features" -> features 28 | ) 29 | } 30 | 31 | private def makeGeoPointDoubles(pt: JsValue): JsValue = { 32 | val arrValues = pt.as[JsArray].value 33 | 34 | val doubleValues = arrValues.map { 35 | case JsString(s) => JsNumber(BigDecimal(s)) 36 | case other => other 37 | } 38 | 39 | JsArray(doubleValues) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /api/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # Home page 6 | GET / controllers.Application.index 7 | 8 | GET /swarms/:token controllers.Swarms.show(token) 9 | GET /swarms/:token/results controllers.Swarms.results(token, page: Int ?= 1, per_page: Int ?= 10, format: Option[String], geo_json_point_key: Option[String], order_by ?= "oldest") 10 | GET /swarms/:token/latest controllers.Swarms.latest(token) 11 | GET /swarms/:token/counts controllers.Swarms.counts(token) 12 | 13 | 14 | GET /health-check controllers.Management.healthCheck 15 | -------------------------------------------------------------------------------- /api/eb_name.txt: -------------------------------------------------------------------------------- 1 | Swarmize API 2 | -------------------------------------------------------------------------------- /api/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PACKAGE_FILENAME=$1 4 | DIRECTORY=$2 5 | 6 | APP_NAME=api 7 | 8 | # clear up stuff left behind from previous builds 9 | [ -d target/docker ] && rm -rf target/docker 10 | 11 | # build the zip 12 | cd .. 13 | 14 | sbt ${APP_NAME}/docker:stage && \ 15 | cd ${DIRECTORY}/target/docker && \ 16 | zip -r ../../${PACKAGE_FILENAME} * 17 | 18 | 19 | -------------------------------------------------------------------------------- /bombard/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'httparty' 4 | -------------------------------------------------------------------------------- /bombard/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | httparty (0.13.1) 5 | json (~> 1.8) 6 | multi_xml (>= 0.5.2) 7 | json (1.8.1) 8 | multi_xml (0.5.5) 9 | 10 | PLATFORMS 11 | ruby 12 | 13 | DEPENDENCIES 14 | httparty 15 | -------------------------------------------------------------------------------- /bombard/README.md: -------------------------------------------------------------------------------- 1 | # bombard 2 | 3 | Bombard is a very simple/dumb ruby script for hammering a swarm (FSVO 'hammering') with hits. It was designed around a specific swarm, and contains code specific to those fields, but it should be straightforward enough to adapt it for other swarms, especially if you'd like to fill them with not-entirely-random data. 4 | 5 | (For instance: in this swarm, an imaginary user will always specify the same plans to vote in the next election, even though who they agree with will change moment-to-moment). -------------------------------------------------------------------------------- /case_studies/broadband_survey/add-fields.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/add-fields.gif -------------------------------------------------------------------------------- /case_studies/broadband_survey/clone.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/clone.gif -------------------------------------------------------------------------------- /case_studies/broadband_survey/create.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/create.gif -------------------------------------------------------------------------------- /case_studies/broadband_survey/csv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/csv.png -------------------------------------------------------------------------------- /case_studies/broadband_survey/embed-code.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/embed-code.gif -------------------------------------------------------------------------------- /case_studies/broadband_survey/graphs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/graphs.gif -------------------------------------------------------------------------------- /case_studies/broadband_survey/login.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/login.gif -------------------------------------------------------------------------------- /case_studies/broadband_survey/move-delete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/move-delete.gif -------------------------------------------------------------------------------- /case_studies/broadband_survey/open.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/open.gif -------------------------------------------------------------------------------- /case_studies/broadband_survey/permissions.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/permissions.gif -------------------------------------------------------------------------------- /case_studies/broadband_survey/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/preview.png -------------------------------------------------------------------------------- /case_studies/broadband_survey/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/table.png -------------------------------------------------------------------------------- /case_studies/broadband_survey/thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/thumb.png -------------------------------------------------------------------------------- /case_studies/broadband_survey/view-form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/broadband_survey/view-form.png -------------------------------------------------------------------------------- /case_studies/indyref/indyref-graph1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/indyref/indyref-graph1.png -------------------------------------------------------------------------------- /case_studies/indyref/indyref-graph2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/indyref/indyref-graph2.png -------------------------------------------------------------------------------- /case_studies/indyref/indyref-graph3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/indyref/indyref-graph3.png -------------------------------------------------------------------------------- /case_studies/indyref/indyref1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/indyref/indyref1.gif -------------------------------------------------------------------------------- /case_studies/indyref/thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/indyref/thumb.png -------------------------------------------------------------------------------- /case_studies/realtime_clicker/aggregate-field.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/realtime_clicker/aggregate-field.gif -------------------------------------------------------------------------------- /case_studies/realtime_clicker/api-keys.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/realtime_clicker/api-keys.gif -------------------------------------------------------------------------------- /case_studies/realtime_clicker/get-code.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/realtime_clicker/get-code.gif -------------------------------------------------------------------------------- /case_studies/realtime_clicker/map-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/realtime_clicker/map-demo.png -------------------------------------------------------------------------------- /case_studies/realtime_clicker/preview-form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/realtime_clicker/preview-form.png -------------------------------------------------------------------------------- /case_studies/realtime_clicker/results-over-time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/realtime_clicker/results-over-time.png -------------------------------------------------------------------------------- /case_studies/realtime_clicker/thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/case_studies/realtime_clicker/thumb.jpg -------------------------------------------------------------------------------- /cloudformation/README.md: -------------------------------------------------------------------------------- 1 | # Cloudformation Scripts 2 | 3 | These scripts are used to deploy swarmize on an amazon account. 4 | 5 | Before using, you need to manually create an S3 bucket to store these, and the distibution artifacts, on. 6 | Update settings.sh to define the name of that bucket. 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /cloudformation/base.template: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion" : "2010-09-09", 3 | 4 | "Description" : "swamize: basic infrastructure", 5 | 6 | "Resources" : { 7 | 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /cloudformation/bashlib/shared.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | S3_CFN_PATH="s3://$BUCKET_NAME/cfn/" 4 | S3_CFN_URL="https://s3-$REGION.amazonaws.com/$BUCKET_NAME/cfn" 5 | S3_DIST_PATH="s3://$BUCKET_NAME/" 6 | S3_DIST_URL="https://s3-$REGION.amazonaws.com/$BUCKET_NAME" 7 | AWS_CMD="aws --region $REGION --profile $PROFILE" 8 | 9 | echo "Region: $REGION Profile: $PROFILE" 10 | 11 | 12 | uploadTemplates() 13 | { 14 | echo "Uploading templates to $S3_CFN_PATH..." 15 | 16 | ${AWS_CMD} s3 sync . ${S3_CFN_PATH} --exclude "*" --include "*.template" --acl public-read 17 | 18 | echo "Uploading distribution files to $S3_DIST_URL..." 19 | 20 | ${AWS_CMD} s3 sync dist ${S3_DIST_PATH} --acl public-read 21 | 22 | } -------------------------------------------------------------------------------- /cloudformation/cfn.conf: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PROFILE=swarmize 4 | REGION=eu-west-1 5 | BUCKET_NAME=swarmize-bin 6 | STACK_NAME="Swarmize" -------------------------------------------------------------------------------- /cloudformation/create_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . cfn.conf 4 | 5 | . bashlib/shared.sh 6 | 7 | uploadTemplates 8 | 9 | echo "Applying cloudformation script..." 10 | 11 | ${AWS_CMD} cloudformation create-stack \ 12 | --stack-name "$STACK_NAME" \ 13 | --template-url "$S3_CFN_URL/all.template" \ 14 | --capabilities CAPABILITY_IAM \ 15 | --parameters ParameterKey=TemplateBucketPath,ParameterValue=${S3_CFN_URL} \ 16 | ParameterKey=DistBucketPath,ParameterValue=${S3_DIST_URL} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /cloudformation/delete_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . cfn.conf 4 | 5 | . bashlib/shared.sh 6 | 7 | echo "Deleting cloudformation stack $STACK_NAME..." 8 | 9 | ${AWS_CMD} cloudformation delete-stack \ 10 | --stack-name "$STACK_NAME" \ 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /cloudformation/dist/elasticsearch.yml: -------------------------------------------------------------------------------- 1 | cluster.name: swarmize 2 | 3 | plugin.mandatory: cloud-aws 4 | 5 | cloud.aws.region: eu-west-1 6 | cloud.node.auto_attributes: true 7 | 8 | gateway.recover_after_nodes: 2 9 | gateway.recover_after_time: 2m 10 | gateway.expected_nodes: 2 11 | 12 | discovery.zen.ping.multicast.enabled: false 13 | discovery.zen.minimum_master_nodes: 1 14 | 15 | discovery.type: ec2 16 | discovery.ec2.tag.Name: swarmize-elasticsearch 17 | 18 | index.number_of_shards: 3 19 | index.number_of_replicas: 0 20 | 21 | -------------------------------------------------------------------------------- /cloudformation/update_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . cfn.conf 4 | 5 | . bashlib/shared.sh 6 | 7 | uploadTemplates 8 | 9 | echo "Updating cloudformation for $STACK_NAME..." 10 | 11 | ${AWS_CMD} cloudformation update-stack \ 12 | --stack-name "$STACK_NAME" \ 13 | --template-url "$S3_CFN_URL/all.template" \ 14 | --capabilities CAPABILITY_IAM \ 15 | --parameters ParameterKey=TemplateBucketPath,ParameterValue=${S3_CFN_URL} \ 16 | ParameterKey=DistBucketPath,ParameterValue=${S3_DIST_URL} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /cloudformation/validate_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . cfn.conf 4 | 5 | . bashlib/shared.sh 6 | 7 | uploadTemplates 8 | 9 | echo "Validating cloudformation script..." 10 | 11 | ${AWS_CMD} cloudformation validate-template \ 12 | --template-url "$S3_CFN_URL/all.template" 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /collector/README.md: -------------------------------------------------------------------------------- 1 | Collector 2 | ========= 3 | 4 | This is a scala app, built with scala's [simple build tool](http://www.scala-sbt.org/). 5 | 6 | To build, first install any version of sbt as described [here](http://www.scala-sbt.org/download.html). 7 | Summary: on a mac, just `brew install sbt`. 8 | 9 | Then in this directory: 10 | 11 | ``` 12 | $ sbt 13 | > test:run 14 | ``` 15 | 16 | The first time you run, the internet will download. It shouldn't happen again. 17 | -------------------------------------------------------------------------------- /collector/app/Global.scala: -------------------------------------------------------------------------------- 1 | import play.api.http.HeaderNames 2 | import play.api.mvc.{Filter, RequestHeader, Result, WithFilters} 3 | 4 | import scala.concurrent.Future 5 | 6 | import scala.concurrent.ExecutionContext.Implicits.global 7 | 8 | object Global extends WithFilters(AddCorsHeaderFilter) { 9 | } 10 | 11 | 12 | object AddCorsHeaderFilter extends Filter { 13 | override def apply(nextFilter: (RequestHeader) => Future[Result])(req: RequestHeader): Future[Result] = { 14 | nextFilter(req) map ( 15 | _.withHeaders(HeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN -> "*") 16 | ) 17 | } 18 | } -------------------------------------------------------------------------------- /collector/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api.mvc._ 4 | 5 | object Application extends Controller { 6 | 7 | def index = Action { 8 | Ok("swarmize: collector") 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /collector/app/controllers/Management.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api.mvc.{Controller, Action} 4 | 5 | object Management extends Controller { 6 | def healthCheck() = Action { 7 | Ok("All good") 8 | } 9 | } -------------------------------------------------------------------------------- /collector/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @main("Welcome to Play") { 4 | 5 | @play20.welcome(message) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /collector/app/views/main.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | 7 | @title 8 | 9 | 10 | 11 | 12 | @content 13 | 14 | 15 | -------------------------------------------------------------------------------- /collector/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # Home page 6 | GET / controllers.Application.index 7 | 8 | GET /swarms/:token controllers.Swarms.show(token) 9 | 10 | POST /swarms/:token.json controllers.Swarms.submitJson(token) 11 | POST /swarms/:token controllers.Swarms.submit(token) 12 | 13 | GET /health-check controllers.Management.healthCheck 14 | 15 | 16 | # Map static resources from the /public folder to the /assets URL path 17 | GET /assets/*file controllers.Assets.at(path="/public", file) 18 | -------------------------------------------------------------------------------- /collector/eb_name.txt: -------------------------------------------------------------------------------- 1 | Collector -------------------------------------------------------------------------------- /collector/example_curl_live_form.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | http collector.swarmize.com/swarms/kwkwafkk \ 5 | do_you_have_internet_at_home=no \ 6 | can_you_get_broadband_where_you_live=yes \ 7 | who_is_your_broadband_provider=other \ 8 | who_is_your_broadband_provider_other=Pidgeon \ 9 | what_s_your_postcode="SW19 1HN" 10 | 11 | 12 | -------------------------------------------------------------------------------- /collector/example_curl_live_json.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | curl -XPOST "http://collector.swarmize.com/swarms/moqxjfwr.json" -d @- < 1.4) 6 | nokogiri (>= 1.4.4) 7 | httparty (0.13.1) 8 | json (~> 1.8) 9 | multi_xml (>= 0.5.2) 10 | json (1.8.1) 11 | mini_portile (0.6.0) 12 | multi_xml (0.5.5) 13 | nokogiri (1.6.3.1) 14 | mini_portile (= 0.6.0) 15 | 16 | PLATFORMS 17 | ruby 18 | 19 | DEPENDENCIES 20 | aws-sdk (< 2) 21 | httparty 22 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Examples of Swarmize usage. Examples currently include: 4 | 5 | * `generic-backend` - the simplest possible node.js generic back-end for any Swarm. 6 | * `livedebate` - a realtime 'clicker' to send data to the Collector, with a backend 7 | * `livedebate-scotland` - a realtime 'clicker' to send data to the Collector, which does so directly from a flat HTML page 8 | * `mapdemo-standalone` - retrieving data from the Swarmize Retrieval API directly from a flat HTML page 9 | * `mapdemo-backend` - retrieving data from the Swarmize Retrieval API via a small backend, for security reasons 10 | -------------------------------------------------------------------------------- /examples/generic-backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /examples/generic-backend/README.md: -------------------------------------------------------------------------------- 1 | # Swarmize Retrieval API Generic Backend 2 | 3 | If you've read the documentation, and looked at both the `mapdemo-standaone` and `mapdemo-backend`, you'll realise that having some kind of back-end for retrieving any remotely sensitive data from a Swarm is a good idea. 4 | 5 | Fortunately, it really doesn't have to be that complex: this is a sample node.js application that forwards requests to the Retreival API and merges the API key in on the backend. Both querystring and method are defined in the front-end JS; the back-end of the site simply adds the API key to the request. 6 | 7 | ## Installation 8 | 9 | Assuming you have node already installed, from the application directory, run `npm install`. This will install all appropriate modules. 10 | 11 | ## Running 12 | 13 | From the application directory: 14 | 15 | npm start 16 | 17 | And then you'll be able to visit the sample index page at: 18 | 19 | http://localhost:5000/ 20 | 21 | ## Notes 22 | 23 | `backend.js` is the entirety of the backend code, and the comments within should explain what is happening. 24 | 25 | `public/index.html` contains the Javascript making the AJAX call, and you can see how the method name (`results`) and parameters passed to the back-end are defined. 26 | 27 | In an ideal world, you probably wouldn't want to forward requests directly: rather, you'd want to configure them on the back-end so they couldn't be enumerated over. But this is a straightforward example that at least stops you exposing your API key to the public. -------------------------------------------------------------------------------- /examples/generic-backend/backend.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var request = require('request'); 3 | var querystring = require('querystring'); 4 | 5 | var app = express(); 6 | 7 | app.use(express.static(__dirname + '/public')); 8 | 9 | // the API Key is, effectively, a global. 10 | var apiKey = '35c58cae15301cfa'; 11 | 12 | app.get("/data/:method", function(req,res) { 13 | // take the original query and merge in the API Token 14 | var query = req.query 15 | query.api_key = apiKey; 16 | 17 | // hit the requested method on the Swarmize API with the new querystring... 18 | var newQueryString = 'http://api.swarmize.com/swarms/rycadjgp/' + req.params.method + "?" + querystring.stringify(query); 19 | request(newQueryString, function (error, response, body) { 20 | if (!error && response.statusCode == 200) { 21 | //... and return that JSON straight to the browser 22 | var json = JSON.parse(body); 23 | res.json(json); 24 | } 25 | }) 26 | }); 27 | 28 | var port = Number(process.env.PORT || 5000); 29 | 30 | var server = app.listen(port, function() { 31 | console.log("Generic swarmize retrieval backend running on port " + port); 32 | }); 33 | -------------------------------------------------------------------------------- /examples/generic-backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generic-backend", 3 | "version": "0.0.1", 4 | "description": "A generic backend to echo Swarmize Retrieval API content to the front-end", 5 | "main": "backend.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node backend.js" 9 | }, 10 | "author": "", 11 | "license": "BSD-2-Clause", 12 | "dependencies": { 13 | "express": "~4.4.5", 14 | "request": "~2.36.0", 15 | "underscore": "~1.7.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/generic-backend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generic Swarmize Backend demo 5 | 6 | 16 | 17 | 18 | 19 | 20 |

Generic Swarmize Backend retrieval

21 |

The below data is retrieved via JSON - but you won't expose a token in the front-end if you do so.

22 |

23 |     

24 |   
25 | 
26 |   
27 | 


--------------------------------------------------------------------------------
/examples/livedebate-scotland/README.md:
--------------------------------------------------------------------------------
 1 | # Swarmize app demo: single page example.
 2 | 
 3 | This demonstration is of a "live clicker" for real-time feedback during a TV debate: the user enters their name and postcode, and then taps a face every time they agree with something said on TV.
 4 | 
 5 | This demo differs from the original `livedebate` version by not requiring a backend: it posts data directly to the collector via Javascript.
 6 | 
 7 | ## Running
 8 | 
 9 | Serve "index.html" from a webserver of your choice. For instance, in the project directory:
10 | 
11 | 	python -m SimpleHTTPServer
12 | 	
13 | or using node's `http-server`
14 | 
15 | 	http-server
16 | 
17 | From the application directory:
18 | 
19 | 	npm start
20 | 	
21 | And then you'll be able to view the clicker at:
22 | 
23 | 	http://localhost:8000/
24 | 		
25 | (or whatever port the server is running on).
26 | 	
27 | 	
28 | ##  Notes
29 | 
30 | You'll note you don't need an API token to send data to the Collector API: all valid requests to swarms that aren't closed are accepted; you just need to know the field names, swarm token, and ensure your data submitted is valid.
31 | 
32 | So as to be able to filter all results by individual users, each user gets a unique token which is set as a cookie, and injected into a hidden field within the form.
33 | 
34 | The app is presented as a single form for conceptual purposes - one form posting to Swarmize - but could, obviously, be made more complex to make the interactions more compelling.
35 | 
36 | As you can see, unlike the retrieval API examples, submitting to the Collector is most straightforwardly done without a backend, and there are few downsides to this approach.


--------------------------------------------------------------------------------
/examples/livedebate-scotland/ad.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/examples/livedebate-scotland/ad.jpg


--------------------------------------------------------------------------------
/examples/livedebate-scotland/as.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/examples/livedebate-scotland/as.jpg


--------------------------------------------------------------------------------
/examples/livedebate-scotland/debate.js:
--------------------------------------------------------------------------------
 1 | $(document).ready(function() {
 2 |   if(!docCookies.hasItem('scotlandUserId')) {
 3 |     docCookies.setItem('scotlandUserId',Date.now(),Infinity);
 4 |   }
 5 | 
 6 |   // set hidden field.
 7 |   $("#userkey").val(docCookies.getItem('scotlandUserId'));
 8 | 
 9 | 	// Open external links in a new window
10 |   $(".feedback-buttons").hide();
11 | 
12 |   $(".next-button").click(function(e) {
13 |     $(".setup").hide();
14 |     $("h1").hide();
15 |     $(".feedback-buttons").show();
16 |     e.preventDefault();
17 |   });
18 | 
19 |   $(".feedback-button").click(function(e) {
20 |     if(!$(this).hasClass('disabled')) {
21 |       clickVisuals(this);
22 |       $("input#feedback").val(this.id);
23 |       $.post( $("form#debate").attr('action'),
24 |              $("form#debate").serialize());
25 |     }
26 |     e.preventDefault();
27 |   });
28 | 
29 | });
30 | 
31 | function clickVisuals(el) {
32 |   $(".feedback-button").animate({'opacity': 0.5},200);
33 | 
34 |   var top = $(el).offset().top + 20
35 |   //var left = ($(el).width() / 2) - 20;
36 |   var left = 0;
37 | 
38 |   $("body").append("
") 39 | $(".tick").css('top', top+"px").css('left', left + 'px').fadeIn(); 40 | 41 | $(".feedback-button").addClass('disabled'); 42 | setTimeout(fadeUpLinks, 200); 43 | } 44 | 45 | function fadeUpLinks(el) { 46 | $(".tick").fadeOut(function() { $(this).remove(); } ); 47 | $(".feedback-button").animate({'opacity': 1},200); 48 | $(".feedback-button").fadeTo(1); 49 | $(".feedback-button").removeClass('disabled'); 50 | } 51 | -------------------------------------------------------------------------------- /examples/livedebate/.gitignore: -------------------------------------------------------------------------------- 1 | .sass-cache 2 | -------------------------------------------------------------------------------- /examples/livedebate/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | ruby "1.9.3" 3 | 4 | gem 'sinatra' 5 | gem 'shotgun' 6 | gem 'haml' 7 | gem 'thin' 8 | gem 'httparty' 9 | 10 | gem 'compass' 11 | gem 'sass' 12 | -------------------------------------------------------------------------------- /examples/livedebate/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | chunky_png (1.2.8) 5 | compass (0.12.6) 6 | chunky_png (~> 1.2) 7 | fssm (>= 0.2.7) 8 | sass (~> 3.2.19) 9 | daemons (1.1.9) 10 | eventmachine (1.0.3) 11 | fssm (0.2.10) 12 | haml (4.0.3) 13 | tilt 14 | httparty (0.13.1) 15 | json (~> 1.8) 16 | multi_xml (>= 0.5.2) 17 | json (1.8.1) 18 | multi_xml (0.5.5) 19 | rack (1.5.2) 20 | rack-protection (1.5.0) 21 | rack 22 | sass (3.2.19) 23 | shotgun (0.9) 24 | rack (>= 1.0) 25 | sinatra (1.4.3) 26 | rack (~> 1.4) 27 | rack-protection (~> 1.4) 28 | tilt (~> 1.3, >= 1.3.4) 29 | thin (1.6.2) 30 | daemons (>= 1.0.9) 31 | eventmachine (>= 1.0.0) 32 | rack (>= 1.0.0) 33 | tilt (1.4.1) 34 | 35 | PLATFORMS 36 | ruby 37 | 38 | DEPENDENCIES 39 | compass 40 | haml 41 | httparty 42 | sass 43 | shotgun 44 | sinatra 45 | thin 46 | -------------------------------------------------------------------------------- /examples/livedebate/README.md: -------------------------------------------------------------------------------- 1 | # Swarmize app demo: using a backend 2 | 3 | This demonstration is of a "live clicker" for real-time feedback during a TV debate: the user enters their name and postcode, and then taps a face every time they agree with something said on TV. 4 | 5 | ## Installation 6 | 7 | This app is a small Ruby/Sinatra application. 8 | 9 | Assuming you have Ruby already installed, from the application directory, run `bundle install`. This will install all appropriate gems. 10 | 11 | ## Running 12 | 13 | From the application directory: 14 | 15 | bundle exec rackup 16 | 17 | And then you'll be able to visit the clicker at: 18 | 19 | http://localhost:9292 20 | 21 | 22 | ## Notes 23 | 24 | You'll note you don't need an API key to send data to the Collector API: all valid requests to swarms that aren't closed are accepted; you just need to know the field names, swarm token, and ensure your data submitted is valid. 25 | 26 | So as to be able to filter all results by individual users, each user gets a unique token which is set as a cookie, and injected into their submission to the collector API. Using the backend code, it'd also be possible to inject other data into the request, if you need to. 27 | 28 | The app is presented as a single form for conceptual purposes - one form posting to Swarmize - but could, obviously, be made more complex to make the interactions more compelling. 29 | 30 | As you can see, unlike the retrieval API examples, submitting to the Collector is most straightforwardly done without a backend, and there are few downsides to this approach. -------------------------------------------------------------------------------- /examples/livedebate/app.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'sinatra/base' 3 | require './lib/swarm_api' 4 | 5 | class LiveDebate < Sinatra::Base 6 | enable :sessions 7 | set :sessions, :expire_after => 2592000 8 | 9 | swarm = SwarmApi.new("fpkvfqwm") 10 | 11 | get '/' do 12 | unless session[:unique_key] 13 | session[:unique_key] = (Time.now.to_f * 10000000).to_i 14 | end 15 | 16 | haml :index, :locals => {:key => session[:unique_key]} 17 | end 18 | 19 | post '/endpoint' do 20 | data = params.merge(:unique_user_key => session[:unique_key]) 21 | 22 | # now send that to Swarm 23 | swarm.send(data) 24 | end 25 | 26 | # start the server if ruby file executed directly 27 | run! if app_file == $0 28 | end 29 | 30 | -------------------------------------------------------------------------------- /examples/livedebate/config.rb: -------------------------------------------------------------------------------- 1 | require './app' 2 | 3 | # Configuration to use when running within Sinatra 4 | project_path = Sinatra::Application.root 5 | 6 | # HTTP paths 7 | http_path = '/' 8 | http_stylesheets_path = '/stylesheets' 9 | http_images_path = '/images' 10 | http_javascripts_path = '/javascripts' 11 | 12 | # File system locations 13 | css_dir = File.join 'public', 'stylesheets' 14 | sass_dir = File.join 'stylesheets' 15 | images_dir = File.join 'public', 'images' 16 | javascripts_dir = File.join 'public', 'javascripts' 17 | 18 | # Syntax preference 19 | preferred_syntax = :scss 20 | 21 | # Determine whether Compass generates relative or absolute paths 22 | relative_assets = false 23 | 24 | # Determines whether line comments should be added to compiled css for easier debugging 25 | line_comments = false 26 | 27 | # CSS output style - :nested, :expanded, :compact, or :compressed 28 | output_style = :expanded 29 | 30 | # Learn more: http://beta.compass-style.org/help/tutorials/configuration-reference/ 31 | -------------------------------------------------------------------------------- /examples/livedebate/config.ru: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'sinatra' 3 | require './app' 4 | 5 | run LiveDebate 6 | -------------------------------------------------------------------------------- /examples/livedebate/lib/swarm_api.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'httparty' 3 | 4 | class SwarmApi 5 | include HTTParty 6 | base_uri 'http://collector.swarmize.com' 7 | attr_reader :token 8 | 9 | def initialize(token) 10 | @token = token 11 | end 12 | 13 | def send(data) 14 | path = "/swarms/#{token}.json" 15 | 16 | response = self.class.post(path, {body: data.to_json}) 17 | 18 | puts "Response #{response.code} #{response.message}: 19 | #{response.body}" 20 | end 21 | end 22 | 23 | -------------------------------------------------------------------------------- /examples/livedebate/public/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/examples/livedebate/public/images/.gitkeep -------------------------------------------------------------------------------- /examples/livedebate/public/images/dc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/examples/livedebate/public/images/dc.jpg -------------------------------------------------------------------------------- /examples/livedebate/public/images/em.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/examples/livedebate/public/images/em.jpg -------------------------------------------------------------------------------- /examples/livedebate/public/images/nc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/examples/livedebate/public/images/nc.jpg -------------------------------------------------------------------------------- /examples/livedebate/public/javascripts/application.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | // Open external links in a new window 4 | $(".feedback-buttons").hide(); 5 | 6 | $(".next-button").click(function(e) { 7 | $(".setup").hide(); 8 | $("h1").hide(); 9 | $(".feedback-buttons").show(); 10 | e.preventDefault(); 11 | }); 12 | 13 | $(".feedback-button").click(function(e) { 14 | if(!$(this).hasClass('disabled')) { 15 | clickVisuals(this); 16 | $("input#feedback").val(this.id); 17 | $.post( $("form#debate").attr('action'), 18 | $("form#debate").serialize()); 19 | } 20 | e.preventDefault(); 21 | }); 22 | 23 | }); 24 | 25 | function clickVisuals(el) { 26 | $(".feedback-button").animate({'opacity': 0.5},200); 27 | 28 | var top = $(el).offset().top + 20 29 | //var left = ($(el).width() / 2) - 20; 30 | var left = 0; 31 | 32 | $("body").append("
") 33 | $(".tick").css('top', top+"px").css('left', left + 'px').fadeIn(); 34 | 35 | $(".feedback-button").addClass('disabled'); 36 | setTimeout(fadeUpLinks, 200); 37 | } 38 | 39 | function fadeUpLinks(el) { 40 | $(".tick").fadeOut(function() { $(this).remove(); } ); 41 | $(".feedback-button").animate({'opacity': 1},200); 42 | $(".feedback-button").fadeTo(1); 43 | $(".feedback-button").removeClass('disabled'); 44 | } 45 | -------------------------------------------------------------------------------- /examples/livedebate/views/index.haml: -------------------------------------------------------------------------------- 1 | %h1 theguardian.com Prime Ministerial Debate Feedback Machine 2 | 3 | %form{:action => "/endpoint", :method => "post", :id => "debate"} 4 | .setup 5 | .form-el 6 | %label How do you plan to vote in the general election? 7 | %select{:name => "how_do_you_plan_to_vote_at_the_next_general_election"} 8 | %option{} - 9 | %option{:value => "labour"} Labour 10 | %option{:value => "conservative"} Conservative 11 | %option{:value => "liberal_democrat"} Liberal Democrats 12 | %option{:value => "green"} Green 13 | %option{:value => "ukip"} UKIP 14 | %option{:value => "other"} Other 15 | 16 | .form-el 17 | %label What is your postcode? 18 | %input{:name => "what_s_your_postcode"} 19 | 20 | %a.next-button{:href => "#"} GO! 21 | 22 | 23 | .feedback-buttons 24 | %h2 Tap their face when they say something you agree with. 25 | 26 | %input{:type => "hidden", :name => "who_did_you_just_agree_with", :id => "feedback"} 27 | %a.feedback-button{:id => "david_cameron", :href => "#"} 28 | %span Conservative 29 | %a.feedback-button{:id => "ed_miliband", :href => "#"} 30 | %span Labour 31 | %a.feedback-button{:id => "nick_clegg", :href => "#"} 32 | %span Liberal Democrat 33 | 34 | .tick ✓ 35 | 36 | -------------------------------------------------------------------------------- /examples/livedebate/views/layout.haml: -------------------------------------------------------------------------------- 1 | !!! 5 2 | %html{html_attrs} 3 | %head 4 | %meta{:'http-equiv' => "Content-Type", :content => "text/html; charset=utf-8"} 5 | %meta{:name => "lang", :content => "en"} 6 | %meta{:name => "viewport", :content => "width=device-width, initial-scale=1"} 7 | %title Live Debate 8 | %link{:href=>'/stylesheets/application.css', :media => "screen", :rel => 'stylesheet', :type => "text/css"} 9 | %script{:type => "text/javascript", :src => "/javascripts/jquery.min.js"} 10 | %script{:type => "text/javascript", :src => "/javascripts/application.js"} 11 | %body 12 | = yield 13 | -------------------------------------------------------------------------------- /examples/mapdemo-backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /examples/mapdemo-backend/README.md: -------------------------------------------------------------------------------- 1 | # Swarmize Retrieval API demo: using a backend 2 | 3 | This demonstration uses a simple node.js backend to obfuscate the Swarm's API key, and also filter the fields being returned to the front-end. It illustrates how relatively simple a backend needs to be to do so, and also how processing/manipulation of data JSON can be offloaded to the backend. 4 | 5 | ## Installation 6 | 7 | Assuming you have node already installed, from the application directory, run `npm install`. This will install all appropriate modules. 8 | 9 | ## Running 10 | 11 | From the application directory: 12 | 13 | npm start 14 | 15 | And then you'll be able to visit the map at: 16 | 17 | http://localhost:5000/?mapid=guardian.jl1hn9jp 18 | 19 | 20 | ## Notes 21 | 22 | If you'd like to see the returned JSON, you can do so at 23 | 24 | http://localhost:5000/data 25 | 26 | Compare this to the data submitted to 'mapdemo-standalone' direct from the Retrieval API to see how a straightforward backend can improve the relative security of your application based on the Swarmize Retreival API. Note how we've removed the `howYouVoteString` function from the front-end Javascript, and are doing all processing within node. -------------------------------------------------------------------------------- /examples/mapdemo-backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mapdemo-backend", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "backend.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node backend.js" 9 | }, 10 | "author": "", 11 | "license": "BSD-2-Clause", 12 | "dependencies": { 13 | "express": "~4.4.5", 14 | "request": "~2.36.0", 15 | "underscore": "~1.7.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/mapdemo-standalone/README.md: -------------------------------------------------------------------------------- 1 | # Swarmize Retrieval API demo: standalone access 2 | 3 | This demonstration shows how simple access to the Swarmize Retrieval API can be. Because we have no cross-origin protection, you can simply make requests directly from a front-end. 4 | 5 | ## Running 6 | 7 | Serve "index.html" from a webserver of your choice. For instance, in the project directory: 8 | 9 | python -m SimpleHTTPServer 10 | 11 | or using node's `http-server` 12 | 13 | http-server 14 | 15 | And then you'll be able to visit the map at: 16 | 17 | http://localhost:8000/index.html?mapid=guardian.jl1hn9jp 18 | 19 | (or whatever port the server is running on). 20 | 21 | ## Notes 22 | 23 | If you'd like to see the returned JSON, you can do so at 24 | 25 | http://api.swarmize.com/swarms/rycadjgp/entirety?format=geojson&geo_json_point_key=what_s_your_postcode_lonlat&ap_key=35c58cae15301cfa 26 | 27 | As you can see, *all* fields from the Swarm are returned in full - and your API key is exposed in public. This may not be an issue depending on your dataset, but the deliberate insecurity of this should be encouragement to use a straightforward backend where appropraite. 28 | -------------------------------------------------------------------------------- /geocoder/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /geocoder/README.md: -------------------------------------------------------------------------------- 1 | # Swarmize geocoder 2 | 3 | This utility acts as the geocoding endpoint for the Swarmize processing pipeline. It uses the MapQuest Open API for geocoding. 4 | 5 | ## Installation 6 | 7 | Assuming you have node already installed, from the application directory, run `npm install`. This will install all appropriate modules. 8 | 9 | ## Running 10 | 11 | From the application directory: 12 | 13 | npm start 14 | 15 | And then you'll be able to visit the sample index page at: 16 | 17 | http://localhost:5000/ 18 | 19 | ## Notes 20 | 21 | `geocoder` is the entirety of the tool. It receives data in and either returns a JSON object representing the lat/lon of the top match from Mapquest, or returns HTTP 204 (No Content) when nothing can be found. -------------------------------------------------------------------------------- /geocoder/eb_name.txt: -------------------------------------------------------------------------------- 1 | Swarmize Geocoder -------------------------------------------------------------------------------- /geocoder/geocoder.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var request = require('request'); 3 | var bodyParser = require('body-parser') 4 | 5 | var app = express(); 6 | 7 | app.use(bodyParser.json()) 8 | 9 | app.post("/geocode", function(req,res) { 10 | 11 | var output = req.body; 12 | 13 | var fieldToGeocode = req.query.field; 14 | var valueToGeocode = req.body[fieldToGeocode]; 15 | 16 | request('http://open.mapquestapi.com/nominatim/v1/search.php?format=json&limit=1&q='+valueToGeocode, function (error, response, body) { 17 | if (!error && response.statusCode == 200) { 18 | var json = JSON.parse(body); 19 | if(json.length > 0) { 20 | // we only care about the first one. 21 | var lat = json[0].lat; 22 | var lon = json[0].lon; 23 | 24 | var latLonFieldName = fieldToGeocode + "_lonlat"; 25 | 26 | output[latLonFieldName] = [parseFloat(lon),parseFloat(lat)]; 27 | 28 | res.json(output); 29 | } else { 30 | res.send(204); 31 | } 32 | } 33 | }) 34 | }); 35 | 36 | var port = Number(process.env.PORT || 5000); 37 | 38 | var server = app.listen(port, function() { 39 | console.log("Geocoder running on port " + port); 40 | }); 41 | -------------------------------------------------------------------------------- /geocoder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swarmize-geocoder", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "geocoder.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node geocoder.js" 9 | }, 10 | "author": "", 11 | "license": "BSD-2-Clause", 12 | "dependencies": { 13 | "express": "~4.4.5", 14 | "request": "~2.36.0", 15 | "body-parser": "~1.6.5" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /geocoder/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PACKAGE_FILENAME=$1 4 | DIRECTORY=$2 5 | 6 | cd .. 7 | 8 | git archive -o ${DIRECTORY}/${PACKAGE_FILENAME} master:${DIRECTORY} 9 | -------------------------------------------------------------------------------- /processor/app/Global.scala: -------------------------------------------------------------------------------- 1 | import lib.{Activity, ActivityDispatcher, Decider} 2 | import play.api.{Application, GlobalSettings, Logger} 3 | import swarmize.aws.SimpleWorkflowConfig 4 | 5 | 6 | object Global extends GlobalSettings { 7 | 8 | override def onStart(app: Application) { 9 | Logger.info("On start") 10 | 11 | Activity.registerAll() 12 | SimpleWorkflowConfig.registerWorkflow() 13 | 14 | Decider.run() 15 | ActivityDispatcher.run() 16 | 17 | } 18 | 19 | override def onStop(app: Application) { 20 | Logger.info("On stop") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /processor/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api._ 4 | import play.api.mvc._ 5 | import swarmize.UniqueId 6 | 7 | object Application extends Controller { 8 | 9 | def index = Action { 10 | Ok("swarmize: processor") 11 | } 12 | 13 | def newtoken() = Action { 14 | Ok(UniqueId.generateSwarmToken) 15 | } 16 | } -------------------------------------------------------------------------------- /processor/app/controllers/Management.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import lib.Elasticsearch 4 | import play.api.mvc.{Controller, Action} 5 | 6 | object Management extends Controller { 7 | def healthCheck() = Action { 8 | val resp = Elasticsearch.client.admin().indices().prepareStats().get() 9 | 10 | Ok(s"All good: $resp") 11 | } 12 | } -------------------------------------------------------------------------------- /processor/app/controllers/Swarms.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException 4 | import play.api.libs.json.{JsArray, JsResultException, JsString, Json} 5 | import play.api.mvc._ 6 | import swarmize.json.SwarmDefinition 7 | import swarmize.{SwarmTable, UniqueId} 8 | 9 | object Swarms extends Controller { 10 | 11 | def returnAll() = Action { 12 | val allKeys = SwarmTable.readAllTokens() 13 | 14 | val urls = allKeys.map(t => routes.Swarms.get(t).url).map(JsString) 15 | Ok(JsArray(urls)) 16 | } 17 | 18 | def get(token: String) = Action { request => 19 | val defn = SwarmTable.get(token) 20 | 21 | defn.map(c => Ok(Json.toJson(c.definition))) getOrElse NotFound(s"No swarm with token $token found") 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /processor/app/lib/Activities.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import com.amazonaws.services.simpleworkflow.model.ActivityType 4 | import swarmize.Swarm 5 | import swarmize.aws.SimpleWorkflowConfig 6 | import swarmize.json.SubmittedData 7 | 8 | import scala.concurrent.Future 9 | 10 | trait Activity { 11 | def name: String 12 | def version: String 13 | 14 | def activityType = new ActivityType().withName(name).withVersion(version) 15 | 16 | def process(r: SubmittedData): Future[SubmittedData] 17 | } 18 | 19 | 20 | object Activity { 21 | 22 | val all = ExternalActivity.all ::: StoreInElasticsearchActivity :: Nil 23 | 24 | val allTypes = all map (_.activityType) 25 | 26 | val lookupByType = all.map(a => a.activityType -> a).toMap.withDefault( 27 | t => sys.error("cannot process activity type of " + t) 28 | ) 29 | 30 | val lookupByName = all.map(a => a.name -> a).toMap.withDefault( 31 | n => sys.error("cannot process activity name of " + n) 32 | ) 33 | 34 | def registerAll() { 35 | allTypes.foreach(SimpleWorkflowConfig.createActivityIfNeeded) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /processor/app/lib/Elasticsearch.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import com.amazonaws.services.ec2.model.{DescribeInstancesRequest, Filter} 4 | import org.elasticsearch.client.transport.TransportClient 5 | import org.elasticsearch.common.settings.ImmutableSettings 6 | import org.elasticsearch.common.transport.InetSocketTransportAddress 7 | import play.api.Logger 8 | import swarmize.aws.AWS 9 | 10 | object Elasticsearch { 11 | 12 | lazy val discoveredElasticsearchHosts = { 13 | import scala.collection.JavaConversions._ 14 | val possibleHosts = AWS.EC2.describeInstances( 15 | new DescribeInstancesRequest().withFilters( 16 | new Filter("tag:Name", List("swarmize-elasticsearch")) 17 | )) 18 | possibleHosts.getReservations.flatMap(_.getInstances).map(_.getPublicDnsName) 19 | } 20 | 21 | private lazy val settings = ImmutableSettings.settingsBuilder() 22 | .put("cluster.name", "swarmize") 23 | .build() 24 | 25 | lazy val client = { 26 | val hosts = discoveredElasticsearchHosts map (new InetSocketTransportAddress(_, 9300)) 27 | 28 | if (hosts.isEmpty) { 29 | sys.error("could not find the elasticsearch hosts") 30 | } 31 | 32 | Logger.info("Connecting to elasticsearch cluster at " + discoveredElasticsearchHosts) 33 | new TransportClient(settings) 34 | .addTransportAddresses(hosts: _*) 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /processor/app/lib/ExternalActivity.scala: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import play.api.libs.ws.WS 4 | import swarmize.{ClassLogger, Swarm, FieldTypes} 5 | import swarmize.json.SubmittedData 6 | 7 | import scala.concurrent.Future 8 | import scala.concurrent.ExecutionContext.Implicits.global 9 | 10 | 11 | class ExternalActivity 12 | ( 13 | override val name: String, 14 | endpoint: String 15 | ) extends Activity with ClassLogger { 16 | 17 | override def version: String = "1" 18 | 19 | override def process(r: SubmittedData): Future[SubmittedData] = { 20 | log.info(s"${r.submissionId} $name running") 21 | 22 | val swarm = Swarm.findByToken(r.swarmToken).get 23 | val fields = swarm.fields.filter(_.processors.exists(_.id == name)) 24 | .map(_.codeName) 25 | .map("field" -> _) 26 | 27 | import play.api.Play.current 28 | 29 | log.info(s"${r.submissionId}: invoking $endpoint with $fields") 30 | 31 | WS.url(endpoint) 32 | .withQueryString(fields :_*) 33 | .post(r.data) 34 | .map { response => 35 | log.info(s"${r.submissionId}: result is ${response.status} ${response.statusText}") 36 | 37 | response.status match { 38 | case 200 => 39 | r.copy(data = response.json) 40 | 41 | case 204 => 42 | r 43 | 44 | case other => 45 | sys.error(s"lookup failed: ${response.status} ${response.statusText} => ${response.body}") 46 | 47 | } 48 | 49 | } 50 | 51 | } 52 | 53 | } 54 | 55 | 56 | object ExternalActivity { 57 | val all = FieldTypes.processors map { p => 58 | new ExternalActivity(p.id, p.endpoint) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /processor/conf/application-logger.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | processor 4 | 5 | 6 | 7 | 8 | logs/processor.log 9 | 10 | 11 | logs/processor.log.%d{yyyy-MM-dd}.gz 12 | 7 13 | 14 | 15 | 16 | %date [%thread{10}] %-5level %logger{20} - %msg%n%xException{3} 17 | 18 | 19 | 20 | 21 | 22 | %coloredLevel %logger{15} - %message%n%xException{5} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /processor/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | GET / controllers.Application.index 6 | 7 | GET /newtoken controllers.Application.newtoken 8 | 9 | # see http://www.restapitutorial.com/lessons/httpmethods.html 10 | 11 | # get summary of all swarms 12 | GET /swarms controllers.Swarms.returnAll 13 | 14 | # get full detail of a swam 15 | GET /swarms/:token controllers.Swarms.get(token) 16 | 17 | GET /health-check controllers.Management.healthCheck 18 | 19 | 20 | -------------------------------------------------------------------------------- /processor/eb_name.txt: -------------------------------------------------------------------------------- 1 | Processor -------------------------------------------------------------------------------- /processor/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PACKAGE_FILENAME=$1 4 | DIRECTORY=$2 5 | 6 | # clear up stuff left behind from previous builds 7 | [ -d target/docker ] && rm -rf target/docker 8 | 9 | # build the zip 10 | cd .. 11 | 12 | sbt processor/docker:stage && \ 13 | cd ${DIRECTORY}/target/docker && \ 14 | zip -r ../../${PACKAGE_FILENAME} * 15 | 16 | 17 | -------------------------------------------------------------------------------- /processor/start_prod_processor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | aws --profile swarmize autoscaling update-auto-scaling-group \ 3 | --auto-scaling-group-name awseb-e-6bv6raqwgv-stack-AWSEBAutoScalingGroup-18Q0FOJPKDO5R \ 4 | --min-size 1 \ 5 | --desired-capacity 1 -------------------------------------------------------------------------------- /processor/stop_prod_processor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | aws --profile swarmize autoscaling update-auto-scaling-group \ 3 | --auto-scaling-group-name awseb-e-6bv6raqwgv-stack-AWSEBAutoScalingGroup-18Q0FOJPKDO5R \ 4 | --min-size 0 \ 5 | --desired-capacity 0 -------------------------------------------------------------------------------- /processor/test/ActivityRunOnce.scala: -------------------------------------------------------------------------------- 1 | import lib.ActivityDispatcher 2 | 3 | import scala.concurrent.Await 4 | import scala.concurrent.duration.Duration 5 | 6 | object ActivityRunOnce extends App { 7 | println("Running once!") 8 | 9 | Await.ready(ActivityDispatcher.runAsync(), atMost = Duration.Inf) 10 | 11 | println("COMPLETED!") 12 | 13 | sys.exit(0) 14 | 15 | } -------------------------------------------------------------------------------- /processor/test/DeciderRunOnce.scala: -------------------------------------------------------------------------------- 1 | import lib.Decider 2 | 3 | import scala.concurrent.Await 4 | import scala.concurrent.duration.Duration 5 | 6 | 7 | object DeciderRunOnce extends App { 8 | println("Running once!") 9 | 10 | Await.ready(Decider.runAsync(), atMost = Duration.Inf) 11 | 12 | println("COMPLETED!") 13 | 14 | sys.exit(0) 15 | } 16 | -------------------------------------------------------------------------------- /processor/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | processor-test 4 | 5 | 6 | 7 | 8 | 9 | %coloredLevel %logger{15} - %message%n%xException{5} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.6 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 2 | 3 | // The Play plugin 4 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.3") 5 | 6 | // native packager, to create docker images 7 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.7.4") 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /sentiment-endpoint/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /sentiment-endpoint/Procfile: -------------------------------------------------------------------------------- 1 | web: node sentiment-endpoint.js 2 | -------------------------------------------------------------------------------- /sentiment-endpoint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sentiment-endpoint", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "sentiment.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "BSD-2-Clause", 11 | "dependencies": { 12 | "express": "~4.4.5", 13 | "sentiment": "~1.0.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sentiment-endpoint/sentiment-endpoint.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var sentiment = require('sentiment'); 3 | var app = express(); 4 | 5 | app.get("/sentiment", function(req,res) { 6 | res.json(sentiment(req.query.input)); 7 | }); 8 | 9 | var port = Number(process.env.PORT || 5000); 10 | 11 | var server = app.listen(port, function() { 12 | console.log("Sentiment analysis running on port " + port); 13 | }); 14 | -------------------------------------------------------------------------------- /shared-lib/src/main/resources/field_types.json: -------------------------------------------------------------------------------- 1 | ../../../../swarmize-dot-com/field_types.json -------------------------------------------------------------------------------- /shared-lib/src/main/scala/swarmize/ClassLogger.scala: -------------------------------------------------------------------------------- 1 | package swarmize 2 | 3 | import play.api.Logger 4 | 5 | trait ClassLogger { 6 | protected lazy val log = Logger(getClass) 7 | 8 | 9 | protected final def logAround[T](msg: String)(block: => T) = { 10 | log.info(msg + "...") 11 | val result = block 12 | log.info(msg + " complete") 13 | result 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /shared-lib/src/main/scala/swarmize/FieldTypes.scala: -------------------------------------------------------------------------------- 1 | package swarmize 2 | 3 | import com.google.common.io.Resources 4 | import play.api.libs.json.Json 5 | import swarmize.json.FieldTypeJson 6 | 7 | object FieldTypes { 8 | 9 | lazy val json = Json.parse(Resources.toByteArray(Resources.getResource("field_types.json"))) 10 | lazy val types = json.as[Map[String, FieldTypeJson]].withDefault { t => 11 | sys.error("I don't know how to deal with an underlying field type of " + t) 12 | } 13 | 14 | def apply(fieldTypeName: String): FieldTypeJson = types(fieldTypeName) 15 | 16 | 17 | def processors = types.values.flatMap(_.processingSteps).toList 18 | } 19 | -------------------------------------------------------------------------------- /shared-lib/src/main/scala/swarmize/Swarm.scala: -------------------------------------------------------------------------------- 1 | package swarmize 2 | 3 | import java.util.concurrent.TimeUnit 4 | 5 | import com.google.common.cache.{CacheLoader, CacheBuilder} 6 | import swarmize.Swarm._ 7 | 8 | 9 | case class Swarm 10 | ( 11 | token: String, 12 | definition: json.SwarmDefinition 13 | ) { 14 | 15 | def name = definition.name 16 | def description = definition.description 17 | 18 | lazy val fields: List[SwarmField] = definition.fields.map(SwarmField.apply) 19 | 20 | lazy val derivedFields: List[SwarmField] = fields.flatMap(_.derivedFields) 21 | 22 | lazy val allFields = fields ::: derivedFields 23 | 24 | lazy val processors: List[String] = fields.flatMap(_.processors.map(_.id)) 25 | 26 | 27 | def status: Status = 28 | if (definition.closes_at.exists(_.isBeforeNow)) Closed 29 | else if (definition.opens_at.exists(_.isBeforeNow)) Open 30 | else Draft 31 | 32 | def isClosed = status == Closed 33 | 34 | def indexName = if (status == Draft) token + "_draft" else token 35 | 36 | } 37 | 38 | 39 | 40 | 41 | object Swarm { 42 | sealed trait Status 43 | case object Draft extends Status 44 | case object Open extends Status 45 | case object Closed extends Status 46 | 47 | private object loader extends CacheLoader[String, Option[Swarm]] { 48 | override def load(token: String): Option[Swarm] = SwarmTable.get(token) 49 | } 50 | 51 | private val cache = CacheBuilder.newBuilder() 52 | .expireAfterWrite(5, TimeUnit.SECONDS) 53 | .build[String, Option[Swarm]](loader) 54 | 55 | def findByToken(token: String): Option[Swarm] = cache.get(token) 56 | 57 | } 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /shared-lib/src/main/scala/swarmize/SwarmApiKeys.scala: -------------------------------------------------------------------------------- 1 | package swarmize 2 | 3 | import java.util.concurrent.TimeUnit 4 | 5 | import com.google.common.cache.{CacheBuilder, CacheLoader} 6 | import swarmize.aws.AWS 7 | 8 | object SwarmApiKeys { 9 | 10 | private object SwarmApiKeyTable { 11 | private val table = AWS.dynamodb.getTable("api_keys") 12 | 13 | def swarmTokenForApiKey(apiKey: String): Option[String] = { 14 | for { 15 | row <- Option(table.getItem("api_key", apiKey)) 16 | s <- Option(row.getString("swarm_token")) 17 | } yield s 18 | } 19 | } 20 | 21 | 22 | private object loader extends CacheLoader[String, Option[String]] { 23 | override def load(apiKey: String): Option[String] = { 24 | SwarmApiKeyTable.swarmTokenForApiKey(apiKey) 25 | } 26 | } 27 | 28 | private val cache = CacheBuilder.newBuilder() 29 | .expireAfterWrite(30, TimeUnit.SECONDS) 30 | .build[String, Option[String]](loader) 31 | 32 | 33 | def isValid(swarmToken: String, apiKey: String) = 34 | cache.get(apiKey).contains(swarmToken) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /shared-lib/src/main/scala/swarmize/SwarmSubmissionValidator.scala: -------------------------------------------------------------------------------- 1 | package swarmize 2 | 3 | import play.api.data.validation.ValidationError 4 | import play.api.libs.json._ 5 | 6 | object SwarmSubmissionValidator { 7 | 8 | private def extractErrorMessages(ex: Seq[(JsPath, Seq[ValidationError])]): Seq[String] = 9 | ex.flatMap { 10 | case (_, errors) => errors.map(_.message) 11 | } 12 | 13 | def validated(swarm: Swarm, jsObject: JsObject): JsObject = { 14 | 15 | val suppliedFields = jsObject.fields.toMap 16 | 17 | val validatedFields: Seq[(String, JsResult[JsValue])] = swarm.allFields.map { f => 18 | f.codeName -> f.validate(suppliedFields.get(f.codeName)) 19 | } 20 | 21 | val validationFailures: Seq[String] = validatedFields.collect { 22 | case (fieldName, JsError(ex)) => s"$fieldName: ${extractErrorMessages(ex) mkString ", "}" 23 | } 24 | 25 | if (validationFailures.nonEmpty) 26 | sys.error( 27 | s"""Your submission is invalid: 28 | | * ${validationFailures mkString "\n * "}""".stripMargin ) 29 | 30 | val fieldValues = validatedFields.collect { 31 | case (fieldName, JsSuccess(fieldValue, _)) if fieldValue != JsNull => fieldName -> fieldValue 32 | } 33 | 34 | JsObject(fieldValues) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /shared-lib/src/main/scala/swarmize/SwarmTable.scala: -------------------------------------------------------------------------------- 1 | package swarmize 2 | 3 | import com.amazonaws.services.dynamodbv2.document.spec.ScanSpec 4 | import play.api.libs.json.Json 5 | import swarmize.aws.AWS 6 | import swarmize.json.SwarmDefinition 7 | 8 | import scala.collection.convert.wrapAll._ 9 | 10 | object SwarmTable { 11 | private val table = AWS.dynamodb.getTable("swarms") 12 | 13 | def get(token: String): Option[Swarm] = { 14 | for { 15 | r <- Option(table.getItem("token", token)) 16 | definition <- Option(r.getString("definition")) 17 | } yield { 18 | val defn = Json.parse(definition).as[SwarmDefinition] 19 | Swarm(token, defn) 20 | } 21 | 22 | } 23 | 24 | def readAllTokens(): List[String] = { 25 | table.scan(new ScanSpec().withAttributesToGet("token")) 26 | .map(_.getString("token")) 27 | .toList 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /shared-lib/src/main/scala/swarmize/UniqueId.scala: -------------------------------------------------------------------------------- 1 | package swarmize 2 | 3 | import java.security.SecureRandom 4 | 5 | import org.apache.commons.codec.binary.Base64 6 | 7 | 8 | object UniqueId { 9 | private val random = new SecureRandom() 10 | 11 | private def generate(numBytes: Int = 16): String = { 12 | val randomBytes = new Array[Byte](numBytes) 13 | random.nextBytes(randomBytes) 14 | 15 | Base64.encodeBase64URLSafeString(randomBytes) 16 | } 17 | 18 | def generateSwarmToken = generate(5) 19 | 20 | def generateSubmissionId = generate(16) 21 | } 22 | -------------------------------------------------------------------------------- /shared-lib/src/main/scala/swarmize/aws/swf/SwfDecision.scala: -------------------------------------------------------------------------------- 1 | package swarmize.aws.swf 2 | 3 | import com.amazonaws.services.simpleworkflow.model._ 4 | import swarmize.aws.SimpleWorkflowConfig 5 | 6 | object SwfDecision { 7 | def failWorkflowExecution(reason: String) = 8 | new Decision() 9 | .withDecisionType(DecisionType.FailWorkflowExecution) 10 | .withFailWorkflowExecutionDecisionAttributes( 11 | new FailWorkflowExecutionDecisionAttributes() 12 | .withReason(reason) 13 | ) 14 | 15 | 16 | def completeWorkflowExecution(result: String) = 17 | new Decision() 18 | .withDecisionType(DecisionType.CompleteWorkflowExecution) 19 | .withCompleteWorkflowExecutionDecisionAttributes( 20 | new CompleteWorkflowExecutionDecisionAttributes() 21 | .withResult(result) 22 | ) 23 | 24 | 25 | def scheduleActivityTask(activityType: ActivityType, activityId: String, input: String) = 26 | new Decision() 27 | .withDecisionType(DecisionType.ScheduleActivityTask) 28 | .withScheduleActivityTaskDecisionAttributes( 29 | new ScheduleActivityTaskDecisionAttributes() 30 | .withActivityType(activityType) 31 | .withActivityId(activityId) 32 | .withInput(input) 33 | .withTaskList(SimpleWorkflowConfig.defaultTaskList) 34 | ) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /shared-lib/src/main/scala/swarmize/json/FieldTypeJson.scala: -------------------------------------------------------------------------------- 1 | package swarmize.json 2 | 3 | import play.api.libs.json.{JsObject, Json} 4 | 5 | case class ProcessingStepJson 6 | ( 7 | id: String, 8 | endpoint: String, 9 | derives: Map[String, String] 10 | ) 11 | 12 | case class FieldTypeJson 13 | ( 14 | display_name: String, 15 | archetype: String, 16 | has_possible_values: Option[Boolean] = None, 17 | max_values: Option[Int] = None, 18 | process: Option[List[ProcessingStepJson]] = None 19 | ) { 20 | def processingSteps = process getOrElse Nil 21 | } 22 | 23 | 24 | object ProcessingStepJson { 25 | implicit val fmt = Json.format[ProcessingStepJson] 26 | } 27 | 28 | object FieldTypeJson { 29 | implicit val fmt = Json.format[FieldTypeJson] 30 | } 31 | -------------------------------------------------------------------------------- /shared-lib/src/main/scala/swarmize/json/PlayJsonIsoDateFormat.scala: -------------------------------------------------------------------------------- 1 | package swarmize.json 2 | 3 | import play.api.libs.json._ 4 | import org.joda.time.DateTime 5 | import scala.util.Try 6 | import org.joda.time.format.ISODateTimeFormat 7 | import play.api.libs.json.JsString 8 | import play.api.libs.json.JsSuccess 9 | import play.api.libs.json.JsNumber 10 | import play.api.data.validation.ValidationError 11 | 12 | object PlayJsonIsoDateFormat extends Format[DateTime] { 13 | def writes(d: org.joda.time.DateTime): JsValue = JsString(d.toString) 14 | 15 | def reads(json: JsValue): JsResult[DateTime] = json match { 16 | case JsNumber(num) => JsSuccess(new DateTime(num.toLong)) 17 | case JsString(s) => Try { 18 | JsSuccess(ISODateTimeFormat.dateTimeParser().parseDateTime(s)) 19 | }.recover { 20 | case e: IllegalArgumentException => 21 | JsError(ValidationError("validate.error.expected.date.isoformat", s)) 22 | }.get 23 | 24 | case other => 25 | JsError(ValidationError("validate.error.expected.date")) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /shared-lib/src/main/scala/swarmize/json/SwarmDefinition.scala: -------------------------------------------------------------------------------- 1 | package swarmize.json 2 | 3 | import org.joda.time.DateTime 4 | import play.api.libs.json.{JsValue, Json} 5 | 6 | 7 | 8 | case class JsonSwarmField 9 | ( 10 | field_name: String, 11 | field_name_code: String, 12 | field_type: String, 13 | possible_values: Option[Map[String, String]] = None, 14 | sample_value: Option[String] = None, 15 | compulsory: Boolean, 16 | allow_other: Option[Boolean] = None 17 | ) 18 | 19 | 20 | case class SwarmDefinition 21 | ( 22 | name: String, 23 | description: String, 24 | 25 | fields: List[JsonSwarmField], 26 | opens_at: Option[DateTime], 27 | closes_at: Option[DateTime] 28 | ) { 29 | def toJson = SwarmDefinition toJson this 30 | } 31 | 32 | 33 | 34 | 35 | object SwarmDefinition { 36 | implicit val dateFormat = PlayJsonIsoDateFormat 37 | implicit val fieldJsonFormat = Json.format[JsonSwarmField] 38 | implicit val definitionJsonFormat = Json.format[SwarmDefinition] 39 | 40 | def toJson(d: SwarmDefinition): JsValue = Json toJson d 41 | } -------------------------------------------------------------------------------- /shared-lib/src/test/scala/swarmize/aws/swf/SwfHistoryEventTest.scala: -------------------------------------------------------------------------------- 1 | package swarmize.aws.swf 2 | 3 | import com.amazonaws.services.simpleworkflow.model.{HistoryEvent, WorkflowExecutionStartedEventAttributes} 4 | import org.joda.time.DateTime 5 | import org.scalatest._ 6 | 7 | class SwfHistoryEventTest extends FlatSpec with Matchers with OptionValues { 8 | "SwfHistoryEvent" should "be able to represent a WorkflowExecutionStarted event" in { 9 | val executionStarted = new HistoryEvent() 10 | .withEventType("WorkflowExecutionStarted") 11 | .withEventId(1L) 12 | .withEventTimestamp(new DateTime(1972, 7, 20, 11, 0).toDate) 13 | .withWorkflowExecutionStartedEventAttributes( 14 | new WorkflowExecutionStartedEventAttributes() 15 | .withInput("hello") 16 | ) 17 | 18 | val ev = SwfHistoryEvent.parse(executionStarted) 19 | 20 | ev.getClass shouldBe classOf[WorkflowExecutionStarted] 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /shared-lib/src/test/scala/swarmize/json/SubmittedDataTest.scala: -------------------------------------------------------------------------------- 1 | package swarmize.json 2 | 3 | import org.scalatest._ 4 | import play.api.libs.json.Json 5 | import swarmize.{TestSwarms, Swarm} 6 | 7 | class SubmittedDataTest extends FlatSpec with Matchers with OptionValues { 8 | 9 | "Submitted Data" should "wrap submitted submission" in { 10 | val rawSubmission = """ 11 | {"user_key":12345,"timestamp":1407228014,"intent":"green", 12 | "feedback": "Mr King","postcode":"N1 9GU","ip":"10.0.0.1"} 13 | """ 14 | 15 | val submissionJson = Json.parse(rawSubmission) 16 | 17 | val wrapped = SubmittedData.wrap(submissionJson, TestSwarms.simpleSurvey) 18 | 19 | val id = wrapped.submissionId 20 | 21 | val string = Json.toJson(wrapped).toString() 22 | 23 | string shouldBe 24 | s"""{"submissionId":"$id","swarmName":"A very simple survey","swarmToken":"test-simple", 25 | |"swarmStatus":"Draft", 26 | |"processingSteps":[], 27 | |"data":{"user_key":12345,"timestamp":1407228014,"intent":"green","feedback":"Mr King","postcode":"N1 9GU","ip":"10.0.0.1"}}""" 28 | .stripMargin.replace("\n", "") 29 | 30 | 31 | Json.parse(string).as[SubmittedData] shouldBe wrapped 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /shared-lib/src/test/scala/swarmize/json/SwarmDefinitionJsonTest.scala: -------------------------------------------------------------------------------- 1 | package swarmize.json 2 | 3 | import org.scalatest._ 4 | import swarmize.TestSwarms 5 | 6 | class SwarmDefinitionJsonTest extends FlatSpec with Matchers with OptionValues { 7 | 8 | 9 | 10 | 11 | "SwarmDefinition" should "be serializable from json" in { 12 | val definition = TestSwarms.broadbandSurveyJson.as[SwarmDefinition] 13 | 14 | definition.name shouldBe "Guardian Broadband Survey 2013" 15 | definition.description shouldBe "With your help, the Guardian is creating an up-to-date broadband map of Britain, showing advertised versus real speeds. We want to highlight the best and worst-served communities, and bring attention to the broadband blackspots." 16 | 17 | definition.opens_at.value.getMillis shouldBe 1438513980000L 18 | definition.closes_at shouldBe None 19 | 20 | definition.fields.size shouldBe 10 21 | 22 | val field = definition.fields(0) 23 | 24 | field.field_name shouldBe "Do you have Internet at home?" 25 | field.compulsory shouldBe true 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /swarmize-dot-com/.ebextensions/packages.config: -------------------------------------------------------------------------------- 1 | packages: 2 | yum: 3 | postgresql-devel: [] 4 | patch: [] 5 | libffi-devel: [] 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/.env.example: -------------------------------------------------------------------------------- 1 | ELASTICSEARCH_HOST=localhost 2 | GOOGLE_CLIENT_ID=xxx 3 | GOOGLE_CLIENT_SECRET=xxx 4 | AWS_ACCESS_KEY_ID=xxx 5 | AWS_SECRET_ACCESS_KEY=xxx 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/*.log 16 | /tmp 17 | 18 | # Ignore deploy config 19 | deploy.json 20 | 21 | # Ignore local .env specifics 22 | .env 23 | 24 | # Ignore simplecov output 25 | coverage 26 | 27 | -------------------------------------------------------------------------------- /swarmize-dot-com/.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /swarmize-dot-com/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '4.0.3' 4 | gem 'pg' 5 | gem 'sass-rails', '~> 4.0.0' 6 | gem 'haml' 7 | gem 'uglifier', '>= 1.3.0' 8 | gem 'therubyracer', platforms: :ruby 9 | gem 'jquery-rails' 10 | gem 'jbuilder', '~> 1.2' 11 | gem 'rspec-rails' 12 | gem 'hashie' 13 | gem 'pg_search' 14 | gem 'aws-sdk', '~> 1.0' 15 | gem 'elasticsearch' 16 | gem 'typhoeus' 17 | gem 'dotenv' 18 | gem 'omniauth-google-oauth2' 19 | gem 'will_paginate', '~> 3.0' 20 | gem 'will_paginate-bootstrap' 21 | gem "paranoia", "~> 2.0" 22 | gem 'aws-ses', '~> 0.5.0', require: 'aws/ses' 23 | gem 'kramdown' 24 | gem "non-stupid-digest-assets" 25 | 26 | group :doc do 27 | # bundle exec rake doc:rails generates the API under doc/api. 28 | gem 'sdoc', require: false 29 | end 30 | 31 | gem 'faker' # will need to be removed later, used for creating fake data 32 | gem 'factory_girl_rails' 33 | 34 | # Use unicorn as the app server 35 | gem 'unicorn' 36 | 37 | group :development do 38 | gem 'thin' 39 | gem 'fog' 40 | gem 'awesome_print' 41 | end 42 | 43 | group :test do 44 | gem 'timecop' 45 | gem 'guard-rspec' 46 | gem 'simplecov' 47 | end 48 | -------------------------------------------------------------------------------- /swarmize-dot-com/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Swarmize::Application.load_tasks 7 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/app/assets/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/app/assets/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/app/assets/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/app/assets/images/.keep -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/app/assets/images/favicon.ico -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require underscore-min 16 | //= require backbone-min 17 | //= require parsley-config 18 | //= require parsley.min 19 | //= require d3.min 20 | //= require c3.min 21 | //= require rickshaw.min 22 | //= require moment.min 23 | //= require_tree . 24 | 25 | $(document).ready(function() { 26 | $('.disabled a').click(function(e) { e.preventDefault(); return false }); 27 | 28 | }); 29 | 30 | function parseTimestamp(string) { 31 | return moment(string); 32 | } 33 | 34 | function formatTimestamp(timestamp) { 35 | return moment(timestamp).format('D MMMM YYYY HH:mm:ss'); 36 | } 37 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/javascripts/form_element.js: -------------------------------------------------------------------------------- 1 | var FormElement = { 2 | onDragStart: function(event) { 3 | if($(event.toElement).find('.move-this-element').is(FormElement.dragTarget)) { 4 | console.log("Dragged by handle"); 5 | FormElement.draggedElement = event.toElement; 6 | event.dataTransfer.effectAllowed = 'move'; 7 | event.dataTransfer.dropEffect = 'move'; 8 | event.dataTransfer.setData('text/plain', 'formElement'); 9 | 10 | window.beingDraggedType = 'formElement'; 11 | window.beingDraggedText = "Move to here"; 12 | } else { 13 | console.log("Dragged by other"); 14 | event.preventDefault(); 15 | } 16 | }, 17 | 18 | setupDraggableFields: function() { 19 | FormElement.dragTarget = false; 20 | $(".form-element").attr('draggable', 'true'); 21 | $(".form-element .move-this-element").on('mousedown', function(e) { 22 | FormElement.dragTarget = e.target; 23 | }); 24 | $('.form-element').attr('ondragstart', 'FormElement.onDragStart(event)'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/javascripts/jquery.iecors.js: -------------------------------------------------------------------------------- 1 | (function( jQuery ) { 2 | // Create the request object 3 | // (This is still attached to ajaxSettings for backward compatibility) 4 | jQuery.ajaxSettings.xdr = function() { 5 | return (window.XDomainRequest ? new window.XDomainRequest() : null); 6 | }; 7 | 8 | // Determine support properties 9 | (function( xdr ) { 10 | jQuery.extend( jQuery.support, { iecors: !!xdr }); 11 | })( jQuery.ajaxSettings.xdr() ); 12 | 13 | // Create transport if the browser can provide an xdr 14 | if ( jQuery.support.iecors ) { 15 | 16 | jQuery.ajaxTransport(function( s ) { 17 | var callback, 18 | xdr = s.xdr(); 19 | 20 | return { 21 | send: function( headers, complete ) { 22 | xdr.onload = function() { 23 | var headers = { 'Content-Type': xdr.contentType }; 24 | complete(200, 'OK', { text: xdr.responseText }, headers); 25 | }; 26 | 27 | // Apply custom fields if provided 28 | if ( s.xhrFields ) { 29 | xhr.onerror = s.xhrFields.error; 30 | xhr.ontimeout = s.xhrFields.timeout; 31 | } 32 | 33 | xdr.open( s.type, s.url ); 34 | 35 | // XDR has no method for setting headers O_o 36 | 37 | xdr.send( ( s.hasContent && s.data ) || null ); 38 | }, 39 | 40 | abort: function() { 41 | xdr.abort(); 42 | } 43 | }; 44 | }); 45 | } 46 | })( jQuery ); 47 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/javascripts/models/graph_data.js: -------------------------------------------------------------------------------- 1 | var GraphData = Backbone.Model.extend({ 2 | fetch: function() { 3 | var that = this; 4 | $.ajax({ 5 | type: 'GET', 6 | url: this.get('url'), 7 | success: function(data) { 8 | that.set('data', data); 9 | that.trigger('data-update'); 10 | } 11 | }); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/javascripts/models/results_data.js: -------------------------------------------------------------------------------- 1 | var ResultsData = Backbone.Model.extend(); 2 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/javascripts/palette_field.js: -------------------------------------------------------------------------------- 1 | var PaletteField = { 2 | setupDraggable: function() { 3 | $('.fields-palette .field').attr('draggable', 'true'); 4 | $('.fields-palette .field').attr('ondragstart', 'PaletteField.onDragPaletteStart(event)'); 5 | $('.fields-palette .field').attr('ondragend', 'PaletteField.onDragPaletteEnd(event)'); 6 | }, 7 | 8 | onDragPaletteStart: function(event) { 9 | var type = $(event.toElement).attr('id').replace("_dragger", ""); 10 | var text = $(event.toElement).text(); 11 | 12 | event.dataTransfer.setData('text/plain', type); 13 | window.beingDraggedType = type; 14 | window.beingDraggedText = text; 15 | }, 16 | 17 | onDragPaletteEnd: function(event) { 18 | $('.hover').removeClass('hover'); 19 | window.beingDraggedType = null; 20 | window.beingDraggedText = null; 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/javascripts/parsley-config.js: -------------------------------------------------------------------------------- 1 | window.ParsleyConfig = { 2 | validators: { 3 | postcode: { 4 | fn: function (value, requirement) { 5 | var fullPostcodeRegex = /^([A-Z][A-Z0-9]?[A-Z0-9]?[A-Z0-9]? {1,2}[0-9][A-Z0-9]{2})$/; 6 | var halfPostcodeRegex = /^(([A-Z][0-9])|([A-Z][A-Z0-9][A-Z])|([A-Z][A-Z][0-9][A-Z0-9]?))$/; 7 | if(value.trim().toUpperCase().match(fullPostcodeRegex) || value.trim().toUpperCase().match(halfPostcodeRegex)) { 8 | return true; 9 | } else { 10 | return false 11 | } 12 | }, 13 | priority: 32 14 | }, 15 | conditionalother: { 16 | fn: function (value, requirement) { 17 | var vals = $('input[name="'+requirement+'"]:checked, select[name="'+requirement+'"] option:selected').map(function() { 18 | return this.value; 19 | }); 20 | 21 | $.inArray('other', vals) 22 | if(($.inArray('other', vals) > -1) && value == '') { 23 | return false; 24 | } else { 25 | return true; 26 | } 27 | }, 28 | priority: 32 29 | } 30 | }, 31 | i18n: { 32 | en: { 33 | postcode: 'Please enter a valid UK postcode, eg "SW1A 1AA" or "SW1A"', 34 | conditionalother: 'Please supply an answer for \'other\'' 35 | } 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/javascripts/views/pie_graph.js: -------------------------------------------------------------------------------- 1 | var SwarmizePieGraph = Backbone.View.extend({ 2 | initialize: function() { 3 | this.listenTo(this.model, 'change', this.render); 4 | 5 | // set up some stuff on the screen 6 | var containerSelector = "#graph-container" + this.model.get('index'); 7 | $("#graphs").append("
"); 8 | $(containerSelector).append("

" + this.model.get('title') + "

"); 9 | $(containerSelector).append("
"); 10 | 11 | }, 12 | 13 | render: function() { 14 | console.log('rendering chart'); 15 | var that = this; 16 | if(this.chart) { 17 | console.log('updating chart'); 18 | this.chart.load({ 19 | json: that.model.get('data') 20 | }); 21 | } else { 22 | console.log('creating chart'); 23 | var that = this 24 | this.chart = c3.generate({ 25 | bindto: '#graph'+that.model.get('index'), 26 | data: { 27 | json: that.model.get('data'), 28 | type: 'pie' 29 | } 30 | }); 31 | } 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/assets/javascripts/views/results_table.js: -------------------------------------------------------------------------------- 1 | var ResultsTable = Backbone.View.extend({ 2 | initialize: function() { 3 | this.listenTo(this.model, 'change', this.render); 4 | }, 5 | 6 | render: function() { 7 | var that = this; 8 | if(this.model.has('rows')) { 9 | $("#results_table tbody tr").remove(); 10 | $.each(this.model.get('rows'), function(i,item) { 11 | var $row = $(''); 12 | $.each(that.model.get('fieldCodes'), function(j, code) { 13 | if(code == 'timestamp') { 14 | var rawTime = item[code]; 15 | var formattedTime = formatTimestamp(parseTimestamp(rawTime)); 16 | $row.append( 17 | $('').text(formattedTime) 18 | ); 19 | } else { 20 | $row.append( 21 | $('').text(item[code]) 22 | ); 23 | } 24 | }); 25 | $("#results_table tbody").append($row); 26 | }); 27 | } 28 | $("span.huge-number").text(this.model.get('count')); 29 | } 30 | }); 31 | 32 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/controllers/api_keys_controller.rb: -------------------------------------------------------------------------------- 1 | class ApiKeysController < ApplicationController 2 | before_filter :scope_to_swarm 3 | before_filter :check_user_has_permissions_on_swarm 4 | 5 | def index 6 | end 7 | 8 | def new 9 | end 10 | 11 | def create 12 | APIKey.create(@swarm.token, @current_user.email) 13 | flash[:success] = "Key created." 14 | redirect_to swarm_api_keys_path(@swarm) 15 | end 16 | 17 | def destroy 18 | APIKey.delete(params[:id]) 19 | flash[:success] = "Key deleted." 20 | redirect_to swarm_api_keys_path(@swarm) 21 | end 22 | 23 | private 24 | 25 | def scope_to_swarm 26 | @swarm = Swarm.find_by(token: params[:swarm_id]) 27 | end 28 | 29 | def check_user_has_permissions_on_swarm 30 | unless AccessPermission.can_alter?(@swarm, @current_user) 31 | flash[:error] = "You don't have permission to do that." 32 | redirect_to root_path 33 | end 34 | end 35 | 36 | end 37 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | before_filter :set_up_current_user 6 | 7 | def set_up_current_user 8 | if session[:user_id] 9 | @current_user = User.find(session[:user_id]) 10 | end 11 | end 12 | 13 | def check_for_user 14 | if !logged_in? 15 | store_location 16 | redirect_to login_path and return 17 | end 18 | end 19 | 20 | def check_for_admin 21 | if !logged_in? 22 | store_location 23 | redirect_to login_path and return 24 | end 25 | 26 | unless @current_user.is_admin? 27 | flash[:error] = "You don't have permission to do that." 28 | redirect_to root_path 29 | end 30 | end 31 | 32 | def logged_in? 33 | session[:user_id] != nil 34 | end 35 | 36 | def store_location 37 | if request.get? 38 | session[:return_to] = request.original_url 39 | else 40 | session[:return_to] = request.referer 41 | end 42 | end 43 | 44 | def redirect_back_or_default(default = root_url) 45 | if session[:return_to] 46 | redirect_url = session[:return_to] 47 | logger.debug("Redirect URL is #{redirect_url}") 48 | session.delete(:return_to) 49 | redirect_to(redirect_url) 50 | else 51 | redirect_to(default) 52 | end 53 | end 54 | 55 | end 56 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/controllers/case_studies_controller.rb: -------------------------------------------------------------------------------- 1 | class CaseStudiesController < ApplicationController 2 | def show 3 | # find file 4 | filename = File.join(Rails.root, 'public', 'case_studies', "#{params[:id]}.md") 5 | if File.exist?(filename) 6 | @body = Kramdown::Document.new(File.read(filename)).to_html 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /swarmize-dot-com/app/controllers/documentation_controller.rb: -------------------------------------------------------------------------------- 1 | class DocumentationController < ApplicationController 2 | before_filter :check_for_user 3 | 4 | def show 5 | # find file 6 | filename = File.join(Rails.root, 'public', 'documentation', "#{params[:id]}.md") 7 | if File.exist?(filename) 8 | @body = Kramdown::Document.new(File.read(filename)).to_html 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | def show 3 | if @current_user 4 | @user_swarms_draft = @current_user.swarms.draft.latest(5) 5 | @user_swarms_in_progress = @current_user.swarms.live.latest(5) 6 | @user_completed_swarms = @current_user.swarms.closed.latest(5) 7 | @swarms_in_progress = Swarm.live.latest(5) 8 | @latest_complete_swarms = Swarm.closed.latest(5) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/controllers/oembed_controller.rb: -------------------------------------------------------------------------------- 1 | class OembedController < ApplicationController 2 | def show 3 | if params[:format] == 'xml' 4 | render :status => 501, :text => 'Format not supported' 5 | return 6 | end 7 | 8 | url = params[:url] 9 | # check URL matches scheme 10 | unless SwarmizeOembed.matches_scheme(url) 11 | raise ActionController::RoutingError.new("URL doesn't match OEmbed scheme") 12 | end 13 | 14 | # find token 15 | token = SwarmizeOembed.extract_token(url) 16 | swarm = Swarm.find_by(token: token) 17 | unless swarm 18 | raise ActionController::RoutingError.new('Not Found') 19 | end 20 | 21 | require 'open-uri' 22 | 23 | 24 | # generate JSON 25 | render :json => SwarmizeOembed.for(swarm, {root_url: root_url, 26 | maxwidth: params[:maxwidth], 27 | maxheight: params[:maxheight]}) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/controllers/search_controller.rb: -------------------------------------------------------------------------------- 1 | class SearchController < ApplicationController 2 | before_filter :check_for_user 3 | 4 | def results 5 | @query = params[:query] 6 | if @current_user 7 | @swarms = Swarm.search_by_name_and_description(@query) 8 | else 9 | @swarms = Swarm.publicly_visible.search_by_name_and_description(@query) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | def callback 3 | if EmailValidator.is_valid_email?(request.env['omniauth.auth'].info.email) 4 | @user = User.find_or_create_from_info_hash(request.env['omniauth.auth'].info) 5 | session[:user_id] = @user.id 6 | flash[:success] = "You've been successfully logged in to Swarmize." 7 | redirect_back_or_default(root_path) 8 | else 9 | flash[:error] = "We're sorry, but you can't log in with that email address." 10 | redirect_to root_path 11 | end 12 | end 13 | 14 | def logout 15 | # render a logout screen 16 | end 17 | 18 | def destroy 19 | session[:user_id] = nil 20 | flash[:success] = "You've been logged out of Swarmize." 21 | redirect_to root_path 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/controllers/swarm_import_controller.rb: -------------------------------------------------------------------------------- 1 | require 'open-uri' 2 | 3 | class SwarmImportController < ApplicationController 4 | before_filter :check_for_user 5 | 6 | def new 7 | end 8 | 9 | def create 10 | # if url doesn't end .json, add it 11 | url = params[:url] 12 | unless url =~ /(.*)\.json$/ 13 | url = "#{url}.json" 14 | end 15 | 16 | json = open(url).read 17 | 18 | swarm = JSONImporter.import!(json, @current_user) 19 | flash[:success] = "Swarm created" 20 | redirect_to swarm 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/controllers/utils_controller.rb: -------------------------------------------------------------------------------- 1 | class UtilsController < ApplicationController 2 | def name_to_code 3 | render :text => params[:name].strip.parameterize.underscore 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | 3 | def glyphicon(string) 4 | "".html_safe 5 | end 6 | 7 | def serve_from_cloudfront(path) 8 | "http://cdn.swarmize.com#{path}" 9 | end 10 | 11 | def will_paginate(collection_or_options = nil, options = {}) 12 | if collection_or_options.is_a? Hash 13 | options, collection_or_options = collection_or_options, nil 14 | end 15 | unless options[:renderer] 16 | options = options.merge :renderer => BootstrapPagination::Rails 17 | end 18 | super *[collection_or_options, options].compact 19 | end 20 | 21 | def bootstrap_class_for(flash_type) 22 | case flash_type 23 | when :success 24 | "alert-success" # Green 25 | when :error 26 | "alert-danger" # Red 27 | when :alert 28 | "alert-warning" # Yellow 29 | when :notice 30 | "alert-info" # Blue 31 | else 32 | flash_type.to_s 33 | end 34 | end 35 | 36 | def login_path 37 | "/auth/google_oauth2" 38 | end 39 | 40 | def format_swarm_date(time) 41 | time.strftime("%d %B %Y") 42 | end 43 | 44 | def format_swarm_time(time) 45 | time.strftime("%H:%M%P") 46 | end 47 | 48 | def escape_apostrophe(string) 49 | string.gsub(/'/) {|s| "\\'"} 50 | end 51 | 52 | 53 | # this comes from the old Sinatra app 54 | def format_timestamp(ts) 55 | t = Time.zone.parse(ts) 56 | t.strftime("%d %B %Y %H:%M:%S") 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/app/mailers/.keep -------------------------------------------------------------------------------- /swarmize-dot-com/app/mailers/permission_mailer.rb: -------------------------------------------------------------------------------- 1 | class PermissionMailer < ActionMailer::Base 2 | default from: "noreply@swarmize.com", 3 | reply_to: 'graham.tackley@guardian.co.uk', 4 | return_path: 'graham.tackley@guardian.co.uk' 5 | 6 | def permission_email(to_address, swarm) 7 | @swarm = swarm 8 | mail(to: to_address, subject: "[Swarmize] You've been given permissions on a Swarm.") 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/app/models/.keep -------------------------------------------------------------------------------- /swarmize-dot-com/app/models/access_permission.rb: -------------------------------------------------------------------------------- 1 | class AccessPermission < ActiveRecord::Base 2 | belongs_to :swarm 3 | belongs_to :user 4 | belongs_to :creator, :class_name => 'User', :foreign_key => 'creator_id' 5 | 6 | before_save :downcase_email 7 | 8 | validates :email, uniqueness: { scope: :swarm, 9 | message: "can only be given permission on a swarm once" } 10 | validates :user, uniqueness: { scope: :swarm, 11 | message: "can only be given permission on a swarm once" }, if: :user 12 | 13 | def self.update_legacy_permissions_for(user) 14 | aps = AccessPermission.where(email: user.email) 15 | aps.each do |ap| 16 | ap.user = user 17 | ap.save 18 | end 19 | end 20 | 21 | def self.can_alter?(swarm, user) 22 | if user 23 | user.is_admin? || swarm.users.include?(user) || swarm.access_permissions.find_by(email: user.email) 24 | end 25 | end 26 | 27 | def self.can_destroy?(swarm,user) 28 | if user 29 | user.is_admin? || swarm.owners.include?(user) 30 | end 31 | end 32 | 33 | def self.can_alter_permissions?(swarm,user) 34 | if user 35 | user.is_admin? || swarm.owners.include?(user) 36 | end 37 | end 38 | 39 | def self.can_see_user_drafts?(current_user, user) 40 | current_user && ((current_user == user) || current_user.is_admin?) 41 | end 42 | private 43 | 44 | def downcase_email 45 | self.email = self.email.downcase 46 | end 47 | 48 | end 49 | 50 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/models/api_key.rb: -------------------------------------------------------------------------------- 1 | class APIKey 2 | def self.create(swarm_token, email) 3 | api_key = generate_key 4 | table.items.create(api_key: api_key, 5 | swarm_token: swarm_token, 6 | created_by: email, 7 | created_at: Time.now.utc.to_s) 8 | end 9 | 10 | def self.delete(api_key) 11 | table.items[api_key].delete 12 | end 13 | 14 | def self.all 15 | table.items 16 | end 17 | 18 | def self.client 19 | @@dynamo ||= AWS::DynamoDB.new 20 | end 21 | 22 | def self.table 23 | client.tables['api_keys'].load_schema 24 | end 25 | 26 | private 27 | 28 | def self.generate_key 29 | loop do 30 | random_key = SecureRandom.hex(8) 31 | break random_key unless table.items[random_key].exists? 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/app/models/concerns/.keep -------------------------------------------------------------------------------- /swarmize-dot-com/app/models/field_description.rb: -------------------------------------------------------------------------------- 1 | require 'hashie' 2 | 3 | class FieldDescription 4 | 5 | ALL_FIELDS = JSON.parse(File.read(Rails.root.join('field_types.json'))) 6 | 7 | def self.all 8 | Hashie::Mash.new(ALL_FIELDS) 9 | end 10 | 11 | def self.find(field_type) 12 | all.send(field_type) 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/models/graph.rb: -------------------------------------------------------------------------------- 1 | class Graph < ActiveRecord::Base 2 | belongs_to :swarm 3 | 4 | serialize :options 5 | GRAPH_TYPES = {pie: "Pie", 6 | timeseries: "Time Series"} 7 | 8 | def data_url(field, opts={}) 9 | case graph_type 10 | when 'pie' 11 | count_url(field,opts) 12 | when 'timeseries' 13 | time_series_url(field,opts) 14 | end 15 | end 16 | 17 | private 18 | 19 | def count_url(graph_field, opts={}) 20 | if opts[:filter_field] 21 | "/swarms/#{swarm.token}/graphs/count/#{graph_field}/#{opts[:filter_field]}" 22 | else 23 | "/swarms/#{swarm.token}/graphs/count/#{graph_field}" 24 | end 25 | end 26 | 27 | def time_series_url(graph_field, opts={}) 28 | opens_at = swarm.opens_at || Time.now 29 | closes_at = swarm.closes_at || Time.now 30 | interval = 'hour' 31 | case (closes_at - opens_at) 32 | when (1.hour..1.day) 33 | interval = 'minute' 34 | when (0..1.hour) 35 | interval = 'minute' 36 | end 37 | 38 | "/swarms/#{swarm.token}/graphs/count_over_time/#{graph_field}/#{interval}" 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | has_many :access_permissions 3 | has_many :swarms, :through => :access_permissions 4 | has_many :owned_swarms, -> { where('access_permissions.is_owner' => true) }, :through => :access_permissions, :source => :swarm 5 | 6 | before_save :downcase_email 7 | after_create :update_access_permissions 8 | 9 | def self.find_or_create_from_info_hash(info_hash) 10 | self.find_or_create_by( 11 | :email => EmailValidator.normalize_email(info_hash['email']), 12 | :name => info_hash['name'], 13 | :image_url => info_hash['image'] 14 | ) 15 | end 16 | 17 | private 18 | 19 | def downcase_email 20 | self.email = self.email.downcase 21 | end 22 | 23 | def update_access_permissions 24 | AccessPermission.update_legacy_permissions_for(self) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/admin/show.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Admin : 3 | 4 | .page-header.swarmize-header 5 | %h1 Admin tools 6 | 7 | %ul.nav.nav-tabs 8 | %li.active= link_to "Fake Data", "#fake", "data-toggle" => 'tab' 9 | %li= link_to "Deleted swarms", "#deleted", "data-toggle" => 'tab' 10 | %li= link_to "Tokens ", "#tokens", "data-toggle" => 'tab' 11 | 12 | .tab-content 13 | #fake.tab-pane.active 14 | %h2 Fake data 15 | 16 | %p= button_to "Clear out fake data, make fake swarms/users", dummy_up_admin_path, :class => "btn btn-default" 17 | %p= button_to "Clear out fake data", delete_dummy_admin_path, :class => "btn btn-default" 18 | %p= button_to "Create dummy users", create_dummy_users_admin_path, :class => "btn btn-default" 19 | %p= button_to "Create dummy swarms", create_dummy_swarms_admin_path, :class => "btn btn-default" 20 | 21 | #deleted.tab-pane 22 | %h2 Deleted swarms. 23 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @deleted_swarms} 24 | 25 | #tokens.tab-pane 26 | %h2 Tokens 27 | %p= button_to "Regenerate tokens", regenerate_tokens_admin_path, :class => "btn btn-default" 28 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/api_keys/index.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | API Keys for #{@swarm.name} : 3 | 4 | .page-header.swarmize-header 5 | %h1 API Keys for #{@swarm.name} 6 | 7 | %table.table 8 | %thead 9 | %tr 10 | %th Key 11 | %th Created By 12 | %th Created At 13 | %th Action 14 | %tbody 15 | - if @swarm.api_keys 16 | - @swarm.api_keys.each do |key| 17 | %tr 18 | %td= key['api_key'] 19 | %td= key['created_by'].name 20 | %td= key['created_at'] 21 | %td= link_to 'Revoke access', swarm_api_key_path(@swarm, key['api_key']), :confirm => 'Are you sure?', :method => :delete 22 | 23 | %p= button_to "Create Key", swarm_api_keys_path(@swarm), :class => "btn btn-default" 24 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/case_studies/_list.html.haml: -------------------------------------------------------------------------------- 1 | %ul#case_studies 2 | %li.col-sm-4 3 | %a{href: case_study_path('broadband_survey')} 4 | %img{src: '/case_studies/broadband_survey/thumb.png'} 5 | %h3= link_to "Making a simple survey: the Guardian Broadband survey", case_study_path('broadband_survey') 6 | %p At its most basic, Swarmize can be used to generate embeddable forms and analyse data. This case study serves as a basic tutorial for Swarmize. 7 | %li.col-sm-4 8 | %a{href: case_study_path('realtime_clicker')} 9 | %img{src: '/case_studies/realtime_clicker/thumb.jpg'} 10 | %h3= link_to "Realtime data, and going beyond embeds: using Swarmize’s Collector and Retrieval APIs to build a live ‘clicker’ for a TV debate", case_study_path('realtime_clicker') 11 | %p Swarmize isn't just about embeddable forms. Here, we use the Collector and Retrieval APIs (for input and output) to build a realtime 'clicker' for feedback during a TV debate. 12 | %li.col-sm-4 13 | %a{href: case_study_path('indyref')} 14 | %img{src: '/case_studies/indyref/thumb.png'} 15 | %h3= link_to "Real world use: the #indyref swarm", case_study_path('indyref') 16 | %p Finally, a real swarm commissioned by a Guardian editor at short notice: to watch how sentiment on Twitter was leaning in the run up to the Scottish Independence Referendum. 17 | 18 | .clearfix 19 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/case_studies/index.html.haml: -------------------------------------------------------------------------------- 1 | .page-header 2 | %h1 Case Studies 3 | 4 | %p These case studies should offer some insight into how Swarmize can be used. 5 | 6 | = render :partial => "case_studies/list" 7 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/case_studies/show.html.haml: -------------------------------------------------------------------------------- 1 | .page-header 2 | %h2 Case Study 3 | 4 | .case_study= @body.html_safe 5 | 6 | %p= link_to "Back to all case studies", case_studies_path 7 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/documentation/index.html.haml: -------------------------------------------------------------------------------- 1 | %h1 Help 2 | 3 | %ul 4 | %li= link_to "API Documentation", doc_path('api') 5 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/documentation/show.html.haml: -------------------------------------------------------------------------------- 1 | .case_study= @body.html_safe 2 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/form_partials/_bigtext_display_field_partial.html.haml: -------------------------------------------------------------------------------- 1 | .form-group 2 | %label= field.field_name 3 | = text_area_tag "#{field.field_code}", nil, {:class => 'form-control', :placeholder => field.sample_value, :rows => 8}.merge(parsley_validations_for_field(field)) 4 | - if field.hint 5 | %span.help-block= field.hint 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/form_partials/_check_box_display_field_partial.html.haml: -------------------------------------------------------------------------------- 1 | .form_group{:id => "group_#{field.field_code}"} 2 | .check 3 | %label 4 | = check_box_tag field.field_code, 1, false, parsley_validations_for_field(field) 5 | = field.field_name 6 | .parsley-errors{:id => "errors_#{field.field_code}"} 7 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/form_partials/_display_field_partial.html.haml: -------------------------------------------------------------------------------- 1 | .form-group 2 | %label= field.field_name 3 | - if field_description.input_type 4 | %input.form-control{parsley_validations_for_field(field), :name => field.field_code, :type => field_description.input_type, :placeholder => field.sample_value} 5 | - else 6 | = text_field_tag field.field_code, nil, {:class => 'form-control', :placeholder => field.sample_value}.merge(parsley_validations_for_field(field)) 7 | - if field.hint 8 | %span.help-block= field.hint 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/form_partials/_element_controls.html.haml: -------------------------------------------------------------------------------- 1 | .controls 2 | %span.move-this-element.glyphicon.glyphicon-move{:style => "cursor:move;"} 3 | - unless @swarm.has_opened? 4 | %a{:href => "#", :class => "close-this-element"} 5 | = glyphicon('remove') 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/form_partials/_pick_several_display_field_partial.html.haml: -------------------------------------------------------------------------------- 1 | .form_group{:id => "group_#{field.field_code}"} 2 | %label= field.field_name 3 | - field.possible_values.each_with_index do |value, index| 4 | .checkbox 5 | %label 6 | = check_box_tag field.field_code, value.parameterize.underscore, false,parsley_validations_for_field(field) 7 | = value 8 | - if field.allow_other 9 | .checkbox 10 | %label.other 11 | = check_box_tag field.field_code, 'other', false, {:class => 'other_field'}.merge(parsley_validations_for_field(field)) 12 | Other: 13 | = text_field_tag "#{field.field_code}_other", nil, {'data-parsley-conditionalother' => field.field_code, 'data-parsley-validate-if-empty' => true, 'data-parsley-trim-value' => true, :class => 'other-inline-input form-control'} 14 | .parsley-errors{:id => "errors_#{field.field_code}"} 15 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/form_partials/_rating_display_field_partial.html.haml: -------------------------------------------------------------------------------- 1 | .form_group{:id => "group_#{field.field_code}"} 2 | %label= field.field_name 3 | - upper_bound = field.maximum.to_i 4 | - lower_bound = field.minimum.to_i 5 | - range = (lower_bound..upper_bound) 6 | - range.each do |rating| 7 | .radio 8 | %label 9 | = radio_button_tag field.field_code, rating, false, parsley_validations_for_field(field) 10 | = rating 11 | 12 | .parsley-errors{:id => "errors_#{field.field_code}"} -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/form_partials/_yesno_display_field_partial.html.haml: -------------------------------------------------------------------------------- 1 | .form_group{:id => "group_#{field.field_code}"} 2 | %label= field.field_name 3 | .radio 4 | %label 5 | = radio_button_tag field.field_code, 'yes', false, parsley_validations_for_field(field) 6 | Yes 7 | .radio 8 | %label 9 | = radio_button_tag field.field_code, 'no', false, parsley_validations_for_field(field) 10 | No 11 | .parsley-errors{:id => "errors_#{field.field_code}"} 12 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/graphs/_pie.html.haml: -------------------------------------------------------------------------------- 1 | :javascript 2 | function updateGraphData#{count+1}() { 3 | window.graphData#{count+1}.fetch(); 4 | 5 | setTimeout(function() { 6 | updateGraphData#{count+1}(); 7 | }, 5000); 8 | } 9 | 10 | $(document).ready(function() { 11 | window.graphData#{count+1} = new GraphData({url: '#{graph.data_url(graph.field, {filter_field: graph.options[:filter_field]})}', 12 | graphType: 'pie', 13 | title: '#{escape_apostrophe(graph.title)}', 14 | index: #{count+1}}); 15 | 16 | 17 | window.graph#{count+1} = new SwarmizePieGraph({model: window.graphData#{count+1}}); 18 | 19 | updateGraphData#{count+1}(); 20 | }); 21 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/graphs/_timeseries.html.haml: -------------------------------------------------------------------------------- 1 | :javascript 2 | function updateGraphData#{count+1}() { 3 | window.graphData#{count+1}.fetch(); 4 | 5 | 6 | setTimeout(function() { 7 | updateGraphData#{count+1}(); 8 | }, 5000); 9 | } 10 | 11 | $(document).ready(function() { 12 | window.graphData#{count+1} = new GraphData({ 13 | url: '#{graph.data_url(graph.field)}', 14 | graphType: 'timeseries', 15 | title: '#{escape_apostrophe(graph.title)}', 16 | index: #{count+1}}); 17 | 18 | window.graph#{count+1} = new SwarmizeTimeSeriesGraph({model: window.graphData#{count+1}}); 19 | 20 | updateGraphData#{count+1}(); 21 | }); 22 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/graphs/delete.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Delete Graph '#{@graph.title}' : Graphs for #{@swarm.name} : 3 | 4 | .page-header.swarmize-header 5 | %h1 Delete Graph '#{@graph.title}'? 6 | 7 | %p Are you sure you want to delete this graph? 8 | 9 | %p 10 | = button_to "Delete", swarm_graph_path(@swarm,@graph), :method => :delete, :class => "btn btn-default" 11 | or 12 | = link_to "Return to all graphs for this swarm.", swarm_graphs_path(@swarm) 13 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/graphs/index.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Manage Graphs : #{@swarm.name} : 3 | 4 | .page-header.swarmize-header 5 | %h1 Manage Graphs for #{@swarm.name} 6 | 7 | - if @swarm.graphs.any? 8 | %table.table 9 | %thead 10 | %tr 11 | %th Title 12 | %th Type 13 | %th Field being graphed 14 | %th Options 15 | %tbody 16 | - @swarm.graphs.each do |g| 17 | %tr 18 | %td= g.title 19 | %td= g.graph_type 20 | %td= g.field 21 | %td= g.options.inspect 22 | %td 23 | = link_to "Edit", edit_swarm_graph_path(@swarm,g) 24 | | 25 | = link_to "Delete", delete_swarm_graph_path(@swarm,g) 26 | 27 | 28 | %p= link_to "Create new graph", new_swarm_graph_path(@swarm), :class => "btn btn-default" 29 | %p= link_to "Back to swarm", @swarm 30 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/home/_logged_in.html.haml: -------------------------------------------------------------------------------- 1 | = render :partial => 'home/project_status' 2 | 3 | %a.btn.btn-default{href: new_swarm_path, style: 'float:right;'} 4 | = glyphicon('plus') 5 | Create A Swarm 6 | 7 | %ul.nav.nav-tabs{:role => 'tablist'} 8 | %li.active= link_to "All Swarms", "#all", "data-toggle" => 'tab' 9 | %li= link_to "My Swarms", "#mine", "data-toggle" => 'tab' 10 | 11 | .tab-content 12 | #all.tab-pane.active 13 | %h2 Latest Swarms In Progress 14 | 15 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @swarms_in_progress} 16 | 17 | %h2 Latest complete swarms 18 | 19 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @latest_complete_swarms} 20 | 21 | %p= link_to "Explor all swarms", swarms_path 22 | 23 | 24 | #mine.tab-pane 25 | %h2 My Latest Draft Swarms 26 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @user_swarms_draft} 27 | 28 | %h2 My Latest Swarms In Progress 29 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @user_swarms_in_progress} 30 | 31 | %h2 My Latest Completed Swarms 32 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @user_completed_swarms} 33 | 34 | %p= link_to "View all my swarms", user_path(@current_user) 35 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/home/_logged_out.html.haml: -------------------------------------------------------------------------------- 1 | .row 2 | .col-sm-6 3 | %p Swarmize is a data journalism platform. It helps editors collect, analyze and output information. 4 | 5 | %p News teams can create simple surveys in minutes or build engaging interactives and custom data collectors with front-end developers using the Swarmize toolchain. The platform includes a survey wizard, embeddable web forms, dashboards and charts, CSV outputs and developer-friendly APIs optimized for high volume, fast-paced news organizations such as theguardian.com. 6 | 7 | %p Swarmize does the heavy lifting behind the scenes and makes it easy to deploy and manage data journalism projects quickly and cheaply. 8 | %br 9 | 10 | = render :partial => 'home/project_status' 11 | 12 | .col-sm-6 13 | %h2{style: 'margin-top: 0;'} A bit about you 14 |
Fill out your answers on swarmize.com
15 | 16 | %hr 17 | 18 | %h2 Case studies 19 | 20 | = render :partial => "case_studies/list" 21 | 22 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/home/_project_status.html.haml: -------------------------------------------------------------------------------- 1 | .panel.panel-default 2 | .panel-heading 3 | %h3.panel-title Project Status 4 | .panel-body 5 | %p 6 | %b Swarmize is currently in alpha. 7 | The platform is accessible for use by Guardian staff only, but the code is available for download and reuse with an open license on 8 | #{link_to "GitHub", "https://github.com/guardian/swarmize"}. 9 | %p 10 | The project was funded by a grant from the Knight News Challenge and also supported by The Guardian. The developers are 11 | #{link_to "Tom Armitage", "mailto:tom@tomarmitage.com"} 12 | and 13 | #{link_to "Graham Tackley", "mailto:graham.tackley@theguardian.com"}. 14 | If you would like to discuss working on the project together please contact 15 | #{link_to "Matt McAlister", "mailto:matt.mcalister@theguardian.com"}. 16 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/home/show.html.haml: -------------------------------------------------------------------------------- 1 | .page-header.swarmize-header 2 | %h1 Welcome to Swarmize 3 | 4 | - if @current_user 5 | = render "home/logged_in" 6 | - else 7 | = render "home/logged_out" 8 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/layouts/_flashes.html.haml: -------------------------------------------------------------------------------- 1 | - flash.each do |type, message| 2 | .alert.alert-dismissable.fade.in{:class => bootstrap_class_for(type)} 3 | %button{:type => 'button', :class => 'close', "data-dismiss" => "alert", "aria-hidden" => true} × 4 | = message 5 | 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/layouts/application.html.haml: -------------------------------------------------------------------------------- 1 | !!! 5 2 | %html{html_attrs} 3 | %head 4 | = csrf_meta_tag 5 | %meta{:'http-equiv' => "Content-Type", :content => "text/html; charset=utf-8"} 6 | %meta{:name => "viewport", :content => "width=device-width, initial-scale=1"} 7 | = favicon_link_tag 8 | %title 9 | =yield :pagetitle 10 | Swarmize 11 | 12 | = yield :oembed_discovery 13 | - if Rails.env.production? 14 | = stylesheet_link_tag "//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" 15 | - else 16 | = stylesheet_link_tag "bootstrap.min" 17 | = stylesheet_link_tag "bootstrap-theme.min" 18 | = stylesheet_link_tag "application" 19 | = javascript_include_tag "application" 20 | %body 21 | = render "layouts/nav" 22 | 23 | .container 24 | = render "layouts/flashes" 25 | = yield 26 | 27 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/layouts/embed.html.haml: -------------------------------------------------------------------------------- 1 | !!! 5 2 | %html{html_attrs} 3 | %head 4 | %meta{:'http-equiv' => "Content-Type", :content => "text/html; charset=utf-8"} 5 | %meta{:name => "lang", :content => "en"} 6 | %meta{:name => "viewport", :content => "width=device-width, initial-scale=1"} 7 | %title Swarmize 8 | = stylesheet_link_tag "bootstrap.min" 9 | = stylesheet_link_tag "bootstrap-theme.min" 10 | = stylesheet_link_tag "application" 11 | = javascript_include_tag "embed" 12 | %body 13 | .container 14 | = yield 15 | 16 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/permission_mailer/permission_email.text.erb: -------------------------------------------------------------------------------- 1 | You've been given permissions on the swarm '<%= @swarm.name %>'. 2 | 3 | You'll be able to edit the swarm, as well as get API code for it, and test it before it's live. Find it on Swarmize here: 4 | 5 | <%= swarm_url(@swarm, host: 'www.swarmize.com') %> 6 | 7 | If you don't have an account on Swarmize, don't worry: you'll be able to log in with your Guardian email address. 8 | 9 | If you think you've received this email in error, feel free to ignore it: it requires no action on your behalf. 10 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/permissions/_logged_out_permissions_page.html.haml: -------------------------------------------------------------------------------- 1 | %table.table#permissions 2 | %thead 3 | %tr 4 | %th 5 | %th User 6 | %tbody 7 | - @swarm.users.each do |user| 8 | %tr 9 | %td.pic= image_tag user.image_url, :height => 25 10 | %td.user 11 | = link_to user.name, user 12 | - if @swarm.owners.include?(user) 13 | %b (owner) 14 | - if @swarm.unassigned_access_permissions.any? 15 | %tr 16 | %td 17 | %td ...and #{pluralize @swarm.unassigned_access_permissions.count, 'other user'}. 18 | 19 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/permissions/_owner_permissions_page.html.haml: -------------------------------------------------------------------------------- 1 | %table.table#permissions 2 | %thead 3 | %tr 4 | %th 5 | %th User 6 | %th Actions 7 | %tbody 8 | - @swarm.access_permissions.each do |a| 9 | %tr 10 | -if a.user 11 | %td.pic= image_tag a.user.image_url, :height => 25 12 | %td.user{:class => ('owner' if @swarm.owners.include?(a.user))} 13 | = link_to a.user.name, a.user 14 | - if @swarm.owners.include?(a.user) 15 | %i (owner) 16 | - if @current_user == a.user 17 | %i (you) 18 | %td 19 | - if @swarm.users.size == 1 20 | - else 21 | = button_to 'Remove permissions on this swarm', swarm_permission_path(@swarm, a), :class => 'btn btn-default remove-permission', :method => :delete 22 | - else 23 | %td.pic 24 | %td= a.email 25 | %td= button_to 'Remove permissions on this swarm', swarm_permission_path(@swarm, a), :class => 'btn btn-default remove-permission', :method => :delete 26 | 27 | %hr.divider 28 | 29 | %h4 Give someone else permission to alter this swarm 30 | %p You can allow other people to edit this swarm. Enter their Guardian email address below to invite them to this swarm. 31 | = form_tag swarm_permissions_path(@swarm), :id => 'create-permission', :class => 'form-horizontal' do 32 | .form-group 33 | %label.col-sm-2.control-label Email 34 | .col-sm-6 35 | = text_field_tag :email, nil, :class => 'form-control' 36 | .row 37 | .col-sm-6.col-sm-offset-2 38 | %p= submit_tag "Grant access", :class => "btn btn-default" 39 | 40 | 41 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/permissions/index.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Permissions for '#{@swarm.name}'? : Swarms : 3 | 4 | .page-header.swarmize-header 5 | %h1 6 | Users with permissions on '#{@swarm.name}' 7 | %p 8 | %a{:href => swarm_path(@swarm)} 9 | = glyphicon('circle-arrow-left') 10 | Back to swarm 11 | 12 | - if AccessPermission.can_alter_permissions?(@swarm, @current_user) 13 | = render :partial => 'owner_permissions_page' 14 | - else 15 | = render :partial => 'logged_out_permissions_page' 16 | 17 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/search/results.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Search results for '#{@query}' : 3 | 4 | %h1 Search results for '#{@query}' 5 | 6 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @swarms} 7 | 8 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/sessions/logout.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Log out ? : 3 | 4 | .page-header.swarmize-header 5 | %h1 Log out of Swarmize? 6 | 7 | %p 8 | = button_to "Log out", session_path, :method => :delete, :class => "btn btn-default" 9 | or 10 | = link_to "Back home", root_path 11 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarm_import/new.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Import Swarm : 3 | 4 | .page-header.swarmize-header 5 | %h1 Import Swarm From JSON 6 | 7 | = form_tag swarm_imports_path do 8 | .form-group 9 | %label JSON Url 10 | = text_field_tag :url, nil, :class => 'form-control' 11 | %span.help-block The URL to the swarm, eg http://www.swarmize.com/swarms/rycadjgp 12 | 13 | = submit_tag "Import Swarm", :class => "btn btn-default" 14 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/_embed_closed.html.haml: -------------------------------------------------------------------------------- 1 | .embeddable-swarm 2 | - if @swarm.display_title 3 | %h2= @swarm.name 4 | - if @swarm.display_description 5 | .explanation= simple_format @swarm.description 6 | .closed 7 | This swarm is now closed, and is no longer accepting responses. 8 | 9 | .small 10 | Powered by 11 | %b Swarmize. 12 | 13 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/_embed_live.html.haml: -------------------------------------------------------------------------------- 1 | .embeddable-swarm 2 | - if @swarm.display_title 3 | %h2= @swarm.name 4 | - if @swarm.display_description 5 | .explanation= simple_format @swarm.description 6 | = form_tag @swarm.collector_url, 'data-parsley-validate' => 'true' do 7 | - if @swarm.swarm_fields 8 | - @swarm.swarm_fields.each do |swarm_field| 9 | - if swarm_field.has_custom_display? 10 | = render "form_partials/#{swarm_field.field_type}_display_field_partial", field: swarm_field, field_description: swarm_field.description 11 | - else 12 | = render "form_partials/display_field_partial", field: swarm_field, field_description: swarm_field.description 13 | %p.submit 14 | = submit_tag "Submit", :class => "btn btn-default" 15 | 16 | .feedback 17 | .small 18 | Powered by 19 | %b Swarmize. 20 | 21 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/_field_code_update_js.html.haml: -------------------------------------------------------------------------------- 1 | :javascript 2 | $(document).ready(function() { 3 | $(".code-text-block a.alter").click(function(e) { 4 | $(this).parents('.form-group').find('.field-code-block').show(); 5 | $(this).parents('.code-text-block').hide(); 6 | e.preventDefault(); 7 | }); 8 | 9 | $("input.field-name").unbind('change').on('change', function(e) { 10 | if(!('hasCustomFieldCode' in $(this).parents('.form-element').data())) { 11 | var fieldCode = $(this).val(); 12 | var codeField = $(this).parents('.form-element').find('.field-code'); 13 | var codeText = $(this).parents('.form-element').find('.code-text'); 14 | $.get("/utils/name_to_code", {name: fieldCode}, function(data) { 15 | $(codeField).val(data); 16 | $(codeText).text(data); 17 | }); 18 | } 19 | }); 20 | 21 | $("input.field-code").unbind('focus').on('focus', function(e) { 22 | console.log('focus'); 23 | $(this).data('code-pre-alter', $(this).val()); 24 | console.log($(this).data()); 25 | }); 26 | 27 | $("input.field-code").unbind('change').on('change', function(e) { 28 | if('codePreAlter' in $(this).data()) { 29 | if($(this).val() != $(this).data('codePreAlter')) { 30 | console.log("changed by hand, noting this is a custom field code"); 31 | $(this).parents('.form-element').data('hasCustomFieldCode', true); 32 | } 33 | } 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/_swarm_description_open_close.html.haml: -------------------------------------------------------------------------------- 1 | .row 2 | .col-sm-6 3 | .desc= simple_format @swarm.description 4 | .col-sm-6 5 | .row 6 | .col-sm-6 7 | - if @swarm.has_opened? 8 | %h3 Opened at 9 | %h4 10 | = format_swarm_date(@swarm.opens_at) 11 | %br 12 | = format_swarm_time(@swarm.opens_at) 13 | .col-sm-6 14 | - if @swarm.closed? 15 | %h3 Closed at 16 | %h4 17 | = format_swarm_date(@swarm.closes_at) 18 | %br 19 | = format_swarm_time(@swarm.closes_at) 20 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/_swarm_dropdown_menu.html.haml: -------------------------------------------------------------------------------- 1 | .dropdown.actions-dropdown 2 | %button.btn.btn-default.dropdown-toggle{"data-toggle" => "dropdown"} 3 | Actions 4 | %span.caret 5 | %ul.dropdown-menu.dropdown-menu-right 6 | %li 7 | = link_to " Clone".html_safe, clone_swarm_path(@swarm), :method => :post 8 | %li.divider 9 | - if AccessPermission.can_alter?(@swarm, @current_user) && !@swarm.closed? 10 | %li 11 | %a{:href => edit_swarm_path(@swarm)} 12 | = glyphicon('pencil') 13 | Edit 14 | 15 | - if AccessPermission.can_alter?(@swarm, @current_user) 16 | %li 17 | %a{:href => code_swarm_path(@swarm)} 18 | = glyphicon('th-list') 19 | Get Code 20 | %li 21 | %a{:href => swarm_api_keys_path(@swarm)} 22 | = glyphicon('tag') 23 | API Keys 24 | %li 25 | %a{:href => swarm_permissions_path(@swarm)} 26 | = glyphicon('user') 27 | Permissions 28 | - if AccessPermission.can_destroy?(@swarm, @current_user) 29 | %li.divider 30 | %li 31 | %a{:href => delete_swarm_path(@swarm)} 32 | = glyphicon('remove') 33 | Delete 34 | 35 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/_swarm_graphs.html.haml: -------------------------------------------------------------------------------- 1 | %h2 Graphs 2 | 3 | - @swarm.graphs.each_with_index do |graph, i| 4 | = render :partial => "graphs/#{graph.graph_type}", :locals => {:graph => graph, :count => i} 5 | 6 | #graphs 7 | 8 | .clearfix 9 | 10 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/_swarm_header.html.haml: -------------------------------------------------------------------------------- 1 | .row 2 | .page-header.swarmize-header 3 | .col-sm-9 4 | %h1 5 | = @swarm.name 6 | - if @swarm.live? 7 | %span.label.label-success.swarmlive Live! 8 | .subheader 9 | = @swarm.owners.map { |creator| link_to @swarm.creator.name, user_path(@swarm.creator) }.to_sentence 10 | - if @current_user 11 | | 12 | = link_to "View Form", embed_swarm_path(@swarm) 13 | - if @swarm.parent_swarm 14 | %small 15 | [Cloned from #{link_to @swarm.parent_swarm.name, swarm_path(@swarm.parent_swarm)}] 16 | 17 | .col-sm-3.swarm-controls 18 | = render :partial => 'swarms/swarm_dropdown_menu' 19 | 20 | .clearfix 21 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/_swarm_page_js.html.haml: -------------------------------------------------------------------------------- 1 | :javascript 2 | $(document).ready(function() { 3 | $("#set-open-date-to-now").click(function(e) { 4 | var d = new Date(); 5 | 6 | var year = d.getFullYear(); 7 | var month = d.getMonth() + 1; 8 | var day = d.getDate(); 9 | var hours = d.getHours(); 10 | var mins = d.getMinutes(); 11 | 12 | $('select[name=open_year]').val(year); 13 | $('select[name=open_month]').val(month); 14 | $('select[name=open_day]').val(day); 15 | $('select[name=open_hour]').val(hours); 16 | $('select[name=open_minute]').val(mins); 17 | 18 | e.preventDefault(); 19 | }); 20 | 21 | $("#open-submit").click(function(e) { 22 | $("#open-date-form").submit(); 23 | e.preventDefault(); 24 | }); 25 | 26 | $("#set-close-date-to-now").click(function(e) { 27 | var d = new Date(); 28 | 29 | var year = d.getFullYear(); 30 | var month = d.getMonth() + 1; 31 | var day = d.getDate(); 32 | var hours = d.getHours(); 33 | var mins = d.getMinutes(); 34 | 35 | $('select[name=close_year]').val(year); 36 | $('select[name=close_month]').val(month); 37 | $('select[name=close_day]').val(day); 38 | $('select[name=close_hour]').val(hours); 39 | $('select[name=close_minute]').val(mins); 40 | 41 | e.preventDefault(); 42 | }); 43 | 44 | $("#close-submit").click(function(e) { 45 | $("#close-date-form").submit(); 46 | e.preventDefault(); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/_swarm_table.html.haml: -------------------------------------------------------------------------------- 1 | %table.table 2 | %thead 3 | %tr 4 | %th Name 5 | %th Description 6 | %th Created by 7 | %th Opens At 8 | %th Closes At 9 | 10 | %tbody 11 | - if swarms && swarms.any? 12 | - for swarm in swarms 13 | %tr 14 | %td= link_to swarm.name, swarm 15 | %td= swarm.description 16 | %td= link_to swarm.creator.name, user_path(swarm.creator) if swarm.creator 17 | %td= swarm.opens_at 18 | %td= swarm.closes_at 19 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/closed.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Closed Swarms : Swarms : 3 | 4 | %h1 Explore Swarms 5 | 6 | %ul.nav.nav-tabs 7 | %li 8 | %a{:href => swarms_path} 9 | All Swarms 10 | %span.badge= @all_swarms_count 11 | - if @current_user 12 | %li 13 | %a{:href => draft_swarms_path} 14 | Draft 15 | %span.badge= @open_swarms_count 16 | %li 17 | %a{:href => live_swarms_path} 18 | In Progress 19 | %span.badge= @live_swarms_count 20 | %li.active 21 | %a{:href => closed_swarms_path} 22 | Closed 23 | %span.badge= @closed_swarms_count 24 | 25 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @swarms} 26 | 27 | = will_paginate @swarms 28 | 29 | - if @current_user 30 | %p= link_to "New swarm", new_swarm_path 31 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/code.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Code for #{@swarm.name} : Swarms : 3 | 4 | .page-header.swarmize-header 5 | %h1 6 | Code for '#{@swarm.name}' 7 | %p 8 | %a{:href => swarm_path(@swarm)} 9 | = glyphicon('circle-arrow-left') 10 | Back to swarm 11 | %dl 12 | %dt Embed code 13 | %dd 14 | %textarea{style: 'width: 400px; height: 150px;'} 15 | #{"
Fill out your answers on swarmize.com
"} 16 | 17 | %dt Token 18 | %dd 19 | %code= @swarm.token 20 | 21 | %dt Collector URL 22 | %dd 23 | %code= @swarm.collector_url 24 | 25 | %table.table 26 | %thead 27 | %tr 28 | %th Field name 29 | %th Field code 30 | %th Field type 31 | %th Possible values 32 | %tbody 33 | - @swarm.swarm_fields.each do |f| 34 | %tr 35 | %td= f.field_name 36 | %td 37 | %code= f.field_code 38 | %td 39 | %code= f.field_type 40 | %td 41 | - case f.field_type 42 | - when 'yesno' 43 | %code yes 44 | %br 45 | %code no 46 | - when 'rating' 47 | %code #{f.minimum}...#{f.maximum} 48 | - when 'pick_one', 'pick_several' 49 | - if f.possible_values 50 | - f.possible_values.each do |v| 51 | %code= v.parameterize.underscore 52 | %br 53 | 54 | %p= link_to "API Documentation", doc_path('api') 55 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/delete.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Delete Swarm '#{@swarm.name}'? : Swarms : 3 | 4 | .page-header.swarmize-header 5 | %h1 6 | Delete swarm '#{@swarm.name}'? 7 | %p 8 | %a{:href => swarm_path(@swarm)} 9 | = glyphicon('circle-arrow-left') 10 | Back to swarm 11 | 12 | %p Are you sure you want to delete this swarm? 13 | 14 | %p 15 | = button_to "Delete", swarm_path(@swarm), :method => :delete, :class => "btn btn-default" 16 | or 17 | = link_to "Return to all swarms", swarms_path 18 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/draft.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Yet To Open : Swarms : 3 | 4 | %h1 Explore Swarms 5 | 6 | %ul.nav.nav-tabs 7 | %li 8 | %a{:href => swarms_path} 9 | All Swarms 10 | %span.badge= @all_swarms_count 11 | %li.active 12 | %a{:href => draft_swarms_path} 13 | Draft 14 | %span.badge= @open_swarms_count 15 | %li 16 | %a{:href => live_swarms_path} 17 | In Progress 18 | %span.badge= @live_swarms_count 19 | %li 20 | %a{:href => closed_swarms_path} 21 | Closed 22 | %span.badge= @closed_swarms_count 23 | 24 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @swarms} 25 | 26 | = will_paginate @swarms 27 | 28 | - if @current_user 29 | %p= link_to "New swarm", new_swarm_path 30 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/embed.html.haml: -------------------------------------------------------------------------------- 1 | - if @swarm.closed? 2 | = render :partial => 'swarms/embed_closed' 3 | - else 4 | = render :partial => 'swarms/embed_live' 5 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/index.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Swarms : 3 | 4 | %h1 Explore Swarms 5 | 6 | %ul.nav.nav-tabs 7 | %li.active 8 | %a{:href => swarms_path} 9 | All Swarms 10 | %span.badge= @all_swarms_count 11 | - if @current_user 12 | %li 13 | %a{:href => draft_swarms_path} 14 | Draft 15 | %span.badge= @open_swarms_count 16 | %li 17 | %a{:href => live_swarms_path} 18 | In Progress 19 | %span.badge= @live_swarms_count 20 | %li 21 | %a{:href => closed_swarms_path} 22 | Closed 23 | %span.badge= @closed_swarms_count 24 | 25 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @swarms} 26 | 27 | = will_paginate @swarms 28 | 29 | - if @current_user 30 | %p= link_to "New swarm", new_swarm_path 31 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/live.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Live Swarms : Swarms : 3 | 4 | %h1 Explore Swarms 5 | 6 | %ul.nav.nav-tabs 7 | %li 8 | %a{:href => swarms_path} 9 | All Swarms 10 | %span.badge= @all_swarms_count 11 | - if @current_user 12 | %li 13 | %a{:href => draft_swarms_path} 14 | Draft 15 | %span.badge= @open_swarms_count 16 | %li.active 17 | %a{:href => live_swarms_path} 18 | In Progress 19 | %span.badge= @live_swarms_count 20 | %li 21 | %a{:href => closed_swarms_path} 22 | Closed 23 | %span.badge= @closed_swarms_count 24 | 25 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @swarms} 26 | 27 | = will_paginate @swarms 28 | 29 | - if @current_user 30 | %p= link_to "New swarm", new_swarm_path 31 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/swarms/preview.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Preview '#{@swarm.name}' : Swarms : 3 | 4 | .page-header.swarmize-header 5 | %h1 Preview '#{@swarm.name}' 6 | %p= link_to "Back to swarm", @swarm 7 | 8 | %ul.nav.nav-tabs.swarmbuilder-nav.nav-justified 9 | %li 10 | %a{:href => edit_swarm_path(@swarm)} 11 | = glyphicon('pencil') 12 | Swarm Details 13 | %li 14 | %a{:href => fields_swarm_path(@swarm)} 15 | = glyphicon('list') 16 | Swarm Fields 17 | %li.active 18 | %a{:href => preview_swarm_path(@swarm)} 19 | = glyphicon('list-alt') 20 | Preview Swarm 21 | 22 | %script{type: 'text/javascript', src: '/js/swarmize-embed.js'} 23 | #swarmize-embedded-form{"data-swarmize-token" => @swarm.token} 24 | = link_to "Fill out your answers on swarmize.com", embed_swarm_path(@swarm) 25 | 26 | %p 27 | = link_to "← Alter fields".html_safe, fields_swarm_path(@swarm), :class => "btn btn-default" 28 | = link_to "Done!", @swarm, :class => "btn btn-default" 29 | 30 | %p= link_to "Back to swarm", @swarm 31 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/users/_user_header.html.haml: -------------------------------------------------------------------------------- 1 | .page-header.swarmize-header 2 | %h1 3 | Swarms for #{@user.name} 4 | - if @user == @current_user 5 | %span.label.label-success.swarmlive You! 6 | 7 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/users/closed.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Closed Swarms for #{@user.name} : Users : 3 | 4 | = render :partial => 'users/user_header' 5 | 6 | %ul.nav.nav-tabs 7 | %li 8 | %a{:href => user_path(@user)} 9 | All Swarms 10 | %span.badge= @all_swarms_count 11 | - if AccessPermission.can_see_user_drafts?(@current_user, @user) 12 | %li 13 | %a{:href => draft_user_path(@user)} 14 | Draft 15 | %span.badge= @open_swarms_count 16 | %li 17 | %a{:href => live_user_path(@user)} 18 | In Progress 19 | %span.badge= @live_swarms_count 20 | %li.active 21 | %a{:href => closed_user_path(@user)} 22 | Closed 23 | %span.badge= @closed_swarms_count 24 | 25 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @swarms} 26 | = will_paginate @swarms 27 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/users/delete.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Delete #{@user.name}? : Users : 3 | 4 | .page-header.swarmize-header 5 | %h1 Delete User '#{@user.name}'? 6 | 7 | %p Are you sure you want to delete this user? 8 | 9 | %p 10 | = button_to "Delete", user_path(@user), :method => :delete, :class => "btn btn-default" 11 | or 12 | = link_to "Return to all users", users_path 13 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/users/draft.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Swarms yet to open for #{@user.name} : Users : 3 | 4 | = render :partial => 'users/user_header' 5 | 6 | %ul.nav.nav-tabs 7 | %li 8 | %a{:href => user_path(@user)} 9 | All Swarms 10 | %span.badge= @all_swarms_count 11 | - if AccessPermission.can_see_user_drafts?(@current_user, @user) 12 | %li.active 13 | %a{:href => draft_user_path(@user)} 14 | Draft 15 | %span.badge= @open_swarms_count 16 | %li 17 | %a{:href => live_user_path(@user)} 18 | In Progress 19 | %span.badge= @live_swarms_count 20 | %li 21 | %a{:href => closed_user_path(@user)} 22 | Closed 23 | %span.badge= @closed_swarms_count 24 | 25 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @swarms} 26 | = will_paginate @swarms 27 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/users/index.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Users : 3 | 4 | .page-header.swarmize-header 5 | %h1 Users 6 | 7 | %p This page is intended for development purposes only: to enumerate who's in the system. 8 | 9 | %table.table 10 | %thead 11 | %tr 12 | %th Name 13 | %th Email 14 | %th Image 15 | %th Is Fake? 16 | %th Actions 17 | %tbody 18 | - for user in @users 19 | %tr 20 | %td= user.name 21 | %td= user.email 22 | %td= image_tag(user.image_url) 23 | %td 24 | - if user.is_fake? 25 | √ 26 | %td 27 | = link_to "View", user_path(user) 28 | | 29 | = link_to "Edit", edit_user_path(user) 30 | | 31 | = link_to "Delete", delete_user_path(user) 32 | 33 | = will_paginate @users 34 | 35 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/users/live.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | Live Swarms for #{@user.name} : Users : 3 | 4 | = render :partial => 'users/user_header' 5 | 6 | %ul.nav.nav-tabs 7 | %li 8 | %a{:href => user_path(@user)} 9 | All Swarms 10 | %span.badge= @all_swarms_count 11 | - if AccessPermission.can_see_user_drafts?(@current_user, @user) 12 | %li 13 | %a{:href => draft_user_path(@user)} 14 | Draft 15 | %span.badge= @open_swarms_count 16 | %li.active 17 | %a{:href => live_user_path(@user)} 18 | In Progress 19 | %span.badge= @live_swarms_count 20 | %li 21 | %a{:href => closed_user_path(@user)} 22 | Closed 23 | %span.badge= @closed_swarms_count 24 | 25 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @swarms} 26 | = will_paginate @swarms 27 | -------------------------------------------------------------------------------- /swarmize-dot-com/app/views/users/show.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :pagetitle do 2 | #{@user.name} : Users : 3 | 4 | = render :partial => 'users/user_header' 5 | 6 | %ul.nav.nav-tabs 7 | %li.active 8 | %a{:href => user_path(@user)} 9 | All Swarms 10 | %span.badge= @all_swarms_count 11 | - if AccessPermission.can_see_user_drafts?(@current_user, @user) 12 | %li 13 | %a{:href => draft_user_path(@user)} 14 | Draft 15 | %span.badge= @open_swarms_count 16 | %li 17 | %a{:href => live_user_path(@user)} 18 | In Progress 19 | %span.badge= @live_swarms_count 20 | %li 21 | %a{:href => closed_user_path(@user)} 22 | Closed 23 | %span.badge= @closed_swarms_count 24 | 25 | = render :partial => "swarms/swarm_table", :locals => {:swarms => @swarms} 26 | = will_paginate @swarms 27 | -------------------------------------------------------------------------------- /swarmize-dot-com/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /swarmize-dot-com/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /swarmize-dot-com/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /swarmize-dot-com/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | 4 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 5 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Swarmize::Application.initialize! 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Swarmize::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send. 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger. 20 | config.active_support.deprecation = :log 21 | 22 | # Raise an error on page load if there are pending migrations 23 | config.active_record.migration_error = :page_load 24 | 25 | # Debug mode disables concatenation and preprocessing of assets. 26 | # This option may cause significant delays in view rendering with a large 27 | # number of complex assets. 28 | config.assets.debug = true 29 | end 30 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/amazon_ses.rb: -------------------------------------------------------------------------------- 1 | ActionMailer::Base.add_delivery_method :ses, AWS::SES::Base, 2 | access_key_id: ENV['AWS_ACCESS_KEY_ID'], 3 | secret_access_key: ENV['AWS_SECRET_KEY'], 4 | server: 'email.eu-west-1.amazonaws.com' 5 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/aws.rb: -------------------------------------------------------------------------------- 1 | AWS.config(region: 'eu-west-1') 2 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/non_digest_assets.rb: -------------------------------------------------------------------------------- 1 | # this is now just in public/js 2 | # NonStupidDigestAssets.whitelist = ['swarmize-embed.js'] 3 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/omniauth.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.middleware.use OmniAuth::Builder do 2 | provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"], { 3 | :prompt => 'select_account' 4 | } 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure your secret_key_base is kept private 11 | # if you're sharing your code publicly. 12 | Swarmize::Application.config.secret_key_base = 'dbe967e6beee21a22e385dac263b7636e6f6069a144d4993a0641f66183fa4507322439902e4a3530db95d2acd70a853a315d6347981f16fc1902a5bb5940d47' 13 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Swarmize::Application.config.session_store :cookie_store, key: '_swarmize_session' 4 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/will_paginate.rb: -------------------------------------------------------------------------------- 1 | WillPaginate.per_page = 10 2 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /swarmize-dot-com/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140712134916_create_swarms.rb: -------------------------------------------------------------------------------- 1 | class CreateSwarms < ActiveRecord::Migration 2 | def change 3 | create_table :swarms do |t| 4 | t.string :name 5 | t.string :description 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140716162431_add_fields_to_swarm.rb: -------------------------------------------------------------------------------- 1 | class AddFieldsToSwarm < ActiveRecord::Migration 2 | def change 3 | add_column :swarms, :fields, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140722135555_add_open_close_to_swarm.rb: -------------------------------------------------------------------------------- 1 | class AddOpenCloseToSwarm < ActiveRecord::Migration 2 | def change 3 | add_column :swarms, :opens_at, :datetime 4 | add_column :swarms, :closes_at, :datetime 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140729093242_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration 2 | def change 3 | create_table :users do |t| 4 | t.string :email 5 | t.string :name 6 | t.string :image_url 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140729123430_add_user_id_to_swarms.rb: -------------------------------------------------------------------------------- 1 | class AddUserIdToSwarms < ActiveRecord::Migration 2 | def change 3 | add_column :swarms, :user_id, :integer 4 | add_index :swarms, :user_id 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140729133308_add_parent_swarm_id_to_swarms.rb: -------------------------------------------------------------------------------- 1 | class AddParentSwarmIdToSwarms < ActiveRecord::Migration 2 | def change 3 | add_column :swarms, :cloned_from, :integer, :default => nil 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140801101148_add_is_fake_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddIsFakeToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :is_fake, :boolean, :default => false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140801134513_add_spiked_field_to_swarms.rb: -------------------------------------------------------------------------------- 1 | class AddSpikedFieldToSwarms < ActiveRecord::Migration 2 | def change 3 | add_column :swarms, :is_spiked, :boolean, :default => false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140812130235_add_tokens_to_swarms.rb: -------------------------------------------------------------------------------- 1 | class AddTokensToSwarms < ActiveRecord::Migration 2 | def change 3 | add_column :swarms, :token, :string, :limit => 8 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140814134610_create_graph_configurations.rb: -------------------------------------------------------------------------------- 1 | class CreateGraphConfigurations < ActiveRecord::Migration 2 | def change 3 | create_table :graph_configurations do |t| 4 | t.string :title 5 | t.string :graph_type 6 | t.string :field 7 | t.string :viz_type 8 | t.text :options 9 | t.references :swarm 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140814135824_rename_graph_configurations.rb: -------------------------------------------------------------------------------- 1 | class RenameGraphConfigurations < ActiveRecord::Migration 2 | def change 3 | rename_table :graph_configurations, :graphs 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140814173018_create_swarm_fields.rb: -------------------------------------------------------------------------------- 1 | class CreateSwarmFields < ActiveRecord::Migration 2 | def change 3 | create_table :swarm_fields do |t| 4 | t.integer :field_index 5 | t.string :field_type 6 | t.string :field_name 7 | t.string :field_code 8 | t.text :hint 9 | t.string :sample_value 10 | t.boolean :compulsory, :default => false 11 | t.text :possible_values 12 | t.integer :minimum, :default => nil 13 | t.integer :maximum, :default => nil 14 | t.references :swarm 15 | t.timestamps 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140815095709_remove_serialized_fields.rb: -------------------------------------------------------------------------------- 1 | class RemoveSerializedFields < ActiveRecord::Migration 2 | def change 3 | remove_column :swarms, :fields, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140826092139_create_access_permissions.rb: -------------------------------------------------------------------------------- 1 | class CreateAccessPermissions < ActiveRecord::Migration 2 | def change 3 | create_table :access_permissions do |t| 4 | t.references :swarm 5 | t.references :user 6 | t.string :email 7 | t.timestamps 8 | end 9 | 10 | add_index :access_permissions, :email 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140827092647_add_owner_field_to_permissions.rb: -------------------------------------------------------------------------------- 1 | class AddOwnerFieldToPermissions < ActiveRecord::Migration 2 | def change 3 | add_column :access_permissions, :is_owner, :boolean, :default => false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140827103714_add_creators_to_access_permissions.rb: -------------------------------------------------------------------------------- 1 | class AddCreatorsToAccessPermissions < ActiveRecord::Migration 2 | def change 3 | add_column :access_permissions, :creator_id, :integer, :default => nil 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140827162410_remove_creator_field_from_swarm.rb: -------------------------------------------------------------------------------- 1 | class RemoveCreatorFieldFromSwarm < ActiveRecord::Migration 2 | def change 3 | remove_column :swarms, :user_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140828114635_add_is_admin_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddIsAdminToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :is_admin, :boolean, :default => false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20140912101553_add_deleted_at_to_swarms.rb: -------------------------------------------------------------------------------- 1 | class AddDeletedAtToSwarms < ActiveRecord::Migration 2 | def up 3 | add_column :swarms, :deleted_at, :datetime 4 | add_index :swarms, :deleted_at 5 | Swarm.where(is_spiked: true).each do |swarm| 6 | swarm.update(deleted_at: Time.now) 7 | end 8 | remove_column :swarms, :is_spiked 9 | end 10 | 11 | def down 12 | add_column :swarms, :is_spiked, :boolean 13 | Swarm.only_deleted.each do |swarm| 14 | swarm.update(is_spiked: true) 15 | end 16 | remove_index :swarms, :deleted_at 17 | remove_column :swarms, :deleted_at 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20141010094402_remove_viz_type.rb: -------------------------------------------------------------------------------- 1 | class RemoveVizType < ActiveRecord::Migration 2 | def up 3 | Graph.all.each do |graph| 4 | graph.graph_type = graph.viz_type 5 | graph.save 6 | end 7 | remove_column :graphs, :viz_type 8 | end 9 | 10 | def down 11 | add_column :graphs, :viz_type, :string 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20141017093840_add_other_to_swarmfield.rb: -------------------------------------------------------------------------------- 1 | class AddOtherToSwarmfield < ActiveRecord::Migration 2 | def change 3 | add_column :swarm_fields, :other, :boolean, :default => false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20141017131200_add_toggles_to_swarm.rb: -------------------------------------------------------------------------------- 1 | class AddTogglesToSwarm < ActiveRecord::Migration 2 | def change 3 | add_column :swarms, :display_title, :boolean, :default => true 4 | add_column :swarms, :display_description, :boolean, :default => true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20141017150238_change_other_to_allow_other.rb: -------------------------------------------------------------------------------- 1 | class ChangeOtherToAllowOther < ActiveRecord::Migration 2 | def change 3 | rename_column :swarm_fields, :other, :allow_other 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20141021114539_change_desscription_to_text_field.rb: -------------------------------------------------------------------------------- 1 | class ChangeDesscriptionToTextField < ActiveRecord::Migration 2 | def up 3 | change_column :swarms, :description, :text 4 | end 5 | def down 6 | change_column :swarms, :description, :string 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/migrate/20141204145601_allow_pick_one_to_be_select.rb: -------------------------------------------------------------------------------- 1 | class AllowPickOneToBeSelect < ActiveRecord::Migration 2 | def change 3 | add_column :swarm_fields, :display_as_select, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /swarmize-dot-com/deploy-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "appname": "Application Name", 3 | "live_env": "Live Environment Name", 4 | "aws_region": "eu-west-1", 5 | "s3_bucket": "bucketname", 6 | "app_dir": "subdir_of_repo", 7 | "slack_key": "slack_key_here" 8 | } 9 | -------------------------------------------------------------------------------- /swarmize-dot-com/eb_name.txt: -------------------------------------------------------------------------------- 1 | Swarmize 2 | -------------------------------------------------------------------------------- /swarmize-dot-com/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/lib/assets/.keep -------------------------------------------------------------------------------- /swarmize-dot-com/lib/dummy.rb: -------------------------------------------------------------------------------- 1 | class Dummy 2 | def self.create_fake_user 3 | u = User.new 4 | first_name = Faker::Name.first_name 5 | last_name = Faker::Name.last_name 6 | 7 | u.name = [first_name,last_name].join(" ") 8 | u.email = Faker::Internet.email(first_name) 9 | 10 | u.is_fake = true 11 | 12 | u.save 13 | u 14 | end 15 | 16 | def self.create_dummy_preopen_swarm(user, n=1) 17 | s = Dummy.create_dummy_swarm(user, (Time.now+n.days), nil) 18 | s 19 | end 20 | 21 | def self.create_dummy_live_swarm(user, n=1) 22 | s = Dummy.create_dummy_swarm(user, (Time.now-n.days), (Time.now+n.days)) 23 | s 24 | end 25 | 26 | def self.create_dummy_closed_swarm(user, n=1) 27 | s = Dummy.create_dummy_swarm(user, (Time.now-(2*n).days), (Time.now-n.days)) 28 | s 29 | end 30 | 31 | def self.create_dummy_swarm(user,opens,closes) 32 | s = Swarm.new 33 | s.user = user 34 | s.opens_at = opens 35 | s.closes_at = closes 36 | s.name = Faker::Lorem.sentence(2) 37 | s.description = Faker::Lorem.sentences(3).join(" ") 38 | s.save 39 | s 40 | end 41 | 42 | def self.destroy_fake_data 43 | fake_users = User.where(:is_fake => true) 44 | fake_users.each do |u| 45 | u.swarms.destroy_all 46 | end 47 | 48 | fake_users.destroy_all 49 | end 50 | 51 | end 52 | -------------------------------------------------------------------------------- /swarmize-dot-com/lib/dynamo_sync.rb: -------------------------------------------------------------------------------- 1 | class DynamoSync 2 | def self.sync(swarm) 3 | if Rails.env.production? 4 | @@dynamo ||= AWS::DynamoDB.new 5 | swarms_table = @@dynamo.tables['swarms'].load_schema 6 | 7 | if swarm.deleted_at 8 | delete(swarm) 9 | else 10 | swarms_table.items.create(token: swarm.token, 11 | definition: swarm.to_json) 12 | end 13 | end 14 | end 15 | 16 | def self.delete(swarm) 17 | if Rails.env.production? 18 | @@dynamo ||= AWS::DynamoDB.new 19 | swarms_table = @@dynamo.tables['swarms'].load_schema 20 | 21 | swarms_table.items[swarm.token].delete 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /swarmize-dot-com/lib/email_validator.rb: -------------------------------------------------------------------------------- 1 | class EmailValidator 2 | def self.is_valid_email?(email) 3 | email = email.downcase 4 | (email == 'tom@infovore.org') || (email =~ /@theguardian\.com$/) || (email =~ /@guardian\.co\.uk$/) 5 | end 6 | 7 | def self.normalize_email(email) 8 | email.downcase.gsub('theguardian.com', 'guardian.co.uk') 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /swarmize-dot-com/lib/swarm_csv_tool.rb: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | include ApplicationHelper 3 | 4 | class SwarmCSVTool 5 | attr_reader :swarm 6 | 7 | def initialize(swarm) 8 | @swarm = swarm 9 | 10 | @header_hash = {timestamp: 'Timestamp'} 11 | @swarm.swarm_fields.each do |f| 12 | @header_hash[f.field_code.to_sym] = f.field_name 13 | if f.derived_field_suffixes.any? 14 | f.derived_field_suffixes.each do |df| 15 | @header_hash["#{f.field_code}#{df}"] = "#{f.field_name} [#{df}]" 16 | end 17 | end 18 | end 19 | 20 | @public_header_keys = @header_hash.keys.select do |key| 21 | @swarm.public_field_codes.include?(key.to_s) 22 | end 23 | 24 | @public_header_values = @public_header_keys.map do |key| 25 | @header_hash[key] 26 | end 27 | end 28 | 29 | def headers 30 | # true indicates it's a header 31 | CSV::Row.new(@header_hash.keys, @header_hash.values, true) 32 | end 33 | 34 | def public_headers 35 | # true indicates it's a header 36 | CSV::Row.new(@public_header_keys, @public_header_values, true) 37 | end 38 | 39 | def result_to_row(result) 40 | cols = @header_hash.keys.map do |key| 41 | result.send(key) 42 | end 43 | CSV::Row.new(@header_hash.keys, cols, false) 44 | end 45 | 46 | def result_to_public_row(result) 47 | cols = @public_header_keys.map do |key| 48 | result.send(key) 49 | end 50 | CSV::Row.new(@header_hash.keys, cols, false) 51 | end 52 | end 53 | 54 | -------------------------------------------------------------------------------- /swarmize-dot-com/lib/swarmize_oembed.rb: -------------------------------------------------------------------------------- 1 | require 'open-uri' 2 | 3 | class SwarmizeOembed 4 | class << self 5 | include Rails.application.routes.url_helpers 6 | include ActionView::Helpers::AssetUrlHelper 7 | end 8 | 9 | def self.matches_scheme(url) 10 | url =~ /http:\/\/www\.swarmize\.com\/swarms\/(\w{8})\/?/ 11 | end 12 | 13 | def self.extract_token(url) 14 | url.gsub("http://www.swarmize.com/swarms/", "").split("/").first 15 | end 16 | 17 | def self.for(swarm, options={}) 18 | if options[:maxwidth] 19 | width = options[:maxwidth].to_i 20 | else 21 | width = 460 22 | end 23 | 24 | if options[:maxheight] 25 | height = options[:maxheight].to_i 26 | else 27 | height = swarm.estimate_form_height 28 | end 29 | 30 | html = "
Fill out your answers on swarmize.com
" 31 | 32 | { 33 | type: "rich", 34 | version: "1.0", 35 | title: swarm.name, 36 | provider_name: "Swarmize", 37 | provider_url: "http://www.swarmize.com", 38 | width: width, 39 | height: height, 40 | html: html 41 | }.to_json 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /swarmize-dot-com/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/lib/tasks/.keep -------------------------------------------------------------------------------- /swarmize-dot-com/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/log/.keep -------------------------------------------------------------------------------- /swarmize-dot-com/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PACKAGE_FILENAME=$1 4 | DIRECTORY=$2 5 | 6 | cd .. 7 | 8 | git archive -o ${DIRECTORY}/${PACKAGE_FILENAME} master:${DIRECTORY} 9 | -------------------------------------------------------------------------------- /swarmize-dot-com/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

The page you were looking for doesn't exist.

54 |

You may have mistyped the address or the page may have moved.

55 |
56 |

If you are the application owner check the logs for more information.

57 | 58 | 59 | -------------------------------------------------------------------------------- /swarmize-dot-com/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

The change you wanted was rejected.

54 |

Maybe you tried to change something you didn't have access to.

55 |
56 |

If you are the application owner check the logs for more information.

57 | 58 | 59 | -------------------------------------------------------------------------------- /swarmize-dot-com/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

We're sorry, but something went wrong.

54 |
55 |

If you are the application owner check the logs for more information.

56 | 57 | 58 | -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/add-fields.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/add-fields.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/clone.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/clone.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/create.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/create.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/csv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/csv.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/embed-code.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/embed-code.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/graphs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/graphs.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/login.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/login.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/move-delete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/move-delete.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/open.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/open.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/permissions.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/permissions.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/preview.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/table.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/thumb.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/broadband_survey/view-form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/broadband_survey/view-form.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/indyref/indyref-graph1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/indyref/indyref-graph1.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/indyref/indyref-graph2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/indyref/indyref-graph2.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/indyref/indyref-graph3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/indyref/indyref-graph3.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/indyref/indyref1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/indyref/indyref1.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/indyref/thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/indyref/thumb.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/realtime_clicker/aggregate-field.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/realtime_clicker/aggregate-field.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/realtime_clicker/api-keys.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/realtime_clicker/api-keys.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/realtime_clicker/get-code.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/realtime_clicker/get-code.gif -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/realtime_clicker/map-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/realtime_clicker/map-demo.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/realtime_clicker/preview-form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/realtime_clicker/preview-form.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/realtime_clicker/results-over-time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/realtime_clicker/results-over-time.png -------------------------------------------------------------------------------- /swarmize-dot-com/public/case_studies/realtime_clicker/thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/case_studies/realtime_clicker/thumb.jpg -------------------------------------------------------------------------------- /swarmize-dot-com/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/favicon.ico -------------------------------------------------------------------------------- /swarmize-dot-com/public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /swarmize-dot-com/public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /swarmize-dot-com/public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /swarmize-dot-com/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/controllers/admin_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'controllers/shared_examples_for_controllers' 3 | 4 | describe AdminController do 5 | describe "GET #show" do 6 | it_should_behave_like "it needs an admin user", :get, :show 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/controllers/api_tokens_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/controllers/case_studies_controller_spec.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/spec/controllers/case_studies_controller_spec.rb -------------------------------------------------------------------------------- /swarmize-dot-com/spec/controllers/csv_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe CsvController do 4 | describe "GET #show" do 5 | describe 'for a user who is not logged in' do 6 | it "should redirect to login" do 7 | get :show, :swarm_id => 1 8 | expect(response).to redirect_to(login_path) 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/controllers/home_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe HomeController do 4 | render_views 5 | 6 | describe "GET #show" do 7 | describe "when logged out" do 8 | it "renders the show page" do 9 | get :show 10 | expect(response).to render_template :show 11 | end 12 | 13 | it "renders the logged out partial" do 14 | get :show 15 | expect(response).to render_template(:partial => "home/_logged_out") 16 | end 17 | 18 | end 19 | 20 | describe "when logged in" do 21 | before do 22 | user = FactoryGirl.create(:user) 23 | session[:user_id] = user.id 24 | end 25 | 26 | it "renders the logged in partial" do 27 | get :show 28 | expect(response).to render_template(:partial => "home/_logged_in") 29 | end 30 | end 31 | 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/controllers/oembed_controller_spec.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/spec/controllers/oembed_controller_spec.rb -------------------------------------------------------------------------------- /swarmize-dot-com/spec/controllers/permissions_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe PermissionsController do 4 | 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/controllers/search_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'controllers/shared_examples_for_controllers' 3 | 4 | describe SearchController do 5 | describe "GET #results" do 6 | it_should_behave_like "it needs login", :get, :results, :query => 'swarm' 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/controllers/sessions_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SessionsController do 4 | 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/controllers/swarm_import_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SwarmImportController do 4 | 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/controllers/utils_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe UtilsController do 4 | 5 | end 6 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/factories/access_permissions.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :access_permission do 3 | user 4 | swarm 5 | email { user.email } 6 | is_owner false 7 | creator_id nil 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/factories/graphs.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :graph do 3 | title "Graph Title" 4 | graph_type "pie" 5 | field "postcode" 6 | swarm 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/factories/swarm_fields.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :swarm_field do 3 | field_index 0 4 | field_type 'text' 5 | field_name 'Enter some text' 6 | hint 'Anything will do' 7 | sample_value 'foo' 8 | swarm 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/factories/swarms.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :swarm do |s| 3 | s.name "A test swarm" 4 | s.description "Test swarm description" 5 | s.token "abcdefgh" 6 | 7 | factory :swarm_opens_in_the_future do 8 | opens_at Time.zone.now + 1.day 9 | end 10 | 11 | factory :swarm_already_opened do 12 | opens_at Time.zone.now - 1.day 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | require 'faker' 2 | 3 | FactoryGirl.define do 4 | factory :user do |f| 5 | f.name Faker::Name.name 6 | f.email Faker::Internet.email 7 | f.image_url "" 8 | f.is_fake false 9 | 10 | factory :admin do 11 | is_admin true 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/helpers/admin_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | # Specs in this file have access to a helper object that includes 4 | # the AdminHelper. For example: 5 | # 6 | # describe AdminHelper do 7 | # describe "string concat" do 8 | # it "concats two strings with spaces" do 9 | # expect(helper.concat_strings("this","that")).to eq("this that") 10 | # end 11 | # end 12 | # end 13 | describe AdminHelper do 14 | end 15 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/helpers/graphs_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | # Specs in this file have access to a helper object that includes 4 | # the GraphsHelper. For example: 5 | # 6 | # describe GraphsHelper do 7 | # describe "string concat" do 8 | # it "concats two strings with spaces" do 9 | # expect(helper.concat_strings("this","that")).to eq("this that") 10 | # end 11 | # end 12 | # end 13 | describe GraphsHelper do 14 | end 15 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/helpers/home_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | # Specs in this file have access to a helper object that includes 4 | # the HomeHelper. For example: 5 | # 6 | # describe HomeHelper do 7 | # describe "string concat" do 8 | # it "concats two strings with spaces" do 9 | # expect(helper.concat_strings("this","that")).to eq("this that") 10 | # end 11 | # end 12 | # end 13 | describe HomeHelper do 14 | end 15 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/helpers/permissions_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | describe PermissionsHelper do 3 | end 4 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/helpers/search_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | # Specs in this file have access to a helper object that includes 4 | # the SearchHelper. For example: 5 | # 6 | # describe SearchHelper do 7 | # describe "string concat" do 8 | # it "concats two strings with spaces" do 9 | # expect(helper.concat_strings("this","that")).to eq("this that") 10 | # end 11 | # end 12 | # end 13 | describe SearchHelper do 14 | end 15 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/helpers/sessions_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | # Specs in this file have access to a helper object that includes 4 | # the SessionsHelper. For example: 5 | # 6 | # describe SessionsHelper do 7 | # describe "string concat" do 8 | # it "concats two strings with spaces" do 9 | # expect(helper.concat_strings("this","that")).to eq("this that") 10 | # end 11 | # end 12 | # end 13 | describe SessionsHelper do 14 | end 15 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/helpers/swarms_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | # Specs in this file have access to a helper object that includes 4 | # the SwarmsHelper. For example: 5 | # 6 | # describe SwarmsHelper do 7 | # describe "string concat" do 8 | # it "concats two strings with spaces" do 9 | # expect(helper.concat_strings("this","that")).to eq("this that") 10 | # end 11 | # end 12 | # end 13 | describe SwarmsHelper do 14 | end 15 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/helpers/users_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | # Specs in this file have access to a helper object that includes 4 | # the UsersHelper. For example: 5 | # 6 | # describe UsersHelper do 7 | # describe "string concat" do 8 | # it "concats two strings with spaces" do 9 | # expect(helper.concat_strings("this","that")).to eq("this that") 10 | # end 11 | # end 12 | # end 13 | describe UsersHelper do 14 | end 15 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/lib/sample.json: -------------------------------------------------------------------------------- 1 | 2 | {"name":"#indyref survey","description":"Extracting data from the #indyref hashtag on Twitter, capturing numbers of #no, #voteno and #yes, #voteyes votes.","fields":[{"field_type":"yesno","field_name":"Are you for an independent Scotland?","field_name_code":"are_you_for_an_independent_scotland","compulsory":true},{"field_type":"text","field_name":"What is your twitter screen name?","field_name_code":"screen_name","compulsory":true},{"field_type":"text","field_name":"Full text of tweet","field_name_code":"full_text_of_tweet","compulsory":true},{"field_type":"geolocation","field_name":"Where was your tweet from?","field_name_code":"where_was_your_tweet_from","compulsory":false}],"opens_at":"2014-09-13T19:46:04.575Z","closes_at":null,"token":"jtjsvwdc"} 3 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/lib/sample_field.json: -------------------------------------------------------------------------------- 1 | 2 | {"name":"#indyref survey","description":"Extracting data from the #indyref hashtag on Twitter, capturing numbers of #no, #voteno and #yes, #voteyes votes.","fields":[{"field_type":"yesno","field_name":"Are you for an independent Scotland?","field_name_code":"are_you_for_an_independent_scotland","compulsory":true}],"opens_at":"2014-09-13T19:46:04.575Z","closes_at":null} 3 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/lib/sample_field_possible_values.json: -------------------------------------------------------------------------------- 1 | {"name":"Prime Ministerial Debate (cloned)","description":"","fields":[{"field_type":"pick_one","field_name":"Who did you just agree with?","field_name_code":"who_did_you_just_agree_with","compulsory":true,"possible_values":{"david_cameron":"David Cameron","ed_miliband":"Ed Miliband","nick_clegg":"Nick Clegg"}}],"opens_at":"2014-09-08T12:03:54.305Z","closes_at":"2014-09-08T16:24:34.414Z"} 2 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/mailers/permission_mailer_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe PermissionMailer do 4 | end 5 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/models/email_validator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe EmailValidator do 4 | describe "testing an email address for validity" do 5 | it "should be valid if it is Tom" do 6 | expect(EmailValidator.is_valid_email?('tom@infovore.org')).to be_truthy 7 | end 8 | 9 | it "should be valid if it is Graham's guardian.com email" do 10 | expect(EmailValidator.is_valid_email?('graham.tackley@theguardian.com')).to be_truthy 11 | end 12 | 13 | it "should be valid if it is Matt's guardian.co.uk email" do 14 | expect(EmailValidator.is_valid_email?('matt.mcalister@guardian.co.uk')).to be_truthy 15 | end 16 | 17 | it "shouldn't be valid if it is Bob" do 18 | expect(EmailValidator.is_valid_email?('bob@example.org')).to be_falsey 19 | end 20 | end 21 | 22 | describe "normalising an email address" do 23 | it "should normalize guardian.com emails to guardian.co.uk emails" do 24 | expect(EmailValidator.normalize_email('SAMPLE@THEGUARDIAN.COM')).to eq('sample@guardian.co.uk') 25 | end 26 | it "should lowercase a non-lowercase email" do 27 | expect(EmailValidator.normalize_email('SAMPLE@GUARDIAN.CO.UK')).to eq('sample@guardian.co.uk') 28 | end 29 | it "should not alter tom's email" do 30 | expect(EmailValidator.normalize_email('tom@infovore.org')).to eq('tom@infovore.org') 31 | 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/models/graph_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Graph do 4 | end 5 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/models/swarm_field_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SwarmField do 4 | describe 'being created' do 5 | it 'should set its field_code correctly' do 6 | s = FactoryGirl.create(:swarm_field, :field_name => 'What is your postcode?') 7 | expect(s.field_code).to eq('what_is_your_postcode') 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /swarmize-dot-com/spec/support/factory_girl.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include FactoryGirl::Syntax::Methods 3 | end -------------------------------------------------------------------------------- /swarmize-dot-com/vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /swarmize-dot-com/vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/swarmize/d3b23b7e093c7d35f9ba68e7174bc026ed16b03f/swarmize-dot-com/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------