jobshop 0.0.131 → 0.0.157
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.
- 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
|
[](https://badge.fury.io/rb/jobshop)
|
|
6
6
|
[](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
|