Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: wip #26

Draft
wants to merge 1 commit into
base: devpf
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
= render partial: "shared/champs/te_fenua/show", locals: { champ: champ, profile: @profile }
- when TypeDeChamp.type_champs.fetch(:numero_dn)
= render partial: "shared/champs/numero_dn/show", locals: { champ: champ, profile: @profile }
- when TypeDeChamp.type_champs.fetch(:table_row_selector)
= render partial: "shared/champs/table_row_selector/show", locals: { champ: champ, profile: @profile }
- when TypeDeChamp.type_champs.fetch(:cojo)
= render partial: "shared/champs/cojo/show", locals: { champ: champ, profile: @profile }
- when TypeDeChamp.type_champs.fetch(:date)
Expand Down
2 changes: 2 additions & 0 deletions app/components/editable_champ/table_row_selector_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class EditableChamp::TableRowSelectorComponent < EditableChamp::ComboSearchComponent
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- render_parent

= @form.hidden_field :value
= @form.hidden_field :external_id
= react_component("ComboTableRowSelectorSearch",
required: @champ.required?,
id: @champ.input_id,
scopeExtra: @champ.table_id,
describedby: @champ.describedby_id,
**react_combo_props)
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@
.cell.width-100
= form.label :accredited_user_string, "Mails des personnes accréditées", for: dom_id(type_de_champ, :accredited_user_string)
= form.text_area :accredited_user_string, class: 'fr-input small-margin small', rows: 7, id: dom_id(type_de_champ, :accredited_user_string), placeholder: 'Ecrire un email par ligne'
- if type_de_champ.table_row_selector?
.flex.width-33
.flex.flex-grow.align-center
.cell.width-100
= form.label :table_id, "Table de recherche", for: dom_id(type_de_champ, :table_id)
= form.select :table_id, type_de_champ.available_tables, {}, id: dom_id(type_de_champ, :table_id), prompt: 'Sélectionnez la table'

-# DS options
- if type_de_champ.drop_down_list?
.flex.width-33
.flex.column.width-33
Expand Down
20 changes: 20 additions & 0 deletions app/controllers/champs/table_row_selector_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

class Champs::TableRowSelectorController < ApplicationController
before_action :authenticate_logged_user!

def search
@params = search_params
if bad_parameters
render json: [], status: 400
else
render json: TableRowSelector::API.search(@params[:domain], @params[:term])
end
end

def bad_parameters
@params[:domain].blank? || @params[:domain].to_i == 0 || @params[:term].blank?
end

def search_params = params.permit(:domain, :term)
end
32 changes: 32 additions & 0 deletions app/javascript/components/ComboTableRowSelectorSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { QueryClientProvider } from 'react-query';

import ComboSearch, { ComboSearchProps } from './ComboSearch';
import { queryClient } from './shared/queryClient';

type TableRowSelectorResult = {
name: string;
id: string;
domain: string;
};

function transformResults(_: unknown, result: unknown) {
return result as TableRowSelectorResult[];
}

export default function ComboTableRowSelectorSearch(
props: ComboSearchProps<TableRowSelectorResult>
) {
return (
<QueryClientProvider client={queryClient}>
<ComboSearch
{...props}
scope="table-row-selector"
scopeExtra={props.scopeExtra}
minimumInputLength={3}
transformResults={transformResults}
transformResult={({ name, id, domain }) => [`${domain}:${id}`, name]}
/>
</QueryClientProvider>
);
}
18 changes: 9 additions & 9 deletions app/javascript/components/shared/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@ const {
autocomplete: { api_adresse_url, api_education_url }
} = getConfig();

type QueryKey = readonly [
scope: string,
term: string,
extra: string | undefined
];
type QueryKey = readonly [scope: string, term: string, extra: string];

