Module: Labimotion::SegmentHelpers

Extended by:
Grape::API::Helpers
Defined in:
lib/labimotion/helpers/segment_helpers.rb

Overview

ElementHelpers

Instance Method Summary collapse

Instance Method Details

#create_ai_segment_klass(current_user, params) ⇒ Object

Create a new (inactive) segment klass whose properties template is generated by an LLM from a user-provided label plus optional description, reference links and uploaded files. The parent ElementKlass must be set on admin reviews/edits the generated template in the designer and activates it.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/labimotion/helpers/segment_helpers.rb', line 62

def create_ai_segment_klass(current_user, params)
  return { status: 'error', message: 'A parent element class is required.' } if @klass.nil?

  if Array(params[:files]).size > Labimotion::AiTemplate::MAX_FILES
    return { status: 'error', message: "Too many files (max #{Labimotion::AiTemplate::MAX_FILES})." }
  end

  overrides = ai_user_overrides(current_user)
  ai = Labimotion::AiTemplate.generate(
    kind: 'segment',
    subject: params[:label],
    desc: params[:desc],
    cols: params[:cols],
    references: params[:references],
    files: params[:files],
    **overrides
  )

  uuid = SecureRandom.uuid
  # property-base schema requires pkg, uuid, klass, layers, version, identifier.
  properties_template = {
    'uuid' => uuid,
    'klass' => 'SegmentKlass',
    'pkg' => Labimotion::Utils.pkg(nil),
    'version' => '1.0.0',
    'identifier' => uuid,
    'layers' => ai['layers'],
    'select_options' => ai['select_options'],
    'metadata' => ai['metadata']
  }
  attributes = {
    'label' => params[:label],
    'desc' => params[:desc].presence || ai['label'].presence,
    'element_klass' => @klass,
    'place' => 100,
    'is_active' => false,
    'uuid' => uuid,
    'released_at' => DateTime.now,
    'properties_template' => properties_template,
    'properties_release' => properties_template,
    'created_by' => current_user.id
  }
  # Klass-level segment metadata (render kind: own tab vs the host element's
  # Properties tab), mirroring the non-AI create_segment_klass which persists
  # params[:metadata] via declared. Absent -> leave the column default.
  attributes['metadata'] = params[:metadata] if params[:metadata].present?

  klass = Labimotion::SegmentKlass.create!(attributes)
  klass.reload
  klass.create_klasses_revision(current_user)
  { status: 'success',
    message: "The AI segment template [#{params[:label]}] has been created as inactive. Review and activate it in the designer." }
rescue StandardError => e
  Labimotion.log_exception(e, current_user)
  { status: 'error', message: e.message }
end

#create_repo_klass(params, current_user, origin) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/labimotion/helpers/segment_helpers.rb', line 183

def create_repo_klass(params, current_user, origin)
  response = Labimotion::TemplateHub.fetch_identifier('SegmentKlass', params[:identifier], origin)
  attributes = response.slice('label', 'desc', 'uuid', 'identifier', 'released_at', 'properties_release', 'version', 'metadata')
  attributes['properties_release']['identifier'] = attributes['identifier']
  attributes['properties_template'] = attributes['properties_release']
  attributes['place'] = ((Labimotion::SegmentKlass.all.length * 10) || 0) + 10
  attributes['is_active'] = false
  attributes['updated_by'] = current_user.id
  attributes['sync_by'] = current_user.id
  attributes['sync_time'] = DateTime.now
  attr_klass = response['element_klass']    ## response.dig('element_klass', {})        # response['element_klass']
  validate_klass(attributes, attr_klass)

rescue StandardError => e
  Labimotion.log_exception(e, current_user)
  raise e
end

#create_segment_klass(current_user, params) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/labimotion/helpers/segment_helpers.rb', line 21

def create_segment_klass(current_user, params)
  place = params[:place]
  begin
    place = place.to_i if place.present? && place.to_i == place.to_f
  rescue StandardError
    place = 100
  end

  uuid = SecureRandom.uuid
  template = { uuid: uuid, layers: {}, select_options: {} }
  attributes = declared(params, include_missing: false)
  attributes[:properties_template]['uuid'] = uuid if attributes[:properties_template].present?
  template = (attributes[:properties_template].presence || template)
  template['pkg'] = Labimotion::Utils.pkg(template['pkg'])
  template['klass'] = 'SegmentKlass'
  attributes.merge!(properties_template: template, element_klass: @klass, created_by: current_user.id,
                    place: place)
  attributes[:is_active] = false
  attributes[:uuid] = uuid
  attributes[:released_at] = DateTime.now
  attributes[:properties_release] = attributes[:properties_template]
  # The owner row shares the create's transaction: a klass committed without it is
  # owner-less, which falls open to the legacy designer-wide gate.
  klass = Labimotion::SegmentKlass.transaction do
    Labimotion::SegmentKlass.create!(attributes).tap do |klz|
      Labimotion::KlassShare.seed_owner!(klz, current_user.id)
    end
  end
  klass.reload
  klass.create_klasses_revision(current_user)
  klass
