Skip to content

Commit

Permalink
Chain: ignore cycles (#3575)
Browse files Browse the repository at this point in the history
* Make chain available to all cycles (WIP)

* chain: ignore cycles

---------

Co-authored-by: Stefano Ricci <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Sep 25, 2024
1 parent bdc8663 commit fa926f3
Show file tree
Hide file tree
Showing 15 changed files with 51 additions and 125 deletions.
2 changes: 0 additions & 2 deletions common/analysis/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export const keys = {
export const keysProps = {
labels: ObjectUtils.keysProps.labels,
descriptions: ObjectUtils.keysProps.descriptions,
cycles: ObjectUtils.keysProps.cycles,
hasSamplingDesign: 'hasSamplingDesign',
samplingDesign: 'samplingDesign',
analysisNodeDefs: 'analysisNodeDefs',
Expand All @@ -46,7 +45,6 @@ export const {
getUuid,
getProps,
getPropsDiff,
getCycles,
getDateCreated,
getDateModified,
getDescriptions,
Expand Down
6 changes: 1 addition & 5 deletions common/model/db/tables/chain/select.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import * as Chain from '@common/analysis/chain'

function _getSelectFields({ count, includeScript }) {
if (count) {
return ['count(*)']
Expand All @@ -14,15 +12,14 @@ function _getSelectFields({ count, includeScript }) {
* Generate the select query for the processing_chain table by the given parameters.
*
* @param {!object} params - The query parameters.
* @param {string} [params.cycle=null] - The survey cycle to filter by.
* @param {string} [params.chainUuid=null] - The chain uuid to filter by.
* @param {boolean} [params.count=false] - Whether to count.
* @param {boolean} [params.includeScript=false] - Whether to include R scripts.
*
* @returns {string} - The select query.
*/
export function getSelect(params) {
const { cycle = null, chainUuid = null, count = false, includeScript = false } = params
const { chainUuid = null, count = false, includeScript = false } = params

this.getSelectFields = _getSelectFields.bind(this)

Expand All @@ -31,7 +28,6 @@ export function getSelect(params) {
FROM
${this.nameAliased}
${cycle ? `WHERE (${this.columnProps})->'${Chain.keysProps.cycles}' @> '"${cycle}"'` : ''}
${chainUuid ? `WHERE ${this.columnUuid} = '${chainUuid}'` : ''}
`
}
12 changes: 6 additions & 6 deletions server/modules/analysis/api/chainApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ export const init = (app) => {

app.post('/survey/:surveyId/chain/', AuthMiddleware.requireRecordAnalysisPermission, async (req, res, next) => {
try {
const { cycle, surveyId } = Request.getParams(req)
const { surveyId } = Request.getParams(req)
const user = Request.getUser(req)

const chain = await AnalysisService.create({ user, surveyId, cycle })
const chain = await AnalysisService.create({ user, surveyId })

res.json(chain)
} catch (error) {
Expand All @@ -32,9 +32,9 @@ export const init = (app) => {
AuthMiddleware.requireRecordAnalysisPermission,
async (req, res, next) => {
try {
const { surveyId, surveyCycleKey: cycle, offset, limit } = Request.getParams(req)
const { surveyId, offset, limit } = Request.getParams(req)

const list = await AnalysisService.fetchChains({ surveyId, cycle, offset, limit })
const list = await AnalysisService.fetchChains({ surveyId, offset, limit })

res.json({ list })
} catch (error) {
Expand All @@ -48,9 +48,9 @@ export const init = (app) => {
AuthMiddleware.requireRecordAnalysisPermission,
async (req, res, next) => {
try {
const { surveyId, surveyCycleKey: cycle } = Request.getParams(req)
const { surveyId } = Request.getParams(req)

const count = await AnalysisService.countChains({ surveyId, cycle })
const count = await AnalysisService.countChains({ surveyId })

res.json({ count })
} catch (error) {
Expand Down
40 changes: 17 additions & 23 deletions server/modules/analysis/manager/chain/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,26 @@ export const insertChain = async ({ user, surveyId, chain }, client = DB.client)
return chainDb
})

export const create = async ({ user, surveyId, cycle }) => {
let newChain = ChainFactory.createInstance({ cycles: [cycle] })

export const create = async ({ user, surveyId }) => {
const survey = await SurveyManager.fetchSurveyAndNodeDefsBySurveyId({ surveyId, draft: true, advanced: true })
const defaultLang = Survey.getDefaultLanguage(Survey.getSurveyInfo(survey))
const validation = await ChainValidator.validateChain({ chain: newChain, defaultLang, survey })
const surveyInfo = Survey.getSurveyInfo(survey)
const cycles = Survey.getCycleKeys(surveyInfo)
const defaultLang = Survey.getDefaultLanguage(surveyInfo)

newChain = Chain.assocValidation(validation)(newChain)
let chain = ChainFactory.createInstance({ cycles })

return insertChain({
surveyId,
user,
chain: newChain,
})
const validation = await ChainValidator.validateChain({ chain, defaultLang, survey })

chain = Chain.assocValidation(validation)(chain)

return insertChain({ surveyId, user, chain })
}

// ====== READ
export const { countChains, fetchChains, fetchChain } = ChainRepository

// ====== UPDATE
export const { updateChain, removeChainCycles } = ChainRepository
export const { updateChain } = ChainRepository

export const updateChainStatusExec = async ({ user, surveyId, chainUuid, statusExec }) =>
DB.client.tx(async (tx) => {
Expand Down Expand Up @@ -101,28 +100,26 @@ export const persistChain = async ({ user, surveyId, chain }, client) => {

// ====== DELETE

export const _deleteChain = async ({ user, surveyId, chainUuid = false, noCycle = false }, client = DB.client) => {
const deletedChains = await ChainRepository.deleteChain({ surveyId, chainUuid, noCycle }, client)

const deletedChainsUuids = deletedChains.map(Chain.getUuid)
export const _deleteChain = async ({ user, surveyId, chainUuid }, client = DB.client) => {
const deletedChain = await ChainRepository.deleteChain({ surveyId, chainUuid }, client)
const deletedChainUuid = Chain.getUuid(deletedChain)
const survey = await SurveyManager.fetchSurveyAndNodeDefsBySurveyId(
{ surveyId, draft: true, advanced: true, includeAnalysis: true },
(client = DB.client)
)

const nodeDefsUuidsInDeleteChains = Survey.getNodeDefsArray(survey)
.filter((_nodeDef) => deletedChainsUuids.includes(NodeDef.getChainUuid(_nodeDef)))
.filter((_nodeDef) => deletedChainUuid === NodeDef.getChainUuid(_nodeDef))
.map(NodeDef.getUuid)

await NodeDefService.markNodeDefsDeleted({ user, surveyId, nodeDefUuids: nodeDefsUuidsInDeleteChains }, client)

return deletedChains
return deletedChain
}

export const deleteChain = async ({ user, surveyId, chainUuid }, client = DB.client) =>
client.tx(async (tx) => {
const deletedChains = await _deleteChain({ user, surveyId, chainUuid }, tx)
const deletedChain = deletedChains[0]
const deletedChain = await _deleteChain({ user, surveyId, chainUuid }, tx)

const content = {
[ActivityLog.keysContent.uuid]: chainUuid,
Expand All @@ -133,6 +130,3 @@ export const deleteChain = async ({ user, surveyId, chainUuid }, client = DB.cli
markSurveyDraft(surveyId, tx),
])
})

export const deleteChainWithoutCycle = async ({ surveyId }, client = DB.client) =>
client.tx(async (tx) => _deleteChain({ surveyId, noCycle: true }, tx))
12 changes: 1 addition & 11 deletions server/modules/analysis/manager/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
// ====== Chain
export {
create,
countChains,
fetchChains,
fetchChain,
updateChain,
updateChainStatusExec,
removeChainCycles,
deleteChain,
deleteChainWithoutCycle,
} from './chain'
export { create, countChains, fetchChains, fetchChain, updateChain, updateChainStatusExec, deleteChain } from './chain'

export { cleanChains } from './chainsCleanManager'
17 changes: 5 additions & 12 deletions server/modules/analysis/repository/chain/delete.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import * as Chain from '@common/analysis/chain'

import * as DB from '../../../../db'

import { TableChain } from '../../../../../common/model/db'
Expand All @@ -9,26 +7,21 @@ import { TableChain } from '../../../../../common/model/db'
*
* @param {!object} params - The query parameters.
* @param {!string} params.surveyId - The survey id.
* @param {string} [params.chainUuid=null] - The chain uuid to delete.
* @param {boolean} [params.noCycle=false] - Whether to delete chains with no cycles.
* @param {!string} [params.chainUuid] - The chain uuid to delete.
* @param {pgPromise.IDatabase} [client=db] - The database client.
*
* @returns {Promise<any[]>} - The result promise.
*/
export const deleteChain = async (params, client = DB.client) => {
const { surveyId, chainUuid = null, noCycle = false } = params

if (!chainUuid && !noCycle)
throw new Error(`One between chainUuid and noCycle are required. {chainUuid:${chainUuid}, noCycle:${noCycle}}`)
const { surveyId, chainUuid } = params

const tableChain = new TableChain(surveyId)

return client.any(
return client.one(
`DELETE FROM ${tableChain.nameQualified}
WHERE
${chainUuid ? TableChain.columnSet.uuid : `jsonb_array_length(props->'${Chain.keysProps.cycles}')`} = $1
WHERE ${TableChain.columnSet.uuid} = $1
RETURNING *`,
chainUuid ? [chainUuid] : [0],
[chainUuid],
DB.transformCallback
)
}
2 changes: 1 addition & 1 deletion server/modules/analysis/repository/chain/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ export { insertMany } from './insert'

export { countChains, fetchChains, fetchChain } from './read'

export { updateChain, removeChainCycles } from './update'
export { updateChain } from './update'

export { deleteChain } from './delete'
14 changes: 6 additions & 8 deletions server/modules/analysis/repository/chain/read.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,25 @@ export const transformCallback = (row) => {
}

/**
* Count the processing chains by the given survey id and the optional survey cycle.
* Count the processing chains by the given survey id.
*
* @param {!object} params - The query parameters.
* @param {!string} params.surveyId - The survey id.
* @param {string} [params.cycle=null] - The survey cycle.
* @param {BaseProtocol} [client=db] - The database client.
*
* @returns {Promise<number>} - The result promise.
*/
export const countChains = async (params, client = DB.client) => {
const { surveyId, cycle = null } = params
const { surveyId } = params
const tableChain = new TableChain(surveyId)
return client.one(`${tableChain.getSelect({ surveyId, cycle, count: true })}`, [], (row) => Number(row.count))
return client.one(`${tableChain.getSelect({ surveyId, count: true })}`, [], (row) => Number(row.count))
}

/**
* Fetches all processing chains by the given survey id and the optional survey cycle.
* Fetches all processing chains by the given survey id.
*
* @param {!object} params - The query parameters.
* @param {!string} params.surveyId - The survey id.
* @param {string} [params.cycle=null] - The survey cycle.
* @param {number} [params.offset=0] - The select query offset.
* @param {number} [params.limit=null] - The select query limit.
* @param {boolean} [params.includeScript=false] - Whether to include the R scripts.
Expand All @@ -42,12 +40,12 @@ export const countChains = async (params, client = DB.client) => {
* @returns {Promise<any[]>} - The result promise.
*/
export const fetchChains = async (params, client = DB.client) => {
const { surveyId, cycle = null, offset = 0, limit = null, includeScript = false } = params
const { surveyId, offset = 0, limit = null, includeScript = false } = params

const tableChain = new TableChain(surveyId)

return client.map(
`${tableChain.getSelect({ surveyId, cycle, includeScript })}
`${tableChain.getSelect({ surveyId, includeScript })}
ORDER BY ${tableChain.columnDateCreated} DESC
LIMIT ${limit || 'ALL'}
OFFSET ${offset}`,
Expand Down
23 changes: 1 addition & 22 deletions server/modules/analysis/repository/chain/update.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as DB from '../../../../db'

import * as Chain from '@common/analysis/chain'
import { TableChain } from '../../../../../common/model/db'

/**
Expand All @@ -9,7 +8,7 @@ import { TableChain } from '../../../../../common/model/db'
* @param {!object} params - The query parameters.
* @param {!string} params.surveyId - The survey id.
* @param {!string} params.chainUuid - The processing chain uuid.
* @param {Object<string, any>} [params.fields={}] - A <key, value> object containing the fields to update.
* @param {{[key:string]: any}} [params.fields={}] - A <key, value> object containing the fields to update.
* @param {boolean} [params.dateExecuted=false] - Whether to update date executed to current time.
* @param {boolean} [params.dateModified=false] - Whether to update date modified to current time.
* @param {pgPromise.IDatabase} [client=db] - The database client.
Expand Down Expand Up @@ -41,23 +40,3 @@ export const updateChain = async (params, client = DB.client) => {
[chainUuid, ...Object.values(fields)]
)
}

/**
* Remove survey cycles from all processing chains.
*
* @param {!object} params - The query parameters.
* @param {!string} params.surveyId - The survey id.
* @param {!Array.<string>} params.cycles - The cycles to remove.
* @param {pgPromise.IDatabase} [client=db] - The database client.
*
* @returns {Promise<null>} - The result promise.
*/
export const removeChainCycles = async (params, client) => {
const { surveyId, cycles } = params
const tableChain = new TableChain(surveyId)
return client.none(
`UPDATE ${tableChain.nameQualified}
SET ${TableChain.columnSet.props} = jsonb_set(${TableChain.columnSet.props}, '{${Chain.keysProps.cycles}}',
(${TableChain.columnSet.props}->'${Chain.keysProps.cycles}') ${cycles.map((cycle) => `- '${cycle}'`).join(' ')})`
)
}
1 change: 0 additions & 1 deletion server/modules/analysis/service/chainSummaryGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ const generateChainSummary = async ({ surveyId, chainUuid, cycle, lang: langPara
label: Chain.getLabel(lang, defaultLang)(chain),
selectedLanguage: lang,
selectedCycle: getCycleLabel(cycle),
cycles: Chain.getCycles(chain).map(getCycleLabel),
samplingDesign: Chain.hasSamplingDesign(chain),
baseUnit: NodeDef.getName(baseUnitNodeDef),
baseUnitEntityKeys,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,17 @@ import * as Survey from '@core/survey/survey'

import * as SurveyManager from '@server/modules/survey/manager/surveyManager'
import * as NodeDefManager from '@server/modules/nodeDef/manager/nodeDefManager'
import * as AnalysisManager from '@server/modules/analysis/manager'

export default class ChainsCyclesCheckJob extends Job {
constructor(params) {
super(ChainsCyclesCheckJob.type, params)
}

async execute() {
this.total = 3

const { surveyId, tx } = this
const { cycleKeys, cycleKeysDeleted = [] } = await this._getCycleKeys()
const { cycleKeys } = await this._getCycleKeys()
// 1. set all survey cycles to all nodeDef analysis
await NodeDefManager.updateNodeDefAnalysisCycles(surveyId, cycleKeys, tx)
this.incrementProcessedItems()

// 2. dissoc deleted survey cycles from processing chains
if (!R.isEmpty(cycleKeysDeleted)) {
await AnalysisManager.removeChainCycles({ surveyId, cycles: cycleKeysDeleted }, tx)
}
this.incrementProcessedItems()

// 3. delete processing chains with no cycles
await AnalysisManager.deleteChainWithoutCycle({ surveyId }, tx)
this.incrementProcessedItems()
}

async _getCycleKeys() {
Expand Down
18 changes: 9 additions & 9 deletions webapp/components/form/Input/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,21 +128,21 @@ Input.propTypes = {
inputType: PropTypes.oneOf(['input', 'textarea']),
maxLength: PropTypes.number,
name: PropTypes.string,
onChange: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
placeholder: PropTypes.string,
readOnly: PropTypes.bool,
title: PropTypes.string,
type: PropTypes.oneOf(['text', 'number', 'password']),
validation: PropTypes.object,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
numberFormat: PropTypes.shape({
decimalScale: PropTypes.number,
decimalSeparator: PropTypes.string,
format: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
maxLength: PropTypes.number,
placeholder: PropTypes.string,
}),
onChange: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
placeholder: PropTypes.string,
readOnly: PropTypes.bool,
textTransformFunction: PropTypes.func,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
type: PropTypes.oneOf(['text', 'number', 'password']),
validation: PropTypes.object,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}
4 changes: 2 additions & 2 deletions webapp/components/form/SimpleTextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ SimpleTextInput.propTypes = {
startAdornment: PropTypes.any,
testId: PropTypes.string,
textTransformFunction: PropTypes.func,
title: PropTypes.string,
title: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
type: PropTypes.string,
value: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
}
Loading

0 comments on commit fa926f3

Please sign in to comment.