<commit_before>'use strict'

const { getFrontSchema, RESERVED_FIELDS, FRONT_KEPT_FIELDS } = require('./schemas')
const { readdirSync } = require('fs')
const path = require('path')
const { pick, merge, map, difference, isArray, isObject, isString, flatten } = require('lodash/fp')
const util = require('util')
const memoize = require('memoizee')


module.exports = {
	getLayout: memoize(name => _getLayout(name, getFrontSchema(name)))
}


// At startup, simply load all layouts
// TODO make it more dynamic?
const root = path.join(__dirname, '..', '..', 'specs')
const layouts = readLayoutJSONs()


function readLayoutJSONs () {
	return readdirSync(root)
		.filter(f => f.substring(0, 7) === 'layout.')
		.reduce((o, f) => {
			const key = f.substring(7, f.length - 5)
			const data = require(path.join(root, f))
			return Object.assign(o, { [key]: data })
		}, {})
}

const _getLayout = (name, schema) => {
	if (!schema) {
		return null
	}
	const rows = (layouts[name] || []).map(getRow(name, schema))
	const rowsFields = flatten(rows.map(row => map('name', row.fields)))
	const expectedFields = Object.keys(schema).filter(f => !RESERVED_FIELDS.includes(f) && f[0] !== '/')
	const missingFields = difference(expectedFields, rowsFields)
	return rows.concat(missingFields.map(getRow(name, schema)))
		// Ignored fields
		.map(row => Object.assign(row, {
			fields: row.fields.filter(field => !field.ignored) // Remove ignored fields
		}))
		.filter(row => row.fields.length > 0) // Remove empty rows
}

const getRow = (name, schema) => row => {
	if (isArray(row)) {
		return getRowArray(name, schema, row)
	} else if (isObject(row)) {
		return getRowObject(name, schema, row)
	} else if (isString(row)) {
		return getRowString(name, schema, row)
	} else {
		throw Error(`${name}: invalid type for layout row description (expects array, object or string, got ${util.inspect(row)}`)
	}
}

const getRowString = (baseName, schema, row) => getRowArray(baseName, schema, [row])

const getRowArray = (baseName, schema, fields) => getRowObject(baseName, schema, {
	// no label
	// not collapsable
	fields
})

const getRowObject = (baseName, schema, row) => merge(
	pick([ 'label', 'collapsable' ].concat(FRONT_KEPT_FIELDS), row),
	{
		fields: row.fields.map(getFieldsDescription(baseName, schema))
	}
)

const getFieldsDescription = (baseName, schema) => name => {
	if (name[0] === '-') {
		return {
			name: name.substring(1),
			ignored: true
		}<commit_msg>fix(layouts): fixes missing 'collapsabled' in /layouts/…<commit_after>'use strict'

const { getFrontSchema, RESERVED_FIELDS, FRONT_KEPT_FIELDS } = require('./schemas')
const { readdirSync } = require('fs')
const path = require('path')
const { pick, merge, map, difference, isArray, isObject, isString, flatten } = require('lodash/fp')
const util = require('util')
const memoize = require('memoizee')


module.exports = {
	getLayout: memoize(name => _getLayout(name, getFrontSchema(name)))
}


// At startup, simply load all layouts
// TODO make it more dynamic?
const root = path.join(__dirname, '..', '..', 'specs')
const layouts = readLayoutJSONs()


function readLayoutJSONs () {
	return readdirSync(root)
		.filter(f => f.substring(0, 7) === 'layout.')
		.reduce((o, f) => {
			const key = f.substring(7, f.length - 5)
			const data = require(path.join(root, f))
			return Object.assign(o, { [key]: data })
		}, {})
}

const _getLayout = (name, schema) => {
	if (!schema) {
		return null
	}
	const rows = (layouts[name] || []).map(getRow(name, schema))
	const rowsFields = flatten(rows.map(row => map('name', row.fields)))
	const expectedFields = Object.keys(schema).filter(f => !RESERVED_FIELDS.includes(f) && f[0] !== '/')
	const missingFields = difference(expectedFields, rowsFields)
	return rows.concat(missingFields.map(getRow(name, schema)))
		// Ignored fields
		.map(row => Object.assign(row, {
			fields: row.fields.filter(field => !field.ignored) // Remove ignored fields
		}))
		.filter(row => row.fields.length > 0) // Remove empty rows
}

const getRow = (name, schema) => row => {
	if (isArray(row)) {
		return getRowArray(name, schema, row)
	} else if (isObject(row)) {
		return getRowObject(name, schema, row)
	} else if (isString(row)) {
		return getRowString(name, schema, row)
	} else {
		throw Error(`${name}: invalid type for layout row description (expects array, object or string, got ${util.inspect(row)}`)
	}
}

const getRowString = (baseName, schema, row) => getRowArray(baseName, schema, [row])

const getRowArray = (baseName, schema, fields) => getRowObject(baseName, schema, {
	// no label
	// not collapsable
	fields
})

const getRowObject = (baseName, schema, row) => merge(
	pick([ 'label', 'collapsabled' ].concat(FRONT_KEPT_FIELDS), row),
	{
		fields: row.fields.map(getFieldsDescription(baseName, schema))
	}
)

const getFieldsDescription = (baseName, schema) => name => {
	if (name[0] === '-') {
		return {
			name: name.substring(1),
			ignored: true
		}
{'fraction_non_alphanumeric': 0.16828478964401294, 'fraction_numerical': 0.003236245954692557, 'mean_word_length': 4.568693693693693, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1963385', 'n_tokens_mistral': 1755, 'n_tokens_neox': 1678, 'n_words': 421}