rescue StandardError => e
  Labimotion.log_exception(e, current_user)
  raise e
end

#klass_list(el_klass, is_active = false, displayed_in_list = false) ⇒ Object



14
15
16
17
18
19
# File 'lib/labimotion/helpers/segment_helpers.rb', line 14

def klass_list(el_klass, is_active = false, displayed_in_list = false)
  scope = displayed_in_list ? Labimotion::SegmentKlass.for_list_display : Labimotion::SegmentKlass.all
  scope = scope.where(is_active: is_active) if is_active.present? && is_active == true
  scope = scope.joins(:element_klass).where(klass_element: params[:element], is_active: true).preload(:element_klass) if el_klass.present?
  scope.order('place') || []
end

#update_segment_klass(current_user, params) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/labimotion/helpers/segment_helpers.rb', line 119

def update_segment_klass(current_user, params)
  segment = fetch_klass('SegmentKlass', params[:id])
  authorize_klass!(segment, :write)
  place = params[:place]
  begin
    place = place.to_i if place.present? && place.to_i == place.to_f
  rescue StandardError
    place = 100
  end
  attributes = declared(params, include_missing: false)
  attributes.delete(:id)
  attributes[:place] = place
  segment&.update!(attributes)
  segment
rescue StandardError => e
  Labimotion.log_exception(e, current_user)
  raise e
end

#validate_klass(attributes, attr_klass) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/labimotion/helpers/segment_helpers.rb', line 138

def validate_klass(attributes, attr_klass)
  element_klass = Labimotion::ElementKlass.find_by(identifier: attr_klass['identifier']) if attr_klass.dig('identifier').present?
  element_klass = Labimotion::ElementKlass.find_by(name: attr_klass['name'], is_generic: false) if element_klass.nil?
  return { status: 'error', message: "The element [#{attr_klass['name']}] does not exist in this instance" } if element_klass.nil?

  # el_attributes = response['element_klass'].slice('name', 'label', 'desc', 'uuid', 'identifier', 'icon_name', 'klass_prefix', 'is_generic', 'released_at')
  # el_attributes['properties_template'] = response['element_klass']['properties_release']
  # Labimotion::ElementKlass.create!(el_attributes)
  attributes['element_klass_id'] = element_klass.id
  segment_klass = Labimotion::SegmentKlass.find_by(identifier: attributes['identifier']) if attributes['identifier'].present?
  if segment_klass.present?
    if segment_klass['uuid'] == attributes['uuid'] && segment_klass['version'] == attributes['version']
      return { status: 'success', message: "This segment: #{attributes['label']} has the latest version!" }
    else
      # Upgrading bumps the version and cuts a revision — :release, owner-only, and the
      # gate lives here because the create/update branch is unknown until the identifier
      # lookup. error! throws rather than raises, so it escapes this method's rescue.
      authorize_klass!(segment_klass, :release)
      # Same reassignment defect as the element path: `upload_klass` merges
      # `created_by: current_user.id` unconditionally (segment_api.rb:147), so upgrading an
      # existing segment template would hand its authorship to the importer.
      segment_klass.update!(attributes.except('created_by', :created_by))
      segment_klass.create_klasses_revision(current_user)
      return { status: 'success', message: "This segment: [#{attributes['label']}] has been upgraded to the version: #{attributes['version']}!" }
    end
  else
    exist_klass = Labimotion::SegmentKlass.find_by(label: attributes['label'], element_klass_id: element_klass.id)
    if exist_klass.present?
      return { status: 'error', message: "The segment [#{attributes['label']}] is already in use." }
    else
      attributes['created_by'] = current_user.id
      segment_klass = Labimotion::SegmentKlass.transaction do
        Labimotion::SegmentKlass.create!(attributes).tap do |klz|
          Labimotion::KlassShare.seed_owner!(klz, current_user.id)
        end
      end
      segment_klass.create_klasses_revision(current_user)
      return { status: 'success', message: "The segment: #{attributes['label']} has been created using version: #{attributes['version']}!" }
    end
  end
rescue StandardError => e
  Labimotion.log_exception(e, current_user)
  return { status: 'error', message: e.message }
end