device-tracker 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/Gemfile +4 -0
  4. data/Rakefile +6 -0
  5. data/Readme.md +32 -0
  6. data/bin/console +14 -0
  7. data/bin/setup +7 -0
  8. data/device-tracker.gemspec +54 -0
  9. data/exe/device-tracker +50 -0
  10. data/lib/device/tracker.rb +19 -0
  11. data/lib/device/tracker/app.rb +25 -0
  12. data/lib/device/tracker/config-schema.json +53 -0
  13. data/lib/device/tracker/config.ru +9 -0
  14. data/lib/device/tracker/controllers/application_controller.rb +215 -0
  15. data/lib/device/tracker/controllers/devices_controller.rb +315 -0
  16. data/lib/device/tracker/controllers/heartbeat_controller.rb +55 -0
  17. data/lib/device/tracker/controllers/os_controller.rb +43 -0
  18. data/lib/device/tracker/controllers/transactions_controller.rb +21 -0
  19. data/lib/device/tracker/controllers/users_controller.rb +193 -0
  20. data/lib/device/tracker/db/data/devices.xlsx +0 -0
  21. data/lib/device/tracker/db/migrate/20150521071815_create_users.rb +13 -0
  22. data/lib/device/tracker/db/migrate/20150521082155_create_devices.rb +23 -0
  23. data/lib/device/tracker/db/migrate/20150521120335_create_operating_systems.rb +8 -0
  24. data/lib/device/tracker/db/migrate/20150527162242_create_transactions.rb +13 -0
  25. data/lib/device/tracker/db/migrate/20151027073050_create_heartbeat.rb +10 -0
  26. data/lib/device/tracker/db/migrate/20151028132946_add_user_verification.rb +7 -0
  27. data/lib/device/tracker/db/migrate/20151028141328_remove_is_active_from_users.rb +5 -0
  28. data/lib/device/tracker/db/migrate/20151029085629_add_password_reset_code_to_users.rb +7 -0
  29. data/lib/device/tracker/db/migrate/20151030130341_add_missing_column_to_devices.rb +7 -0
  30. data/lib/device/tracker/db/migrate/20151102141601_add_serial_number_to_devices.rb +7 -0
  31. data/lib/device/tracker/db/schema.rb +74 -0
  32. data/lib/device/tracker/db/seeds.rb +18 -0
  33. data/lib/device/tracker/dependencies.rb +15 -0
  34. data/lib/device/tracker/helpers/application_helper.rb +84 -0
  35. data/lib/device/tracker/models/device.rb +39 -0
  36. data/lib/device/tracker/models/heartbeat.rb +9 -0
  37. data/lib/device/tracker/models/operating_system.rb +8 -0
  38. data/lib/device/tracker/models/transaction.rb +56 -0
  39. data/lib/device/tracker/models/user.rb +22 -0
  40. data/lib/device/tracker/public/css/bootstrap-sortable.css +100 -0
  41. data/lib/device/tracker/public/css/bootstrap.min.css +5 -0
  42. data/lib/device/tracker/public/css/custom.css +88 -0
  43. data/lib/device/tracker/public/favicon.png +0 -0
  44. data/lib/device/tracker/public/favicon/android-chrome-144x144.png +0 -0
  45. data/lib/device/tracker/public/favicon/android-chrome-192x192.png +0 -0
  46. data/lib/device/tracker/public/favicon/android-chrome-36x36.png +0 -0
  47. data/lib/device/tracker/public/favicon/android-chrome-48x48.png +0 -0
  48. data/lib/device/tracker/public/favicon/android-chrome-72x72.png +0 -0
  49. data/lib/device/tracker/public/favicon/android-chrome-96x96.png +0 -0
  50. data/lib/device/tracker/public/favicon/apple-touch-icon-114x114.png +0 -0
  51. data/lib/device/tracker/public/favicon/apple-touch-icon-120x120.png +0 -0
  52. data/lib/device/tracker/public/favicon/apple-touch-icon-144x144.png +0 -0
  53. data/lib/device/tracker/public/favicon/apple-touch-icon-152x152.png +0 -0
  54. data/lib/device/tracker/public/favicon/apple-touch-icon-180x180.png +0 -0
  55. data/lib/device/tracker/public/favicon/apple-touch-icon-57x57.png +0 -0
  56. data/lib/device/tracker/public/favicon/apple-touch-icon-60x60.png +0 -0
  57. data/lib/device/tracker/public/favicon/apple-touch-icon-72x72.png +0 -0
  58. data/lib/device/tracker/public/favicon/apple-touch-icon-76x76.png +0 -0
  59. data/lib/device/tracker/public/favicon/apple-touch-icon-precomposed.png +0 -0
  60. data/lib/device/tracker/public/favicon/apple-touch-icon.png +0 -0
  61. data/lib/device/tracker/public/favicon/browserconfig.xml +12 -0
  62. data/lib/device/tracker/public/favicon/favicon-16x16.png +0 -0
  63. data/lib/device/tracker/public/favicon/favicon-32x32.png +0 -0
  64. data/lib/device/tracker/public/favicon/favicon-96x96.png +0 -0
  65. data/lib/device/tracker/public/favicon/favicon.ico +0 -0
  66. data/lib/device/tracker/public/favicon/manifest.json +41 -0
  67. data/lib/device/tracker/public/favicon/mstile-144x144.png +0 -0
  68. data/lib/device/tracker/public/favicon/mstile-150x150.png +0 -0
  69. data/lib/device/tracker/public/favicon/mstile-310x150.png +0 -0
  70. data/lib/device/tracker/public/favicon/mstile-310x310.png +0 -0
  71. data/lib/device/tracker/public/favicon/mstile-70x70.png +0 -0
  72. data/lib/device/tracker/public/favicon/safari-pinned-tab.svg +21 -0
  73. data/lib/device/tracker/public/fonts/glyphicons-halflings-regular.eot +0 -0
  74. data/lib/device/tracker/public/fonts/glyphicons-halflings-regular.svg +288 -0
  75. data/lib/device/tracker/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  76. data/lib/device/tracker/public/fonts/glyphicons-halflings-regular.woff +0 -0
  77. data/lib/device/tracker/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  78. data/lib/device/tracker/public/js/bootstrap-sortable.js +211 -0
  79. data/lib/device/tracker/public/js/bootstrap.min.js +7 -0
  80. data/lib/device/tracker/public/js/jquery-2.1.4.min.js +4 -0
  81. data/lib/device/tracker/version.rb +5 -0
  82. data/lib/device/tracker/views/404.erb +25 -0
  83. data/lib/device/tracker/views/_alert.erb +5 -0
  84. data/lib/device/tracker/views/_device_form.erb +52 -0
  85. data/lib/device/tracker/views/_device_list.erb +47 -0
  86. data/lib/device/tracker/views/_footer.erb +3 -0
  87. data/lib/device/tracker/views/_header.erb +97 -0
  88. data/lib/device/tracker/views/_heartbeat_list.erb +25 -0
  89. data/lib/device/tracker/views/_user_form.erb +30 -0
  90. data/lib/device/tracker/views/devices/edit.erb +11 -0
  91. data/lib/device/tracker/views/devices/index.erb +12 -0
  92. data/lib/device/tracker/views/devices/new.erb +10 -0
  93. data/lib/device/tracker/views/devices/show.erb +283 -0
  94. data/lib/device/tracker/views/devices/users.erb +14 -0
  95. data/lib/device/tracker/views/emails/new_password.erb +17 -0
  96. data/lib/device/tracker/views/emails/password_reset.erb +18 -0
  97. data/lib/device/tracker/views/emails/registration.erb +16 -0
  98. data/lib/device/tracker/views/emails/reminder.erb +15 -0
  99. data/lib/device/tracker/views/emails/verification.erb +18 -0
  100. data/lib/device/tracker/views/forgot_password.erb +6 -0
  101. data/lib/device/tracker/views/index.erb +38 -0
  102. data/lib/device/tracker/views/layout.erb +8 -0
  103. data/lib/device/tracker/views/login.erb +14 -0
  104. data/lib/device/tracker/views/operating_system/operating_systems.json.jbuilder +9 -0
  105. data/lib/device/tracker/views/os/manage.erb +38 -0
  106. data/lib/device/tracker/views/transactions/_transactions_list.erb +18 -0
  107. data/lib/device/tracker/views/transactions/index.erb +3 -0
  108. data/lib/device/tracker/views/users/edit.erb +9 -0
  109. data/lib/device/tracker/views/users/manage.erb +31 -0
  110. data/lib/device/tracker/views/users/new.erb +7 -0
  111. metadata +427 -0
@@ -0,0 +1,13 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users do |t|
4
+ t.string :username, index: true
5
+ t.string :name
6
+ t.string :password_digest
7
+ t.string :email
8
+ t.boolean :is_active, default: false
9
+ t.boolean :is_admin, default: false
10
+ t.timestamps null: false
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ class CreateDevices < ActiveRecord::Migration
2
+ def change
3
+
4
+ create_table :devices do |t|
5
+
6
+ t.string :unid, index: true
7
+ t.string :manufacturer
8
+ t.string :device
9
+ t.text :description
10
+ t.datetime :checked_out_since
11
+ t.boolean :available, default: true
12
+ t.string :imei, default: nil
13
+ t.belongs_to :operating_system
14
+ t.belongs_to :user
15
+
16
+ t.boolean :sim_card, default: false
17
+ t.boolean :debug_device, default: false
18
+
19
+ t.timestamps null: false
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,8 @@
1
+ class CreateOperatingSystems < ActiveRecord::Migration
2
+ def change
3
+ create_table :operating_systems do |t|
4
+ t.string :name
5
+ t.integer :api_level
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ class CreateTransactions < ActiveRecord::Migration
2
+ def change
3
+ create_table :transactions do |t|
4
+ t.string :transaction_type, index: true
5
+
6
+ t.belongs_to :user
7
+ t.belongs_to :device
8
+
9
+ t.text :description
10
+ t.datetime :created_at
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ class CreateHeartbeat < ActiveRecord::Migration
2
+ def change
3
+ create_table :heartbeats do |t|
4
+ t.decimal :longitude, { precision: 10, scale: 6 }
5
+ t.decimal :latitude, { precision: 10, scale: 6 }
6
+ t.belongs_to :device
7
+ t.timestamps null: false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ class AddUserVerification < ActiveRecord::Migration
2
+ def change
3
+ change_table :users do |t|
4
+ t.boolean :is_verified, default: false
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class RemoveIsActiveFromUsers < ActiveRecord::Migration
2
+ def change
3
+ remove_column :users, :is_active, :boolean, default: false
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class AddPasswordResetCodeToUsers < ActiveRecord::Migration
2
+ def change
3
+ change_table :users do |t|
4
+ t.string :reset_code, default: nil
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class AddMissingColumnToDevices < ActiveRecord::Migration
2
+ def change
3
+ change_table :devices do |t|
4
+ t.boolean :missing, default: false
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class AddSerialNumberToDevices < ActiveRecord::Migration
2
+ def change
3
+ change_table :devices do |t|
4
+ t.string :serial_number
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: UTF-8
2
+ # This file is auto-generated from the current state of the database. Instead
3
+ # of editing this file, please use the migrations feature of Active Record to
4
+ # incrementally modify your database, and then regenerate this schema definition.
5
+ #
6
+ # Note that this schema.rb definition is the authoritative source for your
7
+ # database schema. If you need to create the application database on another
8
+ # system, you should be using db:schema:load, not running all the migrations
9
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
11
+ #
12
+ # It's strongly recommended that you check this file into your version control system.
13
+
14
+ ActiveRecord::Schema.define(version: 20151109121036) do
15
+
16
+ create_table "devices", force: :cascade do |t|
17
+ t.string "unid"
18
+ t.string "manufacturer"
19
+ t.string "device"
20
+ t.text "description"
21
+ t.datetime "checked_out_since"
22
+ t.boolean "available", default: true
23
+ t.string "imei"
24
+ t.integer "operating_system_id"
25
+ t.integer "user_id"
26
+ t.boolean "sim_card", default: false
27
+ t.boolean "debug_device", default: false
28
+ t.datetime "created_at", null: false
29
+ t.datetime "updated_at", null: false
30
+ t.boolean "missing", default: false
31
+ t.integer "checkout_count", default: 0
32
+ t.string "serial_number"
33
+ end
34
+
35
+ add_index "devices", ["unid"], name: "index_devices_on_unid"
36
+
37
+ create_table "heartbeats", force: :cascade do |t|
38
+ t.decimal "longitude", precision: 10, scale: 6
39
+ t.decimal "latitude", precision: 10, scale: 6
40
+ t.integer "device_id"
41
+ t.datetime "created_at", null: false
42
+ t.datetime "updated_at", null: false
43
+ end
44
+
45
+ create_table "operating_systems", force: :cascade do |t|
46
+ t.string "name"
47
+ t.integer "api_level"
48
+ end
49
+
50
+ create_table "transactions", force: :cascade do |t|
51
+ t.string "transaction_type"
52
+ t.integer "user_id"
53
+ t.integer "device_id"
54
+ t.text "description"
55
+ t.datetime "created_at"
56
+ end
57
+
58
+ add_index "transactions", ["transaction_type"], name: "index_transactions_on_transaction_type"
59
+
60
+ create_table "users", force: :cascade do |t|
61
+ t.string "username"
62
+ t.string "name"
63
+ t.string "password_digest"
64
+ t.string "email"
65
+ t.boolean "is_admin", default: false
66
+ t.datetime "created_at", null: false
67
+ t.datetime "updated_at", null: false
68
+ t.boolean "is_verified", default: false
69
+ t.string "reset_code"
70
+ end
71
+
72
+ add_index "users", ["username"], name: "index_users_on_username"
73
+
74
+ end
@@ -0,0 +1,18 @@
1
+ module Device
2
+ module Tracker
3
+ class Seed
4
+ def self.seed(name: name, password: password, email: email)
5
+ user = User.find_or_create_by({
6
+ name: name,
7
+ username: 'admin',
8
+ email: email,
9
+ is_admin: true,
10
+ is_verified: true
11
+ })
12
+ user.password = password
13
+ user.save
14
+ puts "Added #{user.username} to the database."
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ %w(
5
+ sinatra
6
+ sinatra/activerecord
7
+ sinatra/flash
8
+ sinatra/partial
9
+ tilt/erb
10
+ json
11
+ pony
12
+ ).each { |d| require d }
13
+
14
+ puts File.dirname(__FILE__)
15
+ Dir.glob("#{File.dirname(__FILE__)}/{helpers,controllers,models}/*.rb").each { |file| require file }
@@ -0,0 +1,84 @@
1
+ module Device
2
+ module Tracker
3
+ module ApplicationHelper
4
+
5
+ SECRET = 'mYR4nd0mAr$eS3cr3t/fuGg1wugGl3'
6
+
7
+ def is_admin?
8
+ user = get_logged_in_user
9
+ return if user.nil?
10
+ user[:is_admin]
11
+ end
12
+
13
+ def value_for(name, object)
14
+ if flash[name]
15
+ flash[name]
16
+ elsif object
17
+ object.send name
18
+ end
19
+ end
20
+
21
+ def get_logged_in_user
22
+ session[:user] unless session[:user].nil?
23
+ end
24
+
25
+ def generate_activation_code(size = 6)
26
+ charset = %w{0 1 2 3 4 6 7 8 9 A C D E F G H J K M N P Q R T V W X Y Z}
27
+ (0...size).map{ charset.to_a[rand(charset.size)] }.join
28
+ end
29
+
30
+ def change_password?(params)
31
+ if params[:user][:password].empty? or params[:user][:password_confirmation].empty?
32
+ false
33
+ else
34
+ true
35
+ end
36
+ end
37
+
38
+ def protected!
39
+ if session[:user].nil?
40
+ create_flash "info", ["You must be logged in to see this page."]
41
+ redirect "/login"
42
+ elsif !session[:user][:is_verified]
43
+ create_flash "info", ["You account is awaiting verification by an admin."]
44
+ redirect "/login"
45
+ end
46
+ end
47
+
48
+ def report_transaction(message, type, device = nil)
49
+ transaction = Transaction.new
50
+
51
+ if session[:user]
52
+ transaction.user_id = get_logged_in_user[:id]
53
+ end
54
+
55
+ transaction.description = message
56
+ transaction.transaction_type = type
57
+ transaction.device_id = device.id unless device.nil?
58
+
59
+ transaction.save!
60
+ end
61
+
62
+ def perform_admin_check
63
+ user = get_logged_in_user
64
+ if user.nil? or user[:is_admin] == false
65
+ create_flash "warning", ["#{user[:name]} does not have permissions to access this page."]
66
+ redirect back
67
+ end
68
+ end
69
+
70
+ def create_flash(type, message)
71
+ flash[:message] = {css_class: type, message: message }
72
+ end
73
+
74
+ def valid_heartbeat?(data)
75
+ if !data["heartbeat"].nil? and !data["heartbeat"]["longitude"].nil? and
76
+ !data["heartbeat"]["latitude"].nil? and !data["heartbeat"]["device_id"].nil?
77
+ return true
78
+ end
79
+ false
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,39 @@
1
+ require 'date'
2
+
3
+ module Device
4
+ module Tracker
5
+ class Device < ActiveRecord::Base
6
+
7
+ belongs_to :operating_system
8
+ belongs_to :user
9
+
10
+ has_many :transactions
11
+ has_many :heartbeats
12
+
13
+ validates :unid, :manufacturer, :device, :description, :operating_system, presence: true
14
+
15
+ validates :unid, uniqueness: true
16
+
17
+ def unid=(unid)
18
+ self[:unid] = unid.upcase
19
+ end
20
+
21
+ def full_name
22
+ self[:manufacturer] + " " + self[:device]
23
+ end
24
+
25
+ def passed_use_by_date?
26
+ (! self[:available] and (Time.now - self[:checked_out_since]).to_i / 1.day >= 3)
27
+ end
28
+
29
+ def checked_out_since_formatted
30
+ self[:checked_out_since].strftime("%A, %d %B %Y at %I:%M%p")
31
+ end
32
+
33
+ def days_checked_out
34
+ (Time.now.to_date - self[:checked_out_since].to_date).floor
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,9 @@
1
+ module Device
2
+ module Tracker
3
+ class Heartbeat < ActiveRecord::Base
4
+ validates :longitude, :latitude, :device_id, presence: true
5
+
6
+ belongs_to :device
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module Device
2
+ module Tracker
3
+ class OperatingSystem < ActiveRecord::Base
4
+ validates :name, presence: true, uniqueness: true
5
+ has_many :devices
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,56 @@
1
+ require "date"
2
+
3
+ module Device
4
+ module Tracker
5
+ class Transaction < ActiveRecord::Base
6
+
7
+ belongs_to :user
8
+ belongs_to :device
9
+
10
+ validates :transaction_type, :description, presence: true
11
+ validates :transaction_type, inclusion: { in: %w(CHECKOUT RETURN REGISTRATION LOGIN ACTIVATE DEACTIVATE DELETION MISSING FOUND),
12
+ message: "%{value} is not a valid transaction." }
13
+
14
+ def created_at
15
+ self[:created_at].strftime("%a %d %b %Y at %H:%M:%S")
16
+ end
17
+
18
+ def self.missing
19
+ "MISSING"
20
+ end
21
+
22
+ def self.found
23
+ "FOUND"
24
+ end
25
+
26
+ def self.activate
27
+ "ACTIVATE"
28
+ end
29
+
30
+ def self.deactivate
31
+ "DEACTIVATE"
32
+ end
33
+
34
+ def self.checkout
35
+ "CHECKOUT"
36
+ end
37
+
38
+ def self.return
39
+ "RETURN"
40
+ end
41
+
42
+ def self.registration
43
+ "REGISTRATION"
44
+ end
45
+
46
+ def self.login
47
+ "LOGIN"
48
+ end
49
+
50
+ def self.deletion
51
+ "DELETION"
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,22 @@
1
+ require 'bcrypt'
2
+
3
+ module Device
4
+ module Tracker
5
+ class User < ActiveRecord::Base
6
+ has_secure_password
7
+ has_many :devices
8
+ has_many :transactions
9
+ validates :username, :name, :email, presence: true
10
+ validates :password, :presence => { :if => :password_required? }
11
+ validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
12
+ validates :username, uniqueness: true
13
+ validates :email, uniqueness: true
14
+ validates_confirmation_of :password
15
+
16
+ protected
17
+ def password_required?
18
+ self.new_record?
19
+ end
20
+ end
21
+ end
22
+ end