Skip to content

Commit

Permalink
Merge pull request #155 from i-VRESSE/no-exit
Browse files Browse the repository at this point in the history
Skip exit module + regen catalogs
  • Loading branch information
sverhoeven authored May 16, 2024
2 parents 1fe927d + 6d48844 commit 549bde0
Show file tree
Hide file tree
Showing 13 changed files with 1,473 additions and 238 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Changed

* Autosave ([#144](https://github.com/i-VRESSE/workflow-builder/pull/144))
* Improved handling of groups in ui schema
* Nodes with all its properties in a single group is expanded by default

### Fixed

* Hide the exit module ([#153](https://github.com/i-VRESSE/workflow-builder/issues/153))

## @i-vresse/wb-core 2.0.1 - 2024-03-25

### Changed
Expand Down
2 changes: 1 addition & 1 deletion apps/kitchensink/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@vitejs/plugin-react": "^2.0.0",
"ts-standard": "^11.0.0",
"ts-standard": "^12.0.2",
"typescript": "^4.7.4",
"vite": "^3.0.4"
},
Expand Down
3 changes: 2 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@types/js-yaml": "^4.0.5",
"@types/papaparse": "^5.3.2",
"@types/react-syntax-highlighter": "^13.5.2",
"@vitest/coverage-v8": "^1.5.3",
"babel-loader": "^8.2.5",
"c8": "^7.11.0",
"jsdom": "^20.0.0",
Expand All @@ -59,7 +60,7 @@
"tsup": "^6.1.3",
"typedoc": "^0.23.7",
"vite": "^3.0.4",
"vitest": "^0.20.2"
"vitest": "1.5.3"
},
"dependencies": {
"@dnd-kit/core": "^6.0.5",
Expand Down
84 changes: 83 additions & 1 deletion packages/core/src/grouper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { groupCatalog, groupParameters, groupSchema, groupUiSchema, unGroupParam
import { ICatalog, IParameters } from './types'

function deepCopy<T> (value: T): T {
return JSON.parse(JSON.stringify(value))
return structuredClone(value)
}

describe('given a schema without any property with ui:group in uiSchema', () => {
Expand Down Expand Up @@ -504,6 +504,88 @@ describe('given a un-grouped prop with same name as group', () => {
})
})

describe('given a prop in own group with same name as group of another prop', () => {
describe('groupSchema()', () => {
it('should make 2 groups each with a prop', () => {
const schema: JSONSchema7 = {
type: 'object',
properties: {
receptor_chains: {
type: 'string'
},
restraints: {
type: 'string'
}
},
additionalProperties: false
}
const uiSchema: UiSchema = {
receptor_chains: {
'ui:group': 'restraints'
},
restraints: {
'ui:group': 'distance restraints'
}
}
const groupedSchema = groupSchema(schema, uiSchema)

const expectedSchema: JSONSchema7 = {
type: 'object',
properties: {
restraints: {
type: 'object',
properties: {
receptor_chains: {
type: 'string'
}
},
additionalProperties: false
},
'distance restraints': {
type: 'object',
properties: {
restraints: {
type: 'string'
}
},
additionalProperties: false
}
},
additionalProperties: false
}
expect(groupedSchema).toEqual(expectedSchema)
})
})

describe('unGroupParameters()', () => {
it('should work', () => {
const groupedParameters = {
restraints: {
receptor_chains: 'val1'
},
'distance restraints': {
restraints: 'val2'
}
}

const actual = unGroupParameters(groupedParameters, {
receptor_chains: {
'ui:group': 'restraints'
},
restraints: {
'ui:group': 'distance restraints'
}
})

const expectedParameters = {
receptor_chains: 'val1',
restraints: 'val2'
}
expect(actual).toEqual(expectedParameters)
})
})
})

describe('given a prop with same name as group and another prop in same group', () => {
describe('groupSchema()', () => {
it('should move property inside object with same name', () => {
Expand Down
57 changes: 39 additions & 18 deletions packages/core/src/grouper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,49 +29,70 @@ export function groupSchema (
schema: JSONSchema7,
uiSchema: UiSchema
): JSONSchema7 {
const newSchema = JSON.parse(JSON.stringify(schema))
const newSchema = structuredClone(schema)

// Handle overlap between groups and direct prop names.
const definedGroups = new Set(
Object.values(uiSchema)
.filter((v) => 'ui:group' in v)
.map((v) => v['ui:group'])
)
const directProps = schema.properties ?? {}
const directPropNamesWithSameNameAsGroup = new Set(
Object.keys(directProps).filter((k) => definedGroups.has(k))
const propsWithGroup = new Set(
Object.keys(uiSchema).filter((k) => 'ui:group' in uiSchema[k])
)
for (const k of directPropNamesWithSameNameAsGroup) {
const propHasGroup =
k in uiSchema && 'ui:group' in uiSchema[k] && 'ui:group' in uiSchema[k]
if (!(propHasGroup && uiSchema[k]['ui:group'] === k)) {
throw new Error(
`Can not have group and un-grouped parameter with same name ${k}`
)
}
// Prop has same name as its group so nest it
const v = newSchema.properties[k]
newSchema.properties[k] = {
const allProps = new Set(Object.keys(schema.properties ?? {}))
const grouplessProps = new Set([...allProps].filter((x) => !propsWithGroup.has(x)))
const grouplessPropsWithSameNameAsGroup = new Set(
[...grouplessProps].filter((x) => definedGroups.has(x))
)
// direct prop with same name as group throw error
if (grouplessPropsWithSameNameAsGroup.size > 0) {
const msg = Array.from(grouplessPropsWithSameNameAsGroup).join(', ')
throw new Error(
`Can not have group and un-grouped parameter with same name ${msg}`
)
}

if (!('properties' in newSchema && typeof newSchema.properties === 'object')) {
return newSchema
}

// prop with group and same name as any group should be nested first
const propsWithSameNameAsAnyGroup = new Set(Object.entries(uiSchema).filter(([k, v]) => 'ui:group' in v && definedGroups.has(k)).map((d) => d[0]))
for (const k of propsWithSameNameAsAnyGroup) {
const prop = newSchema.properties[k]
/* eslint-disable @typescript-eslint/no-dynamic-delete */
delete newSchema.properties[k]
/* eslint-enable @typescript-eslint/no-dynamic-delete */
const group = uiSchema[k]['ui:group']
newSchema.properties[group] = {
type: 'object',
properties: {
[k]: v
[k]: prop
},
additionalProperties: false
}
}

Object.entries(uiSchema).forEach(([k, v]) => {
// TODO recursivly, now only loops over first direct props
if ('ui:group' in v && !directPropNamesWithSameNameAsGroup.has(k)) {
if ('ui:group' in v && !propsWithSameNameAsAnyGroup.has(k)) {
const group = v['ui:group']
if (!('properties' in newSchema && typeof newSchema.properties === 'object')) {
throw new Error('Schema must have properties')
}
if (!(group in newSchema.properties)) {
newSchema.properties[group] = {
type: 'object',
properties: {},
additionalProperties: false
}
}
newSchema.properties[group].properties[k] = newSchema.properties[k]
const newGroup = newSchema.properties[group]
if (typeof newGroup === 'boolean' || newGroup.properties === undefined) {
return
}
newGroup.properties[k] = newSchema.properties[k]
// Remove k as it now is in the group
/* eslint-disable @typescript-eslint/no-dynamic-delete */
delete newSchema.properties[k]
Expand Down
24 changes: 23 additions & 1 deletion packages/core/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,27 @@ export function useSelectedNodeFormSchema (): JSONSchema7 | undefined {
return useRecoilValue(selectedNodeFormSchemaState)
}

function expandSingleCollapsibleField (uiSchema: UiSchema): UiSchema {
const nrCollapsibles = Object.values(uiSchema).filter(
(v) => v['ui:field'] === 'collapsible'
).length
if (nrCollapsibles === 1) {
const firstCollapsibleKey = Object.keys(uiSchema).find(
(k) => uiSchema[k]['ui:field'] === 'collapsible'
)
if (firstCollapsibleKey !== undefined) {
return {
...uiSchema,
[firstCollapsibleKey]: {
...uiSchema[firstCollapsibleKey],
'ui:collapsed': false
}
}
}
}
return uiSchema
}

const selectedNodeFormUiSchemaState = selector<UiSchema | undefined>({
key: 'selectedNodeFormUiSchema',
get: ({ get }) => {
Expand All @@ -475,7 +496,8 @@ const selectedNodeFormUiSchemaState = selector<UiSchema | undefined>({
moleculeInfos,
moleculesPropName
)
return uiSchemaWithMolInfo

return expandSingleCollapsibleField(uiSchemaWithMolInfo)
}
})

Expand Down
3 changes: 2 additions & 1 deletion packages/form/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,14 @@
"@storybook/react": "^6.5.9",
"@storybook/testing-library": "^0.0.13",
"@testing-library/react": "12",
"@vitest/coverage-v8": "^1.5.3",
"babel-loader": "^8.2.5",
"jsdom": "^20.0.0",
"postcss": "^8.4.14",
"ts-standard": "^12.0.2",
"tsup": "^6.1.3",
"typedoc": "^0.23.7",
"vitest": "^0.20.2"
"vitest": "^1.5.3"
},
"tsup": {
"entry": [
Expand Down
6 changes: 5 additions & 1 deletion packages/haddock3_catalog/generate_haddock3_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,11 +430,15 @@ def process_level(level_fn: Path, level: str):

broken_modules = {
'topocg', # Gives `AttributeError: module 'haddock.modules.topology.topocg' has no attribute 'HaddockModule'` error
'exit', # Does not make sense to have exit module in the catalog
}
# TODO define module order like category order
# now they are sorted alphabetically
# now they are sorted by insert
nodes = [process_module(module, category, level) for module, category in sorted(modules_category.items()) if module not in broken_modules]

# remove catagories without nodes
categories = [c for c in categories if any(n['category'] == c['name'] for n in nodes)]

catalog = {
"title": f"Haddock 3 on {level} level",
"categories": categories,
Expand Down
Loading

0 comments on commit 549bde0

Please sign in to comment.