Frontier DB


Blue Medora needed to create an app that monitored databases and analyze them reporting the health of the data base. It was named Frontier DB.

We first created an MVP back in 2014 with them and it worked with their users so this time they wanted to make the same concept and but way more robust.


3 months

Design of ui and ux

We started the design process with some low fidelity mockups to learn about the interaction and the user flows.

Then we designed the high fidelity mockups defining a simple user interface with large blue buttons and use of recognized icons to create a familiarity in design and guide the user.

After, we implemented the views and worked with responsive layouts to assure the best mobile experience.


Development

This application was developed in Ruby on Rails and Node.js. We integrated with DynamoDB Locally and Amazon Web Services.

Performance in consultations, session and users, allows you to get a consolidated view of all database view using an icon server and provides recommendations to improve performance and reduce downtime.

With the tracking databases you can get a full report graphically and easy to understand, resulting in effective and efficient data collection.

require 'aws-sdk-core'

class SQS

  def initialize
    @config = ActiveRecord::Base.connection_config
    Aws.config.update({region: @config[:dynamo_region]})
    @sqs = Aws::SQS::Client.new
  end

  def create_relationship_requests(queue_name)
    create_job_per_company(queue_name, 'update_relationships', 'Update Relationships')
  end

  def create_environment_metric_requests(queue_name)
    create_job_per_company(queue_name, 'update_environment_metrics', 'Update Environment Metrics')
  end

  def create_recommendation_requests(queue_name)
    create_job_per_company(queue_name, 'update_recommendations', 'Update Recommendations')
  end

  def create_job_per_company(queue_name, endpoint, task_name)
    return if Company.count == 0

    # Space the jobs out over the next 10 minutes
    delay = (10 * 60) / Company.count

    requests = Company.all.each_with_index.map do |company, index|
      {
        id: "company_#{company.id}_#{endpoint}",
        message_body: 'elasticbeanstalk job',
        delay_seconds: (1 + (index * delay)),
        message_attributes: {
          "beanstalk.sqsd.path" => {
            data_type: "String",
            string_value: "/#{endpoint}/#{company.id}"
          }, "beanstalk.sqsd.scheduled_time" => {
            data_type: "String",
            string_value: Time.now.utc.iso8601(3)
          }, "beanstalk.sqsd.task_name" => {
            data_type: "String",
            string_value: task_name
          }
        }
      }
    end

    add_jobs(queue_name, requests)
  end

  def add_jobs(queue_name, entries)
    Rails.logger.info "Adding <#{entries.length}> jobs to queue <#{queue_name}>"

    get_url_response = @sqs.get_queue_url(
                          {
                            queue_name: queue_name
                          })
    queue_url = get_url_response.queue_url

    until entries.empty?
      current_entries = entries.shift(10)
      message_response = @sqs.send_message_batch(
                            {
                              queue_url: queue_url,
                              entries: current_entries
                            })
      if message_response.successful.length > 0
        Rails.logger.info "Successfully received message(s) <#{message_response.successful}>"
      end
      if message_response.failed.length > 0
        Rails.logger.warn "Message(s) not recieved by SQS Queue"
        Rails.logger.info "Response: <#{message_response.failed}>"
      end
    end

    entries
  end
end
class LandingPagesController < ApplicationController
  layout 'public', :only => [:landing]

  def landing
    redirect_to dashboard_path if logged_in?
    @show_navbar = true
  end

  def dashboard_initialization
    require_permission! :see_dashboard, User
    @databases = Metric.new.databases(current_user)
    @danger_databases_count  = @databases.count{|d| d.status == Health::DANGER}
    @warning_databases_count = @databases.count{|d| d.status == Health::WARNING}

    #TODO - recommendations should be filted by databaseType, and pull parent and child relationships
    # currently the only recommendations we make, we always want to show
    @recommendation_count = current_user.company.recommendation_instances.count

    #TODO - needs to pull back children as well, once there are children with alerts.
    # this could be done by changing the relationships join to look for a match on parent or child
    @alert_instances = current_user.company.entities.joins(
      "LEFT JOIN relationships ON relationships.child_id = entities.id
      LEFT JOIN entities parents_entities ON parents_entities.id = relationships.parent_id
      INNER JOIN alert_instances ON entities.id = alert_instances.entity_id OR parents_entities.id = alert_instances.entity_id"
    ).includes(:alert_instances => :alert_template).where(
      "entities.entity_type" => DatabaseType.entity_types
    ).map { |entity| [entity.alert_instances, entity.parents.map(&:alert_instances)].flatten }.flatten.uniq
    @alert_count = @alert_instances.count
  end

  def internal_dashboard
    dashboard_initialization
    @collectors     = Metric.new.collector_instances(current_user)
    @server_plugins = Metric.new.plugin_instances(current_user)
  end

  def global_alerts
    dashboard_initialization
    @global_alerts = @alert_instances
  end

  def global_recommendations
    dashboard_initialization
    @global_recommendations = current_user.company.recommendation_instances.includes(:recommendation_template)
  end

  def table_overview
    dashboard_initialization
  end

  def aggregates_chart
    company_environment_entity = Entity.where(company_id: current_user.company.id, entity_type: "_internal_environment").first
    aggregates = Dynamo.new.get_last_24_hours_aggregates(company_environment_entity.entity_key)

    data_points = {
      warning: [],
      critical: []
    }

    aggregates.each do |aggregate|
      time = Time.parse(aggregate["Time"]).to_i * 1000
      data_points[:warning] << {x: time, y: aggregate["warning_database_count"].to_i}
      data_points[:critical] << {x: time, y: aggregate["critical_database_count"].to_i}
    end

    render :json => {
      data: data_points
    }.to_json
  end

  def test
    @entity = Entity.new
    @entity.connection = Connection.new
    render :layout => false
  end
end

Our Process

Meetings

We have weekly calls where we discuss progress. We use Skype, Google Hangouts or GotoMeeting.

We agreed on a day and time that worked for everyone.

Planning

In the beginning we send a timeline specifying the planning and tasks for that week.

These tasks are the same cards we have planned in Trello.

Trello

We translate all requirements into stories in Trello Cards.

Trello is the place to add any specification, ask questions or comments to the team.

Deliveries

Once the tasks are done they go through a testing process and we pass them for your approval.

Every week we will pass cards onto the client list for you to give us feedback.

Quality Assurance

Once the functionality is completed one of our functional testers will try to break the feature.

We either move it to Needs Improvement or pass it to you for feedback.

Feedback

We need the support from you to review the cards and Approve them or tell us how to improve it.

This is key to meet goals and being on time in the development of the application.