lca 0.2.5 → 0.3
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/Gemfile +1 -3
- data/Gemfile.lock +10 -56
- data/README.md +28 -21
- data/lib/generators/lca/install_generator.rb +0 -11
- data/lib/generators/lca/templates/migration.rb.tt +21 -57
- data/lib/lca/models/concerns/lcable.rb +37 -61
- data/lib/lca/models/cycle/product.rb +1 -0
- data/lib/lca/models/cycle/service.rb +1 -0
- data/lib/lca/models/impact.rb +10 -0
- data/lib/lca/models/impact_snapshot.rb +55 -0
- data/lib/lca/models/model.rb +1 -74
- data/lib/lca/models/process/transport.rb +5 -1
- data/lib/lca/models/stage.rb +5 -0
- data/lib/lca/queries/query.rb +1 -6
- data/lib/lca/version.rb +1 -1
- data/lib/lca.rb +4 -31
- metadata +6 -24
- data/lib/generators/lca/templates/config.yml.tt +0 -18
- data/lib/generators/lca/templates/initializer.rb.tt +0 -17
- data/lib/lca/builders/builder.rb +0 -3
- data/lib/lca/models/concerns/statusable.rb +0 -49
- data/lib/lca/models/metadata.rb +0 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4a856a2dadcc2f24d2668205c87824cf0efc26dc40b94011dbc37c644cc4e177
|
|
4
|
+
data.tar.gz: 4bd30ccb55f50fd474b84372e0e08bbd29efabf1ec393a489bd8ac97dfb7e8fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 35136a03613252f3c9d3f6297f68993f5d834a62241397a6f358ed26af3fdf715c083b8daf9a7776451d93e948e2a91e91e5cebc338ad29069e55d8c3d047424
|
|
7
|
+
data.tar.gz: ede6826e7116952fc7d25f4b71b1f05a503c499e839e9f3e61f768a4e48103d6ff6924f6c01a516c3e30bfe2038e20359cc5913264b710df9e012351c627b007
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,87 +1,40 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
lca (0.
|
|
5
|
-
|
|
4
|
+
lca (0.3)
|
|
5
|
+
active_tree (~> 0.3)
|
|
6
6
|
activerecord (~> 6.0)
|
|
7
|
-
activestorage (~> 6.0)
|
|
8
7
|
jwt (~> 2.2.3)
|
|
9
8
|
pg_ltree (~> 1.1.8)
|
|
10
9
|
|
|
11
10
|
GEM
|
|
12
11
|
remote: https://rubygems.org/
|
|
13
12
|
specs:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
rack-test (>= 0.6.3)
|
|
19
|
-
rails-dom-testing (~> 2.0)
|
|
20
|
-
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
21
|
-
actiontext (6.1.4.1)
|
|
22
|
-
actionpack (= 6.1.4.1)
|
|
23
|
-
activerecord (= 6.1.4.1)
|
|
24
|
-
activestorage (= 6.1.4.1)
|
|
25
|
-
activesupport (= 6.1.4.1)
|
|
26
|
-
nokogiri (>= 1.8.5)
|
|
27
|
-
actionview (6.1.4.1)
|
|
28
|
-
activesupport (= 6.1.4.1)
|
|
29
|
-
builder (~> 3.1)
|
|
30
|
-
erubi (~> 1.4)
|
|
31
|
-
rails-dom-testing (~> 2.0)
|
|
32
|
-
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
33
|
-
activejob (6.1.4.1)
|
|
34
|
-
activesupport (= 6.1.4.1)
|
|
35
|
-
globalid (>= 0.3.6)
|
|
13
|
+
active_tree (0.3)
|
|
14
|
+
activerecord (~> 6.0)
|
|
15
|
+
jwt (~> 2.2.3)
|
|
16
|
+
pg_ltree (~> 1.1.8)
|
|
36
17
|
activemodel (6.1.4.1)
|
|
37
18
|
activesupport (= 6.1.4.1)
|
|
38
19
|
activerecord (6.1.4.1)
|
|
39
20
|
activemodel (= 6.1.4.1)
|
|
40
21
|
activesupport (= 6.1.4.1)
|
|
41
|
-
activestorage (6.1.4.1)
|
|
42
|
-
actionpack (= 6.1.4.1)
|
|
43
|
-
activejob (= 6.1.4.1)
|
|
44
|
-
activerecord (= 6.1.4.1)
|
|
45
|
-
activesupport (= 6.1.4.1)
|
|
46
|
-
marcel (~> 1.0.0)
|
|
47
|
-
mini_mime (>= 1.1.0)
|
|
48
22
|
activesupport (6.1.4.1)
|
|
49
23
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
50
24
|
i18n (>= 1.6, < 2)
|
|
51
25
|
minitest (>= 5.1)
|
|
52
26
|
tzinfo (~> 2.0)
|
|
53
27
|
zeitwerk (~> 2.3)
|
|
54
|
-
builder (3.2.4)
|
|
55
28
|
concurrent-ruby (1.1.9)
|
|
56
|
-
crass (1.0.6)
|
|
57
29
|
diff-lcs (1.4.4)
|
|
58
|
-
erubi (1.10.0)
|
|
59
|
-
globalid (0.5.2)
|
|
60
|
-
activesupport (>= 5.0)
|
|
61
30
|
i18n (1.8.10)
|
|
62
31
|
concurrent-ruby (~> 1.0)
|
|
63
32
|
jwt (2.2.3)
|
|
64
|
-
loofah (2.12.0)
|
|
65
|
-
crass (~> 1.0.2)
|
|
66
|
-
nokogiri (>= 1.5.9)
|
|
67
|
-
marcel (1.0.1)
|
|
68
|
-
mini_mime (1.1.1)
|
|
69
33
|
minitest (5.14.4)
|
|
70
|
-
nokogiri (1.12.4-x86_64-linux)
|
|
71
|
-
racc (~> 1.4)
|
|
72
34
|
pg (1.2.3)
|
|
73
35
|
pg_ltree (1.1.8)
|
|
74
36
|
activerecord (>= 4.0.0, <= 7.0.0.rc1)
|
|
75
37
|
pg (>= 0.17.0, < 2)
|
|
76
|
-
racc (1.5.2)
|
|
77
|
-
rack (2.2.3)
|
|
78
|
-
rack-test (1.1.0)
|
|
79
|
-
rack (>= 1.0, < 3)
|
|
80
|
-
rails-dom-testing (2.0.3)
|
|
81
|
-
activesupport (>= 4.2.0)
|
|
82
|
-
nokogiri (>= 1.6)
|
|
83
|
-
rails-html-sanitizer (1.4.2)
|
|
84
|
-
loofah (~> 2.3)
|
|
85
38
|
rake (13.0.6)
|
|
86
39
|
rspec (3.10.0)
|
|
87
40
|
rspec-core (~> 3.10.0)
|
|
@@ -104,12 +57,13 @@ PLATFORMS
|
|
|
104
57
|
x86_64-linux
|
|
105
58
|
|
|
106
59
|
DEPENDENCIES
|
|
107
|
-
|
|
60
|
+
active_tree (~> 0.3)
|
|
108
61
|
activerecord (~> 6.0)
|
|
109
|
-
|
|
62
|
+
jwt
|
|
110
63
|
lca!
|
|
64
|
+
pg_ltree
|
|
111
65
|
rake (~> 13.0)
|
|
112
66
|
rspec (~> 3.0)
|
|
113
67
|
|
|
114
68
|
BUNDLED WITH
|
|
115
|
-
2.2.
|
|
69
|
+
2.2.29
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[](https://badge.fury.io/rb/lca)
|
|
1
|
+
[](https://github.com/nicksterious/lca/actions/workflows/ci.yml) [](https://github.com/nicksterious/lca/actions/workflows/rubygems.yml) [](https://badge.fury.io/rb/lca)
|
|
2
2
|
|
|
3
3
|
# LCA
|
|
4
4
|
|
|
@@ -8,6 +8,8 @@ This gem implements a denormalized database model for life-cycle assessment data
|
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
11
|
+
This gem is at home within a Rails 6+ console-only, API or full fledged application on top of a Postgres database.
|
|
12
|
+
|
|
11
13
|
Add this line to your application's Gemfile:
|
|
12
14
|
|
|
13
15
|
```ruby
|
|
@@ -22,15 +24,24 @@ Or install it yourself as:
|
|
|
22
24
|
|
|
23
25
|
$ gem install lca
|
|
24
26
|
|
|
25
|
-
Upon installing the gem you must run the install process in your Rails application's root:
|
|
27
|
+
Upon installing the gem you must run the ActiveTree install process in your Rails application's root:
|
|
28
|
+
|
|
29
|
+
$ rails g active_tree:install
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
This will generate a `config/active_tree.yml` which you may customize to your needs, an initializer and a migration file which you can also customize to add any database columns your models will require.
|
|
33
|
+
|
|
34
|
+
You must also run the LCA gem install generator:
|
|
26
35
|
|
|
27
36
|
$ rails g lca:install
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
|
|
39
|
+
This will create a migration file which you can edit to add your models' specific attributes.
|
|
40
|
+
|
|
30
41
|
|
|
31
42
|
## Usage
|
|
32
43
|
|
|
33
|
-
Include the
|
|
44
|
+
Include the ActiveTree concern into one of your models which will own lifecycle trees (owner model):
|
|
34
45
|
|
|
35
46
|
```ruby
|
|
36
47
|
class User < ApplicationRecord
|
|
@@ -43,16 +54,16 @@ This will extend your model and enable the following functionality:
|
|
|
43
54
|
|
|
44
55
|
```ruby
|
|
45
56
|
# query with ActiveRecord syntax
|
|
46
|
-
User.last.
|
|
47
|
-
User.find_by(name: "Acme").
|
|
48
|
-
User.last.
|
|
49
|
-
User.last.
|
|
50
|
-
User.last.
|
|
57
|
+
User.last.active_trees
|
|
58
|
+
User.find_by(name: "Acme").active_trees.impacts.select("impact_unit, sum(impact_amount) as total_impact").group(:impact_unit)
|
|
59
|
+
User.last.active_trees.where(...)
|
|
60
|
+
User.last.active_trees.where(...).group(...)
|
|
61
|
+
User.last.active_trees.where(...).limit(...).offset(...)
|
|
51
62
|
|
|
52
63
|
# AR query with ltree syntax
|
|
53
|
-
User.last.
|
|
54
|
-
User.last.
|
|
55
|
-
User.last.
|
|
64
|
+
User.last.active_trees.disabled.match_path("*.CustomProcess.*")
|
|
65
|
+
User.last.active_trees.match_path("*{5,10}.CustomProcess.*.Ecosphere.*")
|
|
66
|
+
User.last.active_trees.active.match_path("*.WhateverProcess.*.Ecosphere.*.CO2Emission.*").where( impact_amount: [100..150]).sum(:impact_amount)
|
|
56
67
|
|
|
57
68
|
Lca::Cycle.match_path("Top.Electric Vehicle.*").impacts.match_path("*.CO2*").where( impact_amount: [ 1000..10000 ]).average(:impact_amount)
|
|
58
69
|
Lca::Cycle::Product.where(name: "Electric Vehicle Battery").impacts.match_path("*.Cobalt.*").sum(:impact_amount)
|
|
@@ -62,11 +73,11 @@ Lca::Exchange.match_path("*.Oil.*.Ecosphere.*").impacts.sum(:impact_amount)
|
|
|
62
73
|
Lca::Product.match_path("*.ElectricVehicle.*").processes.match_path("*.Processing.*").where(location: "EU").impacts.match_path("*.Lithium.*").where(location: ["CN", "Africa"]).sum(:impact_amount)
|
|
63
74
|
|
|
64
75
|
# pg_ltree queries
|
|
65
|
-
User.last.
|
|
76
|
+
User.last.active_trees.last.parent
|
|
66
77
|
Lca::Process.match_path("*.Manual assembly.*").children
|
|
67
78
|
|
|
68
79
|
# pg_ltree combined with AR syntax
|
|
69
|
-
User.last.
|
|
80
|
+
User.last.active_trees(type: "Lca::Product").children.match_path("*.Retail").children.exchanges
|
|
70
81
|
|
|
71
82
|
# all queries can be directed to a specific partition:
|
|
72
83
|
Lca::Process.owned_by( owner_id ).match_path("*.Recycling.*").where(impact_unit: "tons CO2/year").impacts.sum(:impact_amount)
|
|
@@ -81,12 +92,13 @@ Lca::Impact::Ecosphere::Fauna.match_path("*.ResourceExtraction.*").where(impact_
|
|
|
81
92
|
```
|
|
82
93
|
|
|
83
94
|
To see what syntax to use for path traversal please check out the following resources:
|
|
95
|
+
* active_tree gem https://github.com/nicksterious/active_tree
|
|
84
96
|
* pg_ltree gem https://github.com/sjke/pg_ltree
|
|
85
97
|
* Postgres ltree extension documentation https://www.postgresql.org/docs/9.1/ltree.html
|
|
86
98
|
|
|
87
|
-
|
|
99
|
+
## Scalability
|
|
88
100
|
|
|
89
|
-
|
|
101
|
+
All LCA data can be stored into separate partitions using the ActiveTree partitioning feature. See `config/active_tree.yml` for table and partitioning naming and options.
|
|
90
102
|
|
|
91
103
|
## Caveats
|
|
92
104
|
|
|
@@ -108,7 +120,6 @@ Lca::Process.last.children.unscope(where: :type).impacts.where(impact_amount: 50
|
|
|
108
120
|
* .child / .parent query support across models unrelated through inheritance
|
|
109
121
|
* more model templates for various LCA cycles, processes and impacts
|
|
110
122
|
* model validations
|
|
111
|
-
* query objects
|
|
112
123
|
* postgres RECURSIVE queries
|
|
113
124
|
* builders
|
|
114
125
|
* seeds/fixtures
|
|
@@ -118,10 +129,6 @@ Lca::Process.last.children.unscope(where: :type).impacts.where(impact_amount: 50
|
|
|
118
129
|
|
|
119
130
|
Bug reports, pull requests and feature suggestions are welcome on GitHub at https://github.com/nicksterious/lca
|
|
120
131
|
|
|
121
|
-
## Sponsors
|
|
122
|
-
|
|
123
|
-
We welcome any sponsorship through Github Sponsors. All corporate sponsors' logo and URL of choice shall show up within this section.
|
|
124
|
-
|
|
125
132
|
## License
|
|
126
133
|
|
|
127
134
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -10,17 +10,6 @@ module Lca
|
|
|
10
10
|
migration_template "migration.rb.tt", "db/migrate/install_lca.rb", migration_version: migration_version
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def copy_initializer
|
|
14
|
-
copy_file 'initializer.rb.tt', 'config/initializers/lca.rb'
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def copy_config
|
|
18
|
-
conf_file = "config/lca.yml"
|
|
19
|
-
copy_file "config.yml.tt", conf_file
|
|
20
|
-
contents = File.read( conf_file ).gsub("changeme", ('a'..'z').to_a.shuffle.first(4).join )
|
|
21
|
-
File.open(conf_file, 'wb') { |file| file.write(contents) }
|
|
22
|
-
end
|
|
23
|
-
|
|
24
13
|
def migration_version
|
|
25
14
|
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
26
15
|
end
|
|
@@ -1,79 +1,43 @@
|
|
|
1
1
|
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
|
|
2
2
|
def change
|
|
3
3
|
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
# add any columns required by your business model
|
|
5
|
+
add_column ACTIVE_TREE_OPTIONS[:table_name], :impact_amount, :decimal
|
|
6
|
+
add_column ACTIVE_TREE_OPTIONS[:table_name], :impact_amount_previous, :decimal
|
|
7
|
+
add_column ACTIVE_TREE_OPTIONS[:table_name], :impact_amount_unit, :text
|
|
8
|
+
add_column ACTIVE_TREE_OPTIONS[:table_name], :impact_factor, :decimal
|
|
9
|
+
add_column ACTIVE_TREE_OPTIONS[:table_name], :impact_precision, :decimal
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
# snapshots table
|
|
13
|
+
# maybe this should only store the impacts?
|
|
12
14
|
execute <<-SQL
|
|
13
|
-
create table #{
|
|
15
|
+
create table #{ ACTIVE_TREE_OPTIONS[:table_name] }_snapshots (
|
|
14
16
|
id serial,
|
|
15
17
|
|
|
18
|
+
impact_id integer,
|
|
19
|
+
|
|
16
20
|
owner_id integer,
|
|
17
21
|
owner_type character varying,
|
|
18
|
-
|
|
19
|
-
status integer,
|
|
20
|
-
|
|
21
|
-
data_external_id character varying,
|
|
22
|
-
data_provider character varying,
|
|
23
|
-
|
|
24
|
-
type character varying,
|
|
25
|
-
name text,
|
|
26
|
-
|
|
27
|
-
parent_entity_id integer,
|
|
28
|
-
parent_entity_type character varying,
|
|
29
|
-
|
|
30
|
-
path ltree,
|
|
31
|
-
path_slug text,
|
|
32
|
-
|
|
33
|
-
metadata_inline jsonb,
|
|
34
22
|
|
|
35
23
|
impact_amount decimal,
|
|
24
|
+
impact_amount_previous decimal,
|
|
36
25
|
impact_amount_unit text,
|
|
37
26
|
impact_factor decimal,
|
|
38
27
|
impact_precision decimal,
|
|
39
28
|
|
|
29
|
+
created_at timestamp(6) without time zone not null,
|
|
30
|
+
updated_at timestamp(6) without time zone not null,
|
|
31
|
+
|
|
40
32
|
primary key (id, owner_id)
|
|
41
33
|
) partition by list(owner_id)
|
|
42
34
|
SQL
|
|
43
35
|
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
#
|
|
48
|
-
add_index
|
|
49
|
-
add_index LCA_OPTIONS[:table_name], :owner_id
|
|
50
|
-
add_index LCA_OPTIONS[:table_name], :type
|
|
51
|
-
add_index LCA_OPTIONS[:table_name], :parent_entity_id
|
|
52
|
-
|
|
53
|
-
# next two indexes unfortunately can't be unique since a cycle can appear several times under an owner
|
|
54
|
-
add_index LCA_OPTIONS[:table_name], :path, using: :gist
|
|
55
|
-
add_index LCA_OPTIONS[:table_name], [:data_provider, :data_external_id]
|
|
56
|
-
|
|
57
|
-
if LCA_OPTIONS[:create_postgrest_roles]
|
|
58
|
-
# create postgrest anon user with no privs
|
|
59
|
-
# postgrest may pass an user's role using JWT
|
|
60
|
-
execute "drop role if exists postgrest_anon"
|
|
61
|
-
execute "create role postgrest_anon nologin"
|
|
62
|
-
execute "grant postgrest_anon to #{ LCA_OPTIONS[:database_user] }"
|
|
63
|
-
end
|
|
36
|
+
# index the snapshots table
|
|
37
|
+
add_index "#{ACTIVE_TREE_OPTIONS[:table_name]}_snapshots", :id
|
|
38
|
+
add_index "#{ACTIVE_TREE_OPTIONS[:table_name]}_snapshots", :owner_id
|
|
39
|
+
add_index "#{ACTIVE_TREE_OPTIONS[:table_name]}_snapshots", :impact_id
|
|
40
|
+
add_index "#{ACTIVE_TREE_OPTIONS[:table_name]}_snapshots", :created_at
|
|
64
41
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
#
|
|
68
|
-
create_table "#{ LCA_OPTIONS[:table_name] }_metadata" do |t|
|
|
69
|
-
t.string :model_type
|
|
70
|
-
t.integer :model_id
|
|
71
|
-
t.string :key
|
|
72
|
-
t.text :value
|
|
73
|
-
|
|
74
|
-
t.timestamps
|
|
75
|
-
end
|
|
76
|
-
add_index "#{ LCA_OPTIONS[:table_name] }_metadata", [ :model_type, :model_id ]
|
|
77
|
-
add_index "#{ LCA_OPTIONS[:table_name] }_metadata", [ :key ]
|
|
78
42
|
end
|
|
79
43
|
end
|
|
@@ -1,84 +1,60 @@
|
|
|
1
1
|
require "jwt"
|
|
2
2
|
|
|
3
|
+
# read https://www.postgresqltutorial.com/postgresql-schema/
|
|
4
|
+
|
|
3
5
|
module Lca
|
|
4
6
|
module Lcable
|
|
5
7
|
extend ActiveSupport::Concern
|
|
6
8
|
|
|
7
|
-
# def self.included(base)
|
|
8
|
-
# ::Lca::Model.class_exec do
|
|
9
|
-
# @@owner_class = base.name
|
|
10
|
-
# end
|
|
11
|
-
# end
|
|
12
|
-
|
|
13
9
|
included do
|
|
10
|
+
before_destroy :lca_delete_storage
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# instance methods
|
|
20
|
-
def lca_role
|
|
21
|
-
"lca_owner_#{id}_#{ LCA_OPTIONS[:owner_role_suffix] }"
|
|
22
|
-
end # role
|
|
23
|
-
|
|
24
|
-
# Generates a JWT token the client (SPA) can pass to PostgREST for privilege escalation
|
|
25
|
-
def generate_jwt
|
|
26
|
-
payload = { role: self.lca_role }
|
|
27
|
-
::JWT.encode payload, LCA_OPTIONS[:jwt_secret], LCA_OPTIONS[:jwt_encryption]
|
|
28
|
-
end # .generate_jwt
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
# Returns the LCA table name as configured within config/lca.yml
|
|
32
|
-
def lca_table_name
|
|
33
|
-
LCA_OPTIONS[:table_name]
|
|
34
|
-
end # .lca_table_name
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
# Creates LCA table partition and role for owner
|
|
38
|
-
def lca_create_storage
|
|
12
|
+
#
|
|
13
|
+
include ActiveTree::ActiveTreeAble
|
|
14
|
+
after_create :lca_create_storage
|
|
39
15
|
|
|
40
|
-
|
|
41
|
-
lca_sql "create table if not exists #{lca_table_name}_#{id} partition of #{lca_table_name} for values in (#{id})"
|
|
42
|
-
# TODO create partition indexes
|
|
16
|
+
has_many :lca_cycles, class_name: "::Lca::Model", foreign_key: :owner_id, as: :owner
|
|
43
17
|
|
|
44
|
-
if LCA_OPTIONS[:create_postgrest_roles]
|
|
45
|
-
# drop role if it exists
|
|
46
|
-
lca_sql "drop role if exists #{ lca_role }"
|
|
47
18
|
|
|
48
|
-
|
|
49
|
-
|
|
19
|
+
def lca_role
|
|
20
|
+
"active_tree_owner_#{id}_#{ ACTIVE_TREE_OPTIONS[:owner_role_suffix] }"
|
|
21
|
+
end # role
|
|
50
22
|
|
|
51
|
-
# grant privs
|
|
52
|
-
lca_sql "grant all privileges on #{lca_table_name}_#{id} to #{ lca_role }"
|
|
53
|
-
end
|
|
54
23
|
|
|
55
|
-
|
|
24
|
+
def lca_table_name
|
|
25
|
+
ACTIVE_TREE_OPTIONS[:table_name]
|
|
26
|
+
end
|
|
56
27
|
|
|
57
28
|
|
|
58
|
-
|
|
59
|
-
|
|
29
|
+
def lca_create_storage
|
|
30
|
+
# create snapshots partition
|
|
31
|
+
lca_sql "create table if not exists #{lca_table_name}_snapshots_#{id} partition of #{lca_table_name}_snapshots for values in (#{id})"
|
|
32
|
+
|
|
33
|
+
# if roles are enabled, grant privileges
|
|
34
|
+
if ACTIVE_TREE_OPTIONS[:create_postgrest_roles]
|
|
35
|
+
lca_sql "grant all privileges on #{lca_table_name}_snapshots_#{id} to #{ lca_role }"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
60
38
|
|
|
61
|
-
if LCA_OPTIONS[:create_postgrest_roles]
|
|
62
|
-
# revoke privs
|
|
63
|
-
lca_sql "REVOKE ALL PRIVILEGES ON #{lca_table_name}_#{id} FROM #{ lca_role }"
|
|
64
39
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
40
|
+
def lca_delete_storage
|
|
41
|
+
# remove privs
|
|
42
|
+
if ACTIVE_TREE_OPTIONS[:create_postgrest_roles]
|
|
43
|
+
lca_sql "REVOKE ALL PRIVILEGES ON #{lca_table_name}_snapshots_#{id} FROM #{ lca_role }"
|
|
44
|
+
end
|
|
68
45
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
end # delete_storage
|
|
46
|
+
if ACTIVE_TREE_OPTIONS[:destroy_partition_on_owner_destroy]
|
|
47
|
+
lca_sql "drop table if exists #{lca_table_name}_snapshots_#{id}"
|
|
48
|
+
else
|
|
49
|
+
lca_sql "alter table #{lca_table_name}_snapshots detach partition #{ lca_role }"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
77
53
|
|
|
78
|
-
def lca_sql sql
|
|
79
|
-
ActiveRecord::Base.connection.execute sql
|
|
80
|
-
end # lca_sql
|
|
81
54
|
|
|
55
|
+
def lca_sql(q)
|
|
56
|
+
ActiveRecord::Base.connection.execute q
|
|
57
|
+
end
|
|
82
58
|
end # ClassMethods
|
|
83
59
|
end
|
|
84
60
|
end
|
data/lib/lca/models/impact.rb
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
class Lca::Impact < Lca::Model
|
|
2
2
|
belongs_to :exchange, class_name: "::Lca::Exchange", foreign_key: :parent_entity_id, required: true
|
|
3
3
|
|
|
4
|
+
has_many :snapshots, class_name: "::Lca::ImpactSnapshot", foreign_key: :impact_id, dependent: :destroy
|
|
5
|
+
|
|
4
6
|
validates_presence_of :impact_amount
|
|
5
7
|
validates_presence_of :impact_amount_unit, allow_blank: false
|
|
6
8
|
validates_presence_of :impact_factor
|
|
7
9
|
validates_presence_of :impact_precision
|
|
10
|
+
|
|
11
|
+
# lol?
|
|
12
|
+
before_update :snapshot!
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def take_snapshot!
|
|
16
|
+
return ::Lca::ImpactSnapshot.take! self
|
|
17
|
+
end # .snapshot!
|
|
8
18
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class Lca::ImpactSnapshot < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
self.primary_key = :id
|
|
4
|
+
|
|
5
|
+
def self.table_name
|
|
6
|
+
return "#{::ACTIVE_TREE_OPTIONS[:table_name]}_snapshots" if defined? ::ACTIVE_TREE_OPTIONS
|
|
7
|
+
return "active_tree_snapshots"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
belongs_to :impact, class_name: "::Lca::Impact", foreign_key: :impact_id, required: true
|
|
12
|
+
belongs_to :owner, polymorphic: true, required: true
|
|
13
|
+
|
|
14
|
+
validates_presence_of :impact_amount
|
|
15
|
+
validates_presence_of :impact_amount_unit, allow_blank: false
|
|
16
|
+
validates_presence_of :impact_factor
|
|
17
|
+
validates_presence_of :impact_precision
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# pull data from owner's partition
|
|
22
|
+
def self.owned_by(owner_id)
|
|
23
|
+
return self if !owner_id.is_a? Integer
|
|
24
|
+
partition_suffix = "_#{owner_id}"
|
|
25
|
+
table = "#{ self.table_name }#{ partition_suffix }"
|
|
26
|
+
|
|
27
|
+
ApplicationRecord.connection.schema_cache.clear!
|
|
28
|
+
return self if !ApplicationRecord.connection.schema_cache.data_source_exists? table
|
|
29
|
+
|
|
30
|
+
model_class = Class.new self
|
|
31
|
+
|
|
32
|
+
original_class_name = self.name
|
|
33
|
+
class_name = "#{name}#{partition_suffix}"
|
|
34
|
+
|
|
35
|
+
model_class.define_singleton_method(:table_name) do
|
|
36
|
+
table
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
model_class.define_singleton_method(:name) do
|
|
40
|
+
class_name
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
model_class
|
|
44
|
+
end # owned_by
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def self.take!(impact)
|
|
48
|
+
return create(
|
|
49
|
+
impact.attributes
|
|
50
|
+
.deep_symbolize_keys
|
|
51
|
+
.slice(:impact_amount, :impact_amount_previous, :impact_amount_unit, :impact_factor, :impact_precision, :owner_id, :owner_type)
|
|
52
|
+
.merge( impact_id: impact.id )
|
|
53
|
+
)
|
|
54
|
+
end # .take!
|
|
55
|
+
end
|
data/lib/lca/models/model.rb
CHANGED
|
@@ -1,78 +1,5 @@
|
|
|
1
|
-
class Lca::Model <
|
|
1
|
+
class Lca::Model < ActiveTree::Model
|
|
2
2
|
|
|
3
|
-
include Lca::Statusable
|
|
4
3
|
|
|
5
|
-
self.primary_key = :id
|
|
6
|
-
|
|
7
|
-
ltree :path
|
|
8
|
-
|
|
9
|
-
def self.table_name
|
|
10
|
-
return ::LCA_OPTIONS[:table_name] if defined? ::LCA_OPTIONS
|
|
11
|
-
return "lca_models"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
belongs_to :owner, polymorphic: :true, required: true
|
|
15
|
-
|
|
16
|
-
scope :match_path, -> (some_path) { where("path ~ ?", "#{some_path}") }
|
|
17
|
-
|
|
18
|
-
#has_one_attached :picture
|
|
19
|
-
#has_rich_text :description
|
|
20
|
-
|
|
21
|
-
validates_presence_of :name, allow_blank: false
|
|
22
|
-
validates_presence_of :path, allow_blank: false
|
|
23
|
-
|
|
24
|
-
has_many :metadata, class_name: "::Lca::Metadata", dependent: :destroy, as: :model
|
|
25
|
-
|
|
26
|
-
before_validation :set_defaults
|
|
27
|
-
def set_defaults
|
|
28
|
-
self.path ||= name.delete(" ").gsub(/[^0-9a-z ]/i, '') if name
|
|
29
|
-
self.path_slug = path.parameterize if path
|
|
30
|
-
self.metadata_inline ||= {}
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
# Scoping by owner in order to select the partition
|
|
35
|
-
#
|
|
36
|
-
# @param owner_id [Integer] the partition owner
|
|
37
|
-
def self.owned_by(owner_id)
|
|
38
|
-
# if we're looking for anything else but an integer, revert to the base class
|
|
39
|
-
return self if !owner_id.is_a? Integer
|
|
40
|
-
|
|
41
|
-
partition_suffix = "_#{owner_id}"
|
|
42
|
-
|
|
43
|
-
table = "#{ self.table_name }#{ partition_suffix }"
|
|
44
|
-
|
|
45
|
-
ApplicationRecord.connection.schema_cache.clear!
|
|
46
|
-
return self if !ApplicationRecord.connection.schema_cache.data_source_exists? table
|
|
47
|
-
|
|
48
|
-
# duplicate the class
|
|
49
|
-
model_class = Class.new self
|
|
50
|
-
original_class_name = self.name
|
|
51
|
-
|
|
52
|
-
# ...for this owner
|
|
53
|
-
class_name = "#{name}#{partition_suffix}"
|
|
54
|
-
|
|
55
|
-
# specify the table
|
|
56
|
-
model_class.define_singleton_method(:table_name) do
|
|
57
|
-
table
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# specify the name
|
|
61
|
-
model_class.define_singleton_method(:name) do
|
|
62
|
-
class_name
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
model_class.define_singleton_method(:sti_name) do
|
|
66
|
-
original_class_name
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# override the STI name lmfao
|
|
70
|
-
model_class.define_singleton_method(:find_sti_class) do |p|
|
|
71
|
-
original_class_name.constantize
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# proceed
|
|
75
|
-
model_class
|
|
76
|
-
end # .owned_by
|
|
77
4
|
|
|
78
5
|
end
|
data/lib/lca/models/stage.rb
CHANGED
|
@@ -3,4 +3,9 @@ class Lca::Stage < Lca::Model
|
|
|
3
3
|
belongs_to :cycle, class_name: "::Lca::Cycle", foreign_key: :parent_entity_id, required: true
|
|
4
4
|
has_many :processes, class_name: "::Lca::Process", foreign_key: :parent_entity_id
|
|
5
5
|
|
|
6
|
+
# duration?
|
|
7
|
+
# duration unit?
|
|
8
|
+
|
|
9
|
+
# quantity?
|
|
10
|
+
# quantity unit?
|
|
6
11
|
end
|
data/lib/lca/queries/query.rb
CHANGED
data/lib/lca/version.rb
CHANGED
data/lib/lca.rb
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
require "active_support/all"
|
|
3
2
|
|
|
3
|
+
require "active_support/all"
|
|
4
4
|
require "active_record"
|
|
5
|
-
|
|
6
5
|
require "pg_ltree"
|
|
6
|
+
require "active_tree"
|
|
7
7
|
|
|
8
8
|
require_relative "lca/version"
|
|
9
9
|
|
|
10
|
-
require_relative "lca/models/concerns/
|
|
11
|
-
require_relative "lca/models/metadata"
|
|
10
|
+
require_relative "lca/models/concerns/lcable"
|
|
12
11
|
require_relative "lca/models/model"
|
|
13
12
|
require_relative "lca/models/cycle"
|
|
14
13
|
require_relative "lca/models/stage"
|
|
15
14
|
require_relative "lca/models/process"
|
|
16
15
|
require_relative "lca/models/exchange"
|
|
17
16
|
require_relative "lca/models/impact"
|
|
17
|
+
require_relative "lca/models/impact_snapshot"
|
|
18
18
|
require_relative "lca/models/cycle/product"
|
|
19
19
|
require_relative "lca/models/cycle/service"
|
|
20
20
|
require_relative "lca/models/cycle/activity"
|
|
@@ -38,39 +38,12 @@ require_relative "lca/models/impact/ecosphere/flora"
|
|
|
38
38
|
require_relative "lca/models/impact/human_health/cancer"
|
|
39
39
|
require_relative "lca/models/impact/technosphere/resource_availability"
|
|
40
40
|
|
|
41
|
-
require_relative "lca/models/concerns/lcable"
|
|
42
41
|
require_relative "lca/active_record"
|
|
43
42
|
|
|
44
|
-
# TODO add query objects and builders
|
|
45
|
-
#require "lca/queries/"
|
|
46
|
-
#require "lca/builders/"
|
|
47
43
|
|
|
48
44
|
module Lca
|
|
49
45
|
class Error < StandardError; end
|
|
50
46
|
|
|
51
|
-
# Your code goes here...
|
|
52
|
-
|
|
53
|
-
class << self
|
|
54
|
-
attr_accessor :lca_models
|
|
55
|
-
attr_accessor :options
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
self.lca_models = []
|
|
59
|
-
|
|
60
|
-
def self.lca_options
|
|
61
|
-
@options ||= begin
|
|
62
|
-
path = Rails.root.join("config", "lca.yml").to_s
|
|
63
|
-
if File.exist?(path)
|
|
64
|
-
YAML.load(ERB.new(File.read(path)).result)
|
|
65
|
-
else
|
|
66
|
-
{
|
|
67
|
-
table_name: "lca_models",
|
|
68
|
-
jwt_secret: "",
|
|
69
|
-
jwt_encryption: "HS256"
|
|
70
|
-
}
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
47
|
|
|
75
48
|
def self.env
|
|
76
49
|
@env ||= ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lca
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: '0.3'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nick @ Earthster
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-10-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -25,33 +25,19 @@ dependencies:
|
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '6.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: active_tree
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '0.3'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: actiontext
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - "~>"
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '6.0'
|
|
48
|
-
type: :runtime
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - "~>"
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '6.0'
|
|
40
|
+
version: '0.3'
|
|
55
41
|
- !ruby/object:Gem::Dependency
|
|
56
42
|
name: pg_ltree
|
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -97,14 +83,10 @@ files:
|
|
|
97
83
|
- bin/console
|
|
98
84
|
- bin/setup
|
|
99
85
|
- lib/generators/lca/install_generator.rb
|
|
100
|
-
- lib/generators/lca/templates/config.yml.tt
|
|
101
|
-
- lib/generators/lca/templates/initializer.rb.tt
|
|
102
86
|
- lib/generators/lca/templates/migration.rb.tt
|
|
103
87
|
- lib/lca.rb
|
|
104
88
|
- lib/lca/active_record.rb
|
|
105
|
-
- lib/lca/builders/builder.rb
|
|
106
89
|
- lib/lca/models/concerns/lcable.rb
|
|
107
|
-
- lib/lca/models/concerns/statusable.rb
|
|
108
90
|
- lib/lca/models/cycle.rb
|
|
109
91
|
- lib/lca/models/cycle/activity.rb
|
|
110
92
|
- lib/lca/models/cycle/product.rb
|
|
@@ -118,7 +100,7 @@ files:
|
|
|
118
100
|
- lib/lca/models/impact/human_health/cancer.rb
|
|
119
101
|
- lib/lca/models/impact/technosphere.rb
|
|
120
102
|
- lib/lca/models/impact/technosphere/resource_availability.rb
|
|
121
|
-
- lib/lca/models/
|
|
103
|
+
- lib/lca/models/impact_snapshot.rb
|
|
122
104
|
- lib/lca/models/model.rb
|
|
123
105
|
- lib/lca/models/process.rb
|
|
124
106
|
- lib/lca/models/process/raw_resource.rb
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# the main table will be called "lca_models"
|
|
2
|
-
# partitions will be called "lca_models_X" where X is the owner object ID
|
|
3
|
-
table_name: "lca_models"
|
|
4
|
-
|
|
5
|
-
# create PG roles for postgrest: anon and owner-specific roles
|
|
6
|
-
create_postgrest_roles: true
|
|
7
|
-
|
|
8
|
-
# on owner removal, detach the partition but preserve the table and data, or destroy the partition and data
|
|
9
|
-
# if you choose to detach and preserve, in order to avoid table name collisions, make sure owner IDs are not reused
|
|
10
|
-
destroy_partition_on_owner_destroy: true
|
|
11
|
-
|
|
12
|
-
# jwt secret required for postgrest role switching
|
|
13
|
-
jwt_secret: "supersecret"
|
|
14
|
-
jwt_encryption: "HS256"
|
|
15
|
-
|
|
16
|
-
# suffix postgres roles with a random string
|
|
17
|
-
# to avoid collisions between other LCA installations in other apps using same db server
|
|
18
|
-
owner_role_suffix: "changeme"
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
LCA_OPTIONS ||= begin
|
|
2
|
-
path = Rails.root.join("config", "lca.yml").to_s
|
|
3
|
-
if File.exist?(path)
|
|
4
|
-
YAML.load( ERB.new(File.read(path)).result ).deep_symbolize_keys
|
|
5
|
-
else
|
|
6
|
-
{
|
|
7
|
-
table_name: "lca_models",
|
|
8
|
-
create_postgrest_roles: true,
|
|
9
|
-
jwt_secret: "supersecret",
|
|
10
|
-
jwt_encryption: "HS256",
|
|
11
|
-
destroy_partition_on_owner_destroy: true,
|
|
12
|
-
owner_role_suffix: "changeme"
|
|
13
|
-
}
|
|
14
|
-
end
|
|
15
|
-
end.merge({
|
|
16
|
-
database_user: Rails.application.config.database_configuration[ Rails.env ].deep_symbolize_keys[:username]
|
|
17
|
-
})
|
data/lib/lca/builders/builder.rb
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
module Lca::Statusable
|
|
3
|
-
|
|
4
|
-
extend ActiveSupport::Concern
|
|
5
|
-
|
|
6
|
-
included do
|
|
7
|
-
scope :active, -> { where(status: 1) }
|
|
8
|
-
scope :inactive, -> { where(status: 0) }
|
|
9
|
-
alias_method :enabled?, :active?
|
|
10
|
-
alias_method :enable!, :active!
|
|
11
|
-
alias_method :on!, :active!
|
|
12
|
-
alias_method :disabled?, :inactive?
|
|
13
|
-
alias_method :disable!, :inactive!
|
|
14
|
-
alias_method :off!, :inactive!
|
|
15
|
-
alias_method :toggle?, :toggle_status!
|
|
16
|
-
|
|
17
|
-
before_create :set_default_status
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def set_default_status
|
|
21
|
-
self.status ||= 1
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def toggle_status!
|
|
25
|
-
if active?
|
|
26
|
-
inactive!
|
|
27
|
-
else
|
|
28
|
-
active!
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def status?
|
|
33
|
-
[:inactive, :active][ status ]
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def active?
|
|
37
|
-
status == 1
|
|
38
|
-
end
|
|
39
|
-
def inactive?
|
|
40
|
-
status == 0
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def active!
|
|
44
|
-
self.update(status: 1)
|
|
45
|
-
end
|
|
46
|
-
def inactive!
|
|
47
|
-
self.update(status: 0)
|
|
48
|
-
end
|
|
49
|
-
end
|
data/lib/lca/models/metadata.rb
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
class Lca::Metadata < ActiveRecord::Base
|
|
2
|
-
|
|
3
|
-
belongs_to :model, polymorphic: true, required: true
|
|
4
|
-
|
|
5
|
-
def self.table_name
|
|
6
|
-
return "#{ ::LCA_OPTIONS[:table_name] }_metadata" if defined? ::LCA_OPTIONS
|
|
7
|
-
return "lca_models_metadata"
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
validates_presence_of :key
|
|
11
|
-
|
|
12
|
-
end
|