function buildURL(scope: string, term: string) {
function buildURL(scope: string, term: string, extra: string) {
term = term.replace(/\(|\)/g, '');
const params = new URLSearchParams();
let path = '';
Expand All @@ -28,17 +24,21 @@ function buildURL(scope: string, term: string) {
params.set('q', term);
params.set('rows', `${API_EDUCATION_QUERY_LIMIT}`);
params.set('dataset', 'fr-en-annuaire-education');
} else if (scope == 'table-row-selector') {
path = '/champs/table_row_selector/search';
params.set('domain', extra);
params.set('term', term);
}

return `${path}?${params}`;
}

const defaultQueryFn: QueryFunction<unknown, QueryKey> = async ({
queryKey: [scope, term],
queryKey: [scope, term, extra],
signal
}) => {
// BAN will error with queries less then 3 chars long
if (scope == 'adresse' && term.length < 3) {
if (term.length < 3 && scope != 'annuaire-education') {
return {
type: 'FeatureCollection',
version: 'draft',
Expand All @@ -47,7 +47,7 @@ const defaultQueryFn: QueryFunction<unknown, QueryKey> = async ({
};
}

const url = buildURL(scope, term);
const url = buildURL(scope, term, extra);
return httpRequest(url, { csrf: false, signal }).json();
};

Expand Down
2 changes: 2 additions & 0 deletions app/jobs/champ_fetch_external_data_job.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'dry/monads/result/fixed'

class ChampFetchExternalDataJob < ApplicationJob
discard_on ActiveJob::DeserializationError

Expand Down
22 changes: 22 additions & 0 deletions app/lib/table_row_selector/api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

class TableRowSelector::API
class << self
def available_tables
engine&.available_tables
end

def search(domain_id, term)
engine&.search(domain_id, term)
end

def fetch_row(external_id)
table, id = external_id.split(':')
engine.fetch_row(table, id)
end

def engine
@engine ||= ENV['API_BASEROW_URL'].present? ? TableRowSelector::BaserowAPI : nil
end
end
end
86 changes: 86 additions & 0 deletions app/lib/table_row_selector/baserow_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# frozen_string_literal: true

class TableRowSelector::BaserowAPI
class << self
SECRETS = Rails.application.secrets.baserow

def search(domain_id, term)
config = config(domain_id)
search_field = config['Champ de recherche']
params = { "filter__field_#{search_field}__contains" => term }
url = rows_url(config['Table'])
response = Typhoeus.get(url, headers: database_headers(config['Token']), params: params)
pp response
if response.success?
JSON.parse(response.body, symbolize_names: true)[:results].map do
{ name: _1[:"field_#{search_field}"], id: _1[:id], domain: domain_id }
end + [{ name: 'Autre', id: 0, domain: domain_id }]
end
end

def available_tables
response = Typhoeus.get(rows_url(SECRETS[:config_table]), headers: config_database_headers, params: default_params)
if response.success?
JSON.parse(response.body, symbolize_names: true)[:results]&.filter { _1[:Actif] }&.map do
{ name: _1[:Nom], id: _1[:id] }
end
end
end

def fetch_row(domain_id, row)
return {} if row.to_i.zero?

config = config(domain_id)
response = Typhoeus.get(row_url(config['Table'], row), headers: database_headers(config['Token']))
if response.success?
model = fields(config)
usager_fields = field_names(model, config['Champs usager'])
instructeur_fields = field_names(model, config['Champs instructeur'])
row = JSON.parse(response.body).filter { |name, _| name.start_with?('field_') }.transform_keys do |key|
model[key[6..-1].to_i]
end
{ usager_fields:, instructeur_fields:, row: }
end
end

def field_names(model, field_ids)
field_ids&.split(/,/)&.map(&:strip)&.map { model[_1.to_i] } || []
end

def config(row_id)
response = Typhoeus.get(row_url(SECRETS[:config_table], row_id), headers: config_database_headers, params: default_params)
response.success? ? JSON.parse(response.body) : nil
end

def fields(config)
response = Typhoeus.get(list_database_table_fields(config['Table']), headers: database_headers(config['Token']))
if response.success?
JSON.parse(response.body).map { [_1['id'], _1['name']] }.to_h
end
end

def rows_url(table_id) = "#{SECRETS[:url]}/api/database/rows/table/#{table_id}/"

def row_url(table_id, row_id) = "#{rows_url(table_id)}#{row_id}/"

def list_database_table_fields(table_id) = "#{SECRETS[:url]}/api/database/fields/table/#{table_id}/"

def config_database_headers = database_headers(SECRETS[:token])

def database_headers(token) = { 'Authorization' => "Token #{token}" }

def default_params = { user_field_names: true }

JWT_URL = "#{SECRETS[:url]}/api/user/token-auth/"
MUTEX = Mutex.new

def meta_headers
credentials = { username: SECRETS[:user], password: SECRETS[:password] }
response = Typhoeus.post(JWT_URL, body: credentials)
if response.success?
jwt = JSON.parse(response.body)['token']
{ authorization: "JWT #{jwt}" }
end
end
end
end
2 changes: 2 additions & 0 deletions app/models/champ.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class Champ < ApplicationRecord
# pf champ
include DateEncodingConcern

delegate :accredited_user_list, :visa?, :table_id, to: :type_de_champ

delegate :accredited_user_list, :visa?, to: :type_de_champ

delegate :to_typed_id, :to_typed_id_for_query, to: :type_de_champ, prefix: true
Expand Down
16 changes: 16 additions & 0 deletions app/models/champs/table_row_selector_champ.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

class Champs::TableRowSelectorChamp < Champs::TextChamp
def fetch_external_data?
true
end

def fetch_external_data
# APIEducation::AnnuaireEducationAdapter.new(external_id).to_params
TableRowSelector::API.fetch_row(external_id)
end

def update_with_external_data!(data:)
update!(data: data) if data&.is_a?(Hash)
end
end
9 changes: 9 additions & 0 deletions app/models/logic/champ_value.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
class Logic::ChampValue < Logic::Term
INSTANCE_MANAGED_TYPE_DE_CHAMP = [:table_row_selector]

MANAGED_TYPE_DE_CHAMP = TypeDeChamp.type_champs.slice(
*INSTANCE_MANAGED_TYPE_DE_CHAMP,
:yes_no,
:checkbox,
:integer_number,
Expand Down Expand Up @@ -64,6 +67,8 @@ def compute(champs)
code_departement: targeted_champ.code_departement,
code_region: targeted_champ.code_region
}
when "Champs::TableRowSelectorChamp"
targeted_champ.value
end
end

Expand All @@ -87,6 +92,8 @@ def type(type_de_champs)
CHAMP_VALUE_TYPE.fetch(:departement_enum)
when MANAGED_TYPE_DE_CHAMP.fetch(:multiple_drop_down_list)
CHAMP_VALUE_TYPE.fetch(:enums)
when MANAGED_TYPE_DE_CHAMP.fetch(:table_row_selector)
CHAMP_VALUE_TYPE.fetch(:enum)
else
CHAMP_VALUE_TYPE.fetch(:unmanaged)
end
Expand Down Expand Up @@ -122,6 +129,8 @@ def options(type_de_champs, operator_name = nil)
APIGeoService.regions.map { ["#{_1[:code]} – #{_1[:name]}", _1[:code]] }
elsif operator_name.in?([Logic::InDepartementOperator.name, Logic::NotInDepartementOperator.name]) || tdc.type_champ.in?([MANAGED_TYPE_DE_CHAMP.fetch(:communes), MANAGED_TYPE_DE_CHAMP.fetch(:epci), MANAGED_TYPE_DE_CHAMP.fetch(:departements)])
APIGeoService.departements.map { ["#{_1[:code]} – #{_1[:name]}", _1[:code]] }
elsif tdc == MANAGED_TYPE_DE_CHAMP.fetch(:table_row_selector)
[['Autre', 'Autre']]
else
tdc.drop_down_list_enabled_non_empty_options(other: true).map { _1.is_a?(Array) ? _1 : [_1, _1] }
end
Expand Down
15 changes: 13 additions & 2 deletions app/models/type_de_champ.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class TypeDeChamp < ApplicationRecord
code_postal_de_polynesie: 'code_postal_de_polynesie',
numero_dn: 'numero_dn',
te_fenua: 'te_fenua',
table_row_selector: 'table_row_selector',
visa: 'visa'
}

Expand All @@ -39,6 +40,7 @@ class TypeDeChamp < ApplicationRecord
code_postal_de_polynesie: LOCALISATION,
numero_dn: REFERENTIEL_EXTERNE,
te_fenua: REFERENTIEL_EXTERNE,
table_row_selector: REFERENTIEL_EXTERNE,
visa: STRUCTURE
}

Expand Down Expand Up @@ -130,7 +132,7 @@ class TypeDeChamp < ApplicationRecord
expression_reguliere: 'expression_reguliere'
}.merge(INSTANCE_TYPE_CHAMPS)

INSTANCE_OPTIONS = [:parcelles, :batiments, :zones_manuelles, :min, :max, :level, :accredited_users]
INSTANCE_OPTIONS = [:parcelles, :batiments, :zones_manuelles, :min, :max, :level, :accredited_users, :table_id]
INSTANCE_CHAMPS_PARAMS = [:numero_dn, :date_de_naissance]

ROUTABLE_TYPES = [
Expand Down Expand Up @@ -463,6 +465,10 @@ def te_fenua?
type_champ == TypeDeChamp.type_champs.fetch(:te_fenua)
end

def table_row_selector?
type_champ == TypeDeChamp.type_champs.fetch(:table_row_selector)
end

def cnaf?
type_champ == TypeDeChamp.type_champs.fetch(:cnaf)
end
Expand Down Expand Up @@ -661,6 +667,10 @@ def accredited_user_list
accredited_users.presence || []
end

def available_tables
TableRowSelector::API.available_tables.map { [_1[:name], _1[:id]] }
end

def to_typed_id
GraphQL::Schema::UniqueWithinType.encode('Champ', stable_id)
end
Expand Down Expand Up @@ -719,7 +729,8 @@ def self.refresh_after_update?(type_champ)
type_champs.fetch(:rna),
type_champs.fetch(:siret),
type_champs.fetch(:numero_dn),
type_champs.fetch(:te_fenua)
type_champs.fetch(:te_fenua),
type_champs.fetch(:table_row_selector)
false
else
true
Expand Down
4 changes: 4 additions & 0 deletions app/models/types_de_champ/table_row_selector_type_de_champ.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# frozen_string_literal: true

class TypesDeChamp::TableRowSelectorTypeDeChamp < TypesDeChamp::TypeDeChampBase
end
Loading
Loading