jobshop 0.0.131 → 0.0.157
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/Rakefile +16 -9
- data/app/controllers/concerns/jobshop/authentication_handler.rb +15 -0
- data/app/controllers/concerns/jobshop/authorization_handler.rb +29 -0
- data/app/controllers/jobshop/application_controller.rb +5 -32
- data/app/controllers/jobshop/organizations/lookups_controller.rb +18 -0
- data/app/controllers/jobshop/places_controller.rb +1 -1
- data/app/controllers/jobshop/{welcome/registrations_controller.rb → registrations_controller.rb} +2 -7
- data/app/controllers/jobshop/session_activations_controller.rb +1 -1
- data/app/controllers/jobshop/sessions_controller.rb +15 -1
- data/app/controllers/jobshop/users_controller.rb +9 -0
- data/app/mailers/jobshop/application_mailer.rb +2 -2
- data/app/models/jobshop/ability.rb +7 -0
- data/app/models/jobshop/collection.rb +22 -0
- data/app/models/jobshop/company.rb +22 -0
- data/app/models/jobshop/dashboard.rb +1 -1
- data/app/models/jobshop/inspection/boolean_criterion.rb +44 -0
- data/app/models/jobshop/inspection/criterion.rb +22 -0
- data/app/models/jobshop/inspection/deviation_criterion.rb +112 -0
- data/app/models/jobshop/inspection/limit_criterion.rb +114 -0
- data/app/models/jobshop/inspection/report.rb +41 -0
- data/app/models/jobshop/inspection/result.rb +42 -0
- data/app/models/jobshop/inspection/tuple.rb +13 -0
- data/app/models/jobshop/inspection.rb +13 -0
- data/app/models/jobshop/order.rb +41 -0
- data/app/models/jobshop/order_line.rb +13 -0
- data/app/models/jobshop/organization.rb +34 -0
- data/app/models/jobshop/place.rb +6 -4
- data/app/models/jobshop/product.rb +23 -0
- data/app/models/jobshop/registration.rb +14 -10
- data/app/models/jobshop/role.rb +24 -0
- data/app/models/jobshop/role_ability.rb +7 -0
- data/app/models/jobshop/role_assignment.rb +7 -0
- data/app/models/jobshop/routing_process.rb +20 -0
- data/app/models/jobshop/routing_step.rb +11 -0
- data/app/models/jobshop/thing.rb +30 -4
- data/app/models/jobshop/user.rb +23 -25
- data/app/serializers/jobshop/test_user_serializer.rb +10 -0
- data/app/services/jobshop/authentication_service.rb +20 -0
- data/app/services/jobshop/authorization_service.rb +30 -0
- data/app/services/jobshop/jwt_service.rb +17 -0
- data/config/routes.rb +2 -43
- data/db/migrate/20170311194758_initialize_jobshop.rb +1 -106
- data/db/migrate/20171216021339_create_organizations.rb +20 -0
- data/db/migrate/20171216021717_create_users.rb +30 -0
- data/db/migrate/20171216021853_create_companies.rb +25 -0
- data/db/migrate/20171216022020_create_places.rb +22 -0
- data/db/migrate/20171216022135_create_products.rb +27 -0
- data/db/migrate/20171216022605_create_orders.rb +53 -0
- data/db/migrate/20171216023018_create_roles.rb +52 -0
- data/db/migrate/20171216023022_create_sessions.rb +21 -0
- data/db/migrate/20171216035357_create_things.rb +44 -0
- data/db/migrate/20171219022118_create_routing_processes.rb +57 -0
- data/db/migrate/20180107203241_create_inspection.rb +159 -0
- data/{app/assets/stylesheets/jobshop/welcome.scss → db/seeds.rb} +0 -0
- data/lib/generators/jobshop/app/app_generator.rb +36 -3
- data/lib/generators/jobshop/app/templates/config/database.yml.tt +19 -0
- data/lib/generators/jobshop/config/templates/config/initializers/jobshop.rb.tt +0 -2
- data/lib/generators/jobshop/dummy/dummy_generator.rb +3 -14
- data/lib/jobshop/cli/spinner.rb +21 -0
- data/lib/jobshop/cli.rb +44 -24
- data/lib/jobshop/configuration.rb +24 -0
- data/lib/jobshop/dummy_app.rb +155 -0
- data/lib/jobshop/engine.rb +30 -31
- data/lib/jobshop/helpers/migration.rb +55 -0
- data/lib/jobshop/version.rb +2 -2
- data/lib/jobshop.rb +7 -21
- data/lib/tasks/jobshop_tasks.rake +7 -24
- metadata +113 -150
- data/app/assets/config/jobshop_manifest.js +0 -2
- data/app/assets/images/jobshop/logo.svg +0 -50
- data/app/assets/javascripts/jobshop/application.js +0 -34
- data/app/assets/stylesheets/jobshop/application.scss +0 -57
- data/app/assets/stylesheets/jobshop/breakpoints.scss +0 -47
- data/app/assets/stylesheets/jobshop/dialog.scss +0 -43
- data/app/controllers/concerns/registration_token_validation.rb +0 -41
- data/app/controllers/jobshop/teams/lookups_controller.rb +0 -20
- data/app/controllers/jobshop/teams_controller.rb +0 -6
- data/app/controllers/jobshop/welcome/places_controller.rb +0 -42
- data/app/controllers/jobshop/welcome/things_controller.rb +0 -42
- data/app/controllers/jobshop/welcome_controller.rb +0 -11
- data/app/helpers/jobshop/application_helper.rb +0 -14
- data/app/mailers/jobshop/teams_mailer.rb +0 -10
- data/app/models/jobshop/team.rb +0 -36
- data/app/policies/jobshop/application_policy.rb +0 -55
- data/app/views/devise/confirmations/new.html.haml +0 -10
- data/app/views/devise/mailer/confirmation_instructions.html.haml +0 -4
- data/app/views/devise/mailer/password_change.html.haml +0 -3
- data/app/views/devise/mailer/reset_password_instructions.html.haml +0 -6
- data/app/views/devise/mailer/unlock_instructions.html.haml +0 -5
- data/app/views/devise/passwords/edit.html.haml +0 -19
- data/app/views/devise/passwords/new.html.haml +0 -10
- data/app/views/devise/registrations/edit.html.haml +0 -31
- data/app/views/devise/registrations/new.html.haml +0 -21
- data/app/views/devise/sessions/new.html.haml +0 -15
- data/app/views/devise/shared/_links.html.haml +0 -19
- data/app/views/devise/unlocks/new.html.haml +0 -10
- data/app/views/jobshop/dashboards/show.html.haml +0 -140
- data/app/views/jobshop/places/show.html.haml +0 -12
- data/app/views/jobshop/session_activations/destroy.js.erb +0 -1
- data/app/views/jobshop/shared/_authenticated_header.html.haml +0 -8
- data/app/views/jobshop/shared/_unauthenticated_header.html.haml +0 -10
- data/app/views/jobshop/teams/lookups/show.html.haml +0 -14
- data/app/views/jobshop/teams_mailer/found_teams.html.haml +0 -11
- data/app/views/jobshop/teams_mailer/found_teams.text.erb +0 -8
- data/app/views/jobshop/welcome/index.html.haml +0 -21
- data/app/views/jobshop/welcome/places/new.html.haml +0 -22
- data/app/views/jobshop/welcome/registrations/new.html.haml +0 -32
- data/app/views/jobshop/welcome/things/new.html.haml +0 -24
- data/app/views/layouts/jobshop/application.html.haml +0 -15
- data/app/views/layouts/jobshop/unauthenticated.html.haml +0 -6
- data/config/initializers/assets.rb +0 -11
- data/config/initializers/devise.rb +0 -282
- data/config/initializers/field_with_errors.rb +0 -3
- data/config/initializers/simple_form.rb +0 -157
- data/config/locales/devise.en.yml +0 -62
- data/config/locales/simple_form.en.yml +0 -31
- data/db/migrate/keep +0 -0
- data/lib/generators/jobshop/team/USAGE +0 -7
- data/lib/generators/jobshop/team/team_generator.rb +0 -60
- data/lib/jobshop/failure_app.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cfebb1f31d1fcbb60de9a9b6d74d38cf33db9dd8
|
4
|
+
data.tar.gz: 06d074ee3efe7275a4a2704b0c4ad1a512853259
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 342ba401730334f6e0b99959c2a90abb10b1f5a46dc5caf88c36df26515797d510944035742c133cd38e244f551df4235ed15bd6774cf0ac022594807be1f138
|
7
|
+
data.tar.gz: c6186e7da358301f38968e0a700bab329a4a98be4f57934163513b005cad73f31370184489f2ec10367a90cdfb0d0f6c5d74c100a437937b19aa3ab9ccd6f38d
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Jobshop
|
2
2
|
|
3
|
-
Real-time production tracking and process evaluation
|
3
|
+
Real-time production tracking and process evaluation, under your own roof.
|
4
4
|
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/jobshop.svg)](https://badge.fury.io/rb/jobshop)
|
6
6
|
[![Code Climate](https://codeclimate.com/github/jobshop/jobshop/badges/gpa.svg)](https://codeclimate.com/github/jobshop/jobshop)
|
data/Rakefile
CHANGED
@@ -1,12 +1,9 @@
|
|
1
|
-
#!/usr/bin/env rake
|
2
|
-
|
3
1
|
begin
|
4
2
|
require "bundler/setup"
|
5
3
|
rescue LoadError
|
6
4
|
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
7
5
|
end
|
8
6
|
|
9
|
-
require "rails/version"
|
10
7
|
require "rdoc/task"
|
11
8
|
|
12
9
|
RDoc::Task.new(:rdoc) do |rdoc|
|
@@ -18,17 +15,27 @@ RDoc::Task.new(:rdoc) do |rdoc|
|
|
18
15
|
end
|
19
16
|
|
20
17
|
require "jobshop/dummy_app"
|
18
|
+
load "rails/tasks/statistics.rake"
|
21
19
|
|
22
20
|
APP_RAKEFILE = Jobshop::DummyApp.rakefile
|
21
|
+
require "bundler/gem_tasks"
|
23
22
|
|
24
23
|
load "rails/tasks/engine.rake" if File.exist?(APP_RAKEFILE)
|
25
|
-
load "rails/tasks/statistics.rake"
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
|
27
|
+
require "rspec/core"
|
28
|
+
require "rspec/core/rake_task"
|
29
|
+
|
30
|
+
desc "Verify that all FactoryBot factories are valid"
|
31
|
+
task lint: :environment do
|
32
|
+
DatabaseCleaner.cleaning do
|
33
|
+
FactoryBot.definition_file_paths = Jobshop::DummyApp.factory_paths
|
34
|
+
FactoryBot.find_definitions
|
35
|
+
FactoryBot.lint traits: true
|
31
36
|
end
|
32
37
|
end
|
33
38
|
|
34
|
-
|
39
|
+
desc "Run all specs"
|
40
|
+
RSpec::Core::RakeTask.new(:spec)
|
41
|
+
task default: :spec
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Jobshop
|
2
|
+
module AuthenticationHandler
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class InvalidCredentials < StandardError; end
|
6
|
+
|
7
|
+
included do
|
8
|
+
rescue_from AuthenticationHandler::InvalidCredentials, with: :unauthorized
|
9
|
+
end
|
10
|
+
|
11
|
+
private def unauthorized(e)
|
12
|
+
render({ json: { message: "Invalid credentials #{e.message}" } }, :unauthorized)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Jobshop
|
2
|
+
module AuthorizationHandler
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class TokenMissing < StandardError; end
|
6
|
+
class TokenInvalid < StandardError; end
|
7
|
+
|
8
|
+
included do
|
9
|
+
before_action :authorize_request
|
10
|
+
|
11
|
+
rescue_from Jobshop::AuthorizationHandler::TokenMissing, with: :four_twenty_two
|
12
|
+
rescue_from Jobshop::AuthorizationHandler::TokenInvalid, with: :four_twenty_two
|
13
|
+
rescue_from ActiveRecord::RecordInvalid, with: :four_twenty_two
|
14
|
+
rescue_from ActiveRecord::RecordNotFound do |e|
|
15
|
+
render({ json: { message: e.message } }, :not_found)
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :current_user
|
19
|
+
end
|
20
|
+
|
21
|
+
private def four_twenty_two(e)
|
22
|
+
render({ json: { message: e.message } }, :unprocessable_entity)
|
23
|
+
end
|
24
|
+
|
25
|
+
private def authorize_request
|
26
|
+
@current_user = (AuthorizationService.new(request.headers).perform)[:user]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,37 +1,10 @@
|
|
1
|
-
# Parts of this class borrowed from:
|
2
|
-
# https://gist.github.com/josevalim/fb706b1e933ef01e4fb6
|
3
|
-
# Thank you Jose Valim!
|
4
|
-
|
5
1
|
module Jobshop
|
6
|
-
class ApplicationController < ActionController::
|
7
|
-
|
8
|
-
|
9
|
-
protect_from_forgery
|
10
|
-
|
11
|
-
before_action EmailTokenValidation
|
12
|
-
before_action :authenticate_user!
|
13
|
-
|
14
|
-
def after_sign_in_path_for(resource_or_scope)
|
15
|
-
if !resource_or_scope.onboard?
|
16
|
-
welcome_path
|
17
|
-
else
|
18
|
-
super
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def current_team
|
25
|
-
@current_team ||= current_user && current_user.team
|
26
|
-
end; helper_method :current_team
|
2
|
+
class ApplicationController < ActionController::API
|
3
|
+
include Jobshop::AuthorizationHandler
|
4
|
+
include Jobshop::AuthenticationHandler
|
27
5
|
|
28
|
-
def
|
29
|
-
|
30
|
-
controller_path == "jobshop/teams/lookups"
|
31
|
-
"jobshop/unauthenticated"
|
32
|
-
else
|
33
|
-
"jobshop/application"
|
34
|
-
end
|
6
|
+
def current_organization
|
7
|
+
@current_organization ||= current_user && current_user.organization
|
35
8
|
end
|
36
9
|
end
|
37
10
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_dependency "jobshop/application_controller"
|
2
|
+
|
3
|
+
module Jobshop
|
4
|
+
class Organizations::LookupsController < ApplicationController
|
5
|
+
def show
|
6
|
+
@lookup = Jobshop::User.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def create
|
10
|
+
emails = params[:user][:email].split(",").map(&:strip).take(5)
|
11
|
+
Jobshop::Organization.grouped_by_email(emails).each_pair do |email, organizations|
|
12
|
+
Jobshop::OrganizationsMailer.found_organizations(email, organizations).deliver_now
|
13
|
+
end
|
14
|
+
|
15
|
+
redirect_to organizations_lookup_path
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/app/controllers/jobshop/{welcome/registrations_controller.rb → registrations_controller.rb}
RENAMED
@@ -1,15 +1,10 @@
|
|
1
1
|
require_dependency "jobshop/application_controller"
|
2
2
|
|
3
3
|
module Jobshop
|
4
|
-
class
|
5
|
-
skip_before_action :authenticate_user!
|
6
|
-
|
7
|
-
before_action RegistrationTokenValidation
|
8
|
-
|
9
|
-
layout "jobshop/unauthenticated"
|
10
|
-
|
4
|
+
class RegistrationsController < ApplicationController
|
11
5
|
def new
|
12
6
|
@registration = ::Jobshop::Registration.new(params)
|
7
|
+
render json: @registration
|
13
8
|
end
|
14
9
|
|
15
10
|
def create
|
@@ -1,6 +1,20 @@
|
|
1
1
|
require_dependency "jobshop/application_controller"
|
2
2
|
|
3
3
|
module Jobshop
|
4
|
-
class SessionsController <
|
4
|
+
class SessionsController < ApplicationController
|
5
|
+
skip_before_action :authorize_request, only: :new
|
6
|
+
|
7
|
+
def new
|
8
|
+
token = AuthenticationService.new(
|
9
|
+
session_params[:email],
|
10
|
+
session_params[:password]
|
11
|
+
).perform
|
12
|
+
|
13
|
+
render(json: { auth_token: token })
|
14
|
+
end
|
15
|
+
|
16
|
+
private def session_params
|
17
|
+
params.permit(:email, :password)
|
18
|
+
end
|
5
19
|
end
|
6
20
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Jobshop
|
2
|
+
class Collection < ApplicationRecord
|
3
|
+
self.primary_keys = [ :organization_id, :collection_id ]
|
4
|
+
|
5
|
+
after_create do
|
6
|
+
self.class.connection.clear_query_cache
|
7
|
+
fresh_collection = self.class.unscoped {
|
8
|
+
self.class.find_by!(organization: organization, name: name)
|
9
|
+
}
|
10
|
+
@attributes = fresh_collection.instance_variable_get(:@attributes)
|
11
|
+
@new_record = false
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
belongs_to :organization, inverse_of: :collections
|
16
|
+
has_many :things, inverse_of: :collection,
|
17
|
+
foreign_key: [ :organization_id, :collection_id ]
|
18
|
+
|
19
|
+
validates :name, presence: true,
|
20
|
+
uniqueness: { scope: :organization_id, case_sensitive: false }
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Jobshop
|
2
|
+
class Company < ApplicationRecord
|
3
|
+
self.primary_keys = [ :organization_id, :company_id ]
|
4
|
+
|
5
|
+
after_create do
|
6
|
+
self.class.connection.clear_query_cache
|
7
|
+
@attributes = self.class.unscoped {
|
8
|
+
self.class.find_by!(organization: organization, name: name)
|
9
|
+
}.instance_variable_get(:@attributes)
|
10
|
+
@new_record = false
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
belongs_to :organization, inverse_of: :companies
|
15
|
+
belongs_to :created_by, class_name: "Jobshop::User",
|
16
|
+
foreign_key: [ :organization_id, :created_by_id ]
|
17
|
+
has_many :orders, inverse_of: :company,
|
18
|
+
foreign_key: [ :organization_id, :company_id ]
|
19
|
+
|
20
|
+
validates :name, presence: true
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Jobshop
|
2
|
+
class Inspection::BooleanCriterion < ApplicationRecord
|
3
|
+
self.primary_keys = [ :organization_id, :report_id, :criterion_id, :criterion_type ]
|
4
|
+
|
5
|
+
after_initialize do
|
6
|
+
self[:criterion_id] ||= SecureRandom.uuid if new_record?
|
7
|
+
end
|
8
|
+
|
9
|
+
belongs_to :organization
|
10
|
+
has_one :criterion, as: :criterion, autosave: true, dependent: :destroy,
|
11
|
+
foreign_key: [ :organization_id, :report_id, :criterion_id, :criterion_type ]
|
12
|
+
belongs_to :report, class_name: "Jobshop::Inspection::Report",
|
13
|
+
foreign_key: [ :organization_id, :report_id ], inverse_of: :criteria
|
14
|
+
|
15
|
+
alias_method :criterion_without_build, :criterion
|
16
|
+
def criterion_with_build
|
17
|
+
criterion_without_build || build_criterion(criterion_id: criterion_id, report: report)
|
18
|
+
end; alias_method :criterion, :criterion_with_build
|
19
|
+
|
20
|
+
delegate :name, :name=, :position, :position=, to: :criterion
|
21
|
+
|
22
|
+
def specification
|
23
|
+
@specification ||= "#{condition}\nPASS/FAIL"
|
24
|
+
end
|
25
|
+
|
26
|
+
def tolerance
|
27
|
+
@tolerance ||= condition
|
28
|
+
end
|
29
|
+
|
30
|
+
def unit
|
31
|
+
"boolean".freeze
|
32
|
+
end
|
33
|
+
|
34
|
+
def pass?(value)
|
35
|
+
![ false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF" ].include?(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generate a random value that has a 90% chance of being in spec.
|
39
|
+
# TODO: This needs to go in the tests somewhere, not really in the model.
|
40
|
+
def random
|
41
|
+
rand(100) < 10
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Jobshop
|
2
|
+
class Inspection::Criterion < ApplicationRecord
|
3
|
+
self.primary_keys = [ :organization_id, :report_id, :criterion_id, :criterion_type ]
|
4
|
+
|
5
|
+
after_create do
|
6
|
+
self.class.connection.clear_query_cache
|
7
|
+
@attributes = self.class.unscoped {
|
8
|
+
self.class.find_by!(organization: organization, criterion_id: criterion_id)
|
9
|
+
}.instance_variable_get(:@attributes)
|
10
|
+
@new_record = false
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
belongs_to :organization
|
15
|
+
belongs_to :criterion, polymorphic: true, optional: true, dependent: :destroy,
|
16
|
+
foreign_key: [ :organization_id, :report_id, :criterion_id, :criterion_type ]
|
17
|
+
belongs_to :report, class_name: "Jobshop::Inspection::Report",
|
18
|
+
foreign_key: [ :organization_id, :report_id ], inverse_of: :criteria
|
19
|
+
|
20
|
+
delegate :ascii, :pass?, :random, :unit, to: :criterion
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Jobshop
|
2
|
+
class Inspection::DeviationCriterion < ApplicationRecord
|
3
|
+
self.primary_keys = [ :organization_id, :report_id, :criterion_id, :criterion_type ]
|
4
|
+
|
5
|
+
after_initialize do
|
6
|
+
self.criterion_id ||= SecureRandom.uuid if new_record?
|
7
|
+
end
|
8
|
+
|
9
|
+
belongs_to :organization
|
10
|
+
belongs_to :report, class_name: "Jobshop::Inspection::Report",
|
11
|
+
foreign_key: [ :organization_id, :report_id ], inverse_of: :criteria
|
12
|
+
has_one :criterion, as: :criterion, autosave: true, dependent: :destroy,
|
13
|
+
foreign_key: [ :organization_id, :report_id, :criterion_id, :criterion_type ]
|
14
|
+
|
15
|
+
alias_method :criterion_without_build, :criterion
|
16
|
+
def criterion_with_build
|
17
|
+
criterion_without_build || build_criterion
|
18
|
+
end; alias_method :criterion, :criterion_with_build
|
19
|
+
|
20
|
+
delegate :name, :name=, :position, :position=, to: :criterion
|
21
|
+
|
22
|
+
validates :nominal, presence: true
|
23
|
+
validates :lower, presence: true
|
24
|
+
validates :upper, presence: true
|
25
|
+
|
26
|
+
validate :lower_less_than_upper
|
27
|
+
|
28
|
+
def mean
|
29
|
+
((nominal + lower) + (nominal + upper)) / 2
|
30
|
+
end
|
31
|
+
|
32
|
+
def nominal
|
33
|
+
self[:nominal] && Unitwise(self[:nominal].truncate(4), self[:unit])
|
34
|
+
end
|
35
|
+
|
36
|
+
def lower
|
37
|
+
self[:lower] && Unitwise(self[:lower].truncate(4), self[:unit])
|
38
|
+
end
|
39
|
+
|
40
|
+
def lower=(value)
|
41
|
+
self[:lower] = if value.respond_to?(:unit)
|
42
|
+
value.convert_to(self[:unit]).value
|
43
|
+
else
|
44
|
+
value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def upper
|
49
|
+
self[:upper] && Unitwise(self[:upper].truncate(4), self[:unit])
|
50
|
+
end
|
51
|
+
|
52
|
+
def upper=(value)
|
53
|
+
self[:upper] = if value.respond_to?(:unit)
|
54
|
+
value.convert_to(self[:unit]).value
|
55
|
+
else
|
56
|
+
value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def lower_less_than_upper
|
61
|
+
return unless lower && upper
|
62
|
+
if lower > upper
|
63
|
+
errors[:base] << "minimum must be less than maximum"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def specification
|
68
|
+
@specfication ||= if symmetric?
|
69
|
+
"#{nominal}±#{abs(upper)}"
|
70
|
+
else
|
71
|
+
"#{nominal}\n#{lower}/#{upper}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def symmetric?
|
76
|
+
@symmetrical ||= lower == upper * -1
|
77
|
+
end
|
78
|
+
|
79
|
+
def pass?(value)
|
80
|
+
!undersize?(value) && !oversize?(value)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Generate a random value that has a 90% chance of being in spec.
|
84
|
+
# TODO: This needs to go in the tests somewhere, not really in the model.
|
85
|
+
def random
|
86
|
+
rand_lower = nominal.to_f + lower.to_f * 1.1
|
87
|
+
rand_upper = nominal.to_f + upper.to_f * 1.1
|
88
|
+
value = rand(rand_lower..rand_upper)
|
89
|
+
Unitwise(value.truncate(4), self[:unit])
|
90
|
+
end
|
91
|
+
|
92
|
+
private def oversize?(value)
|
93
|
+
value = if value.respond_to?(:unit)
|
94
|
+
value.convert_to(self[:unit])
|
95
|
+
else
|
96
|
+
Unitwise(value, self[:unit])
|
97
|
+
end
|
98
|
+
|
99
|
+
value > upper || false
|
100
|
+
end
|
101
|
+
|
102
|
+
private def undersize?(value)
|
103
|
+
value = if value.respond_to?(:unit)
|
104
|
+
value.convert_to(self[:unit])
|
105
|
+
else
|
106
|
+
Unitwise(value, self[:unit])
|
107
|
+
end
|
108
|
+
|
109
|
+
value < nominal - lower || false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Jobshop
|
2
|
+
class Inspection::LimitCriterion < ApplicationRecord
|
3
|
+
self.primary_keys = [ :organization_id, :report_id, :criterion_id, :criterion_type ]
|
4
|
+
|
5
|
+
after_initialize do
|
6
|
+
self.criterion_id = SecureRandom.uuid if new_record?
|
7
|
+
end
|
8
|
+
|
9
|
+
belongs_to :organization
|
10
|
+
has_one :criterion, as: :criterion, autosave: true, dependent: :destroy, inverse_of: :criterion,
|
11
|
+
foreign_key: [ :organization_id, :report_id, :criterion_id, :criterion_type ]
|
12
|
+
belongs_to :report, class_name: "Jobshop::Inspection::Report",
|
13
|
+
foreign_key: [ :organization_id, :report_id ], inverse_of: :criteria
|
14
|
+
|
15
|
+
def criterion
|
16
|
+
super || build_criterion
|
17
|
+
end
|
18
|
+
|
19
|
+
delegate :name, :name=, :position, :position=, to: :criterion
|
20
|
+
|
21
|
+
validate :minimum_or_maximum_or_both,
|
22
|
+
:minimum_less_than_maximum
|
23
|
+
|
24
|
+
def minimum
|
25
|
+
self[:minimum] && Unitwise(self[:minimum].truncate(4), self[:unit])
|
26
|
+
end
|
27
|
+
|
28
|
+
def minimum=(value)
|
29
|
+
if value && value.respond_to?(:unit)
|
30
|
+
self[:minimum] = value.value
|
31
|
+
self[:unit] = value.unit
|
32
|
+
else
|
33
|
+
self[:minimum] = value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def maximum
|
38
|
+
self[:maximum] && Unitwise(self[:maximum].truncate(4), self[:unit])
|
39
|
+
end
|
40
|
+
|
41
|
+
def maximum=(value)
|
42
|
+
if value && value.respond_to?(:unit)
|
43
|
+
self[:maximum] = value.value
|
44
|
+
self[:unit] = value.unit
|
45
|
+
else
|
46
|
+
self[:maximum] = value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def specification
|
51
|
+
@specification ||= [
|
52
|
+
qualifier, [ minimum, maximum ].compact.join("/")
|
53
|
+
].compact.join("\n")
|
54
|
+
end
|
55
|
+
|
56
|
+
def qualifier
|
57
|
+
@qualifier ||= if minimum && !maximum
|
58
|
+
"Minimum"
|
59
|
+
elsif maximum && !minimum
|
60
|
+
"Maximum"
|
61
|
+
else
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def unbound?
|
67
|
+
!minimum ^ !maximum
|
68
|
+
end
|
69
|
+
|
70
|
+
def bound?
|
71
|
+
!unbound?
|
72
|
+
end
|
73
|
+
|
74
|
+
def pass?(value)
|
75
|
+
!undersize?(value) && !oversize?(value)
|
76
|
+
end
|
77
|
+
|
78
|
+
private def oversize?(value)
|
79
|
+
value = Unitwise(value, self[:unit]) unless value.respond_to?(:unit)
|
80
|
+
maximum && value > maximum || false
|
81
|
+
end
|
82
|
+
|
83
|
+
private def undersize?(value)
|
84
|
+
value = Unitwise(value, self[:unit]) unless value.respond_to?(:unit)
|
85
|
+
minimum && value < minimum || false
|
86
|
+
end
|
87
|
+
|
88
|
+
# Generate a random value that has a 90% chance of being in spec.
|
89
|
+
# TODO: This needs to go in the tests somewhere, not really in the model.
|
90
|
+
def random
|
91
|
+
value = if bound?
|
92
|
+
rand(self[:minimum].to_f..self[:maximum].to_f) * 1.1
|
93
|
+
elsif maximum
|
94
|
+
rand(self[:maximum].to_f) * 1.1
|
95
|
+
elsif minimum
|
96
|
+
rand((self[:minimum].to_f * 0.8)..(self[:minimum].to_f * 2.0))
|
97
|
+
end
|
98
|
+
|
99
|
+
Unitwise(value, self[:unit])
|
100
|
+
end
|
101
|
+
|
102
|
+
private def minimum_or_maximum_or_both
|
103
|
+
unless minimum || maximum
|
104
|
+
errors[:base] << "must specify minimum, maximum or both"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
private def minimum_less_than_maximum
|
109
|
+
if(minimum && maximum && minimum > maximum)
|
110
|
+
errors[:base] << "minimum must be less than maximum"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Jobshop
|
2
|
+
class Inspection::Report < ApplicationRecord
|
3
|
+
self.primary_keys = [ :organization_id, :report_id ]
|
4
|
+
|
5
|
+
after_initialize do
|
6
|
+
self.report_id ||= SecureRandom.uuid if new_record?
|
7
|
+
end
|
8
|
+
|
9
|
+
after_create do
|
10
|
+
self.class.connection.clear_query_cache
|
11
|
+
@attributes = self.class.unscoped {
|
12
|
+
self.class.find_by!(organization: organization, report_id: report_id)
|
13
|
+
}.instance_variable_get(:@attributes)
|
14
|
+
@new_record = false
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
belongs_to :organization
|
19
|
+
has_many :criteria, -> { includes(:criterion).order(:position) },
|
20
|
+
foreign_key: [ :organization_id, :report_id ], inverse_of: :report
|
21
|
+
has_many :tuples, -> { order(:position) }, inverse_of: :report,
|
22
|
+
foreign_key: [ :organization_id, :report_id ]
|
23
|
+
|
24
|
+
def ascii
|
25
|
+
require "terminal-table"
|
26
|
+
table = Terminal::Table.new do |t|
|
27
|
+
t.title = "Inspection Report"
|
28
|
+
t << [ "Feature" ].concat(criteria.map(&:criterion).map(&:name))
|
29
|
+
t << [ "Specification" ].concat(criteria.map(&:criterion).map(&:specification))
|
30
|
+
tuples.each do |tuple|
|
31
|
+
results = criteria.map do |criterion|
|
32
|
+
tuple.results.find_by(criterion: criterion)
|
33
|
+
end
|
34
|
+
t << [ tuple.position ].concat(results.map(&:value))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
puts table
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|