Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
6df778e
init api for adding and deleting tree ranks
acwhite211 Dec 5, 2023
d17e266
init unit test for adding/deleting ranks
acwhite211 Dec 7, 2023
fc664ad
unit test fixing and api parameter addition
acwhite211 Dec 8, 2023
5125989
fix more unit tests
acwhite211 Dec 9, 2023
d40fb84
fix delete rank unit test
acwhite211 Dec 12, 2023
c08cbb8
fix call to set_fullnames
acwhite211 Dec 12, 2023
62e902e
add edit_ranks permission
acwhite211 Dec 12, 2023
e4b9dbb
fix openapi schema
acwhite211 Dec 13, 2023
024efa0
reconfigure HttpResponseBadRequest responses
acwhite211 Dec 13, 2023
d031ff5
change wrapper for rank edit functions
acwhite211 Dec 15, 2023
2dc4ba8
add fullname tests
acwhite211 Jan 2, 2024
0c6b825
Merge remote-tracking branch 'origin/production' into add_delete_tree…
CarolineDenis Mar 15, 2024
1ff3385
get tree by name or id
acwhite211 Mar 18, 2024
19bc954
Add a button to add new ranks in trees
CarolineDenis Mar 18, 2024
77223ec
add test of parents of child nodes
acwhite211 Mar 18, 2024
2625e45
Remove code
CarolineDenis Mar 18, 2024
0424b56
Merge branch 'add_delete_tree_rank_api' of https://github.com/specify…
acwhite211 Mar 18, 2024
67c61f6
add treeName to api schema
acwhite211 Mar 18, 2024
051af5c
retract required fields
acwhite211 Mar 18, 2024
b171bc8
Change variable name
CarolineDenis Mar 19, 2024
2b73d71
Lint code with ESLint and Prettier
CarolineDenis Mar 19, 2024
b70bbe0
add extra params to add_tree_rank api
acwhite211 Mar 19, 2024
24cb1db
Merge branch 'add_delete_tree_rank_api' of https://github.com/specify…
acwhite211 Mar 19, 2024
79fe45f
add business rules for adding/deleting trees
acwhite211 Mar 19, 2024
46cd29c
Refactor add rank feature
CarolineDenis Mar 20, 2024
a6c98e3
Revert unecessary cahnges
CarolineDenis Mar 20, 2024
7ec54c6
Use tree id
CarolineDenis Mar 20, 2024
1d6a189
Remove tree name
CarolineDenis Mar 20, 2024
f82eaaa
add tree def dependent fields to help deletion blocker
acwhite211 Mar 20, 2024
b2940ad
Merge branch 'add_delete_tree_rank_api' of https://github.com/specify…
acwhite211 Mar 20, 2024
f5173a6
treedefitems typo fix
acwhite211 Mar 20, 2024
77e53ae
Code improvement, deletion of previous logic for treeDefItems
CarolineDenis Mar 21, 2024
a045b1d
Lint code with ESLint and Prettier
CarolineDenis Mar 21, 2024
947d9d2
Remove addRank function
CarolineDenis Mar 21, 2024
62fbee2
fix tree dependent fields
acwhite211 Mar 21, 2024
0258dab
filter_rank_deletion_exception
acwhite211 Mar 21, 2024
29cbb77
fix delete_blockers filter
acwhite211 Mar 21, 2024
6693ab9
tree ranks bussiness rules init
acwhite211 Mar 21, 2024
e696d4e
raise exception for parent id not given
acwhite211 Mar 21, 2024
bd35e0f
test assign parent to treeResource
CarolineDenis Mar 21, 2024
cf67f55
Lint code with ESLint and Prettier
CarolineDenis Mar 21, 2024
907da38
Set parent on continue
CarolineDenis Mar 21, 2024
97139a2
Lint code with ESLint and Prettier
CarolineDenis Mar 21, 2024
5fcf3b4
param and exception fixes
acwhite211 Mar 21, 2024
173eeae
Assign parent to new rank with parent id
CarolineDenis Mar 22, 2024
f8c45a4
Remove import
CarolineDenis Mar 22, 2024
6d2c8a1
Lint code with ESLint and Prettier
CarolineDenis Mar 22, 2024
60977fa
Erase console log
CarolineDenis Mar 22, 2024
542b3fd
fix rankid error
acwhite211 Mar 22, 2024
1191f92
working tree rank tests using resource api
acwhite211 Mar 22, 2024
8eb677f
Simplify code and check for permissions
CarolineDenis Mar 25, 2024
ef98956
Lint code with ESLint and Prettier
CarolineDenis Mar 25, 2024
6a658f7
code cleanup, remove old view funciton
acwhite211 Mar 25, 2024
9538ec5
remove old urls
acwhite211 Mar 25, 2024
d64c7d0
revert change to the simple api unit test
acwhite211 Mar 25, 2024
d5b5f6c
fix delete with avant_delete()
acwhite211 Mar 25, 2024
d189e7f
cleanup old tree_views code
acwhite211 Mar 25, 2024
1ed635c
use is_instance_of_tree_def_item in tree_rules
acwhite211 Mar 25, 2024
691e798
fix treedef error
acwhite211 Mar 25, 2024
d549285
add_tree_rank description update
acwhite211 Mar 25, 2024
21a2361
naming change
CarolineDenis Mar 26, 2024
817b5bf
Lint code with ESLint and Prettier
CarolineDenis Mar 26, 2024
7ba0ac2
add orm_signal_handler_with_kwargs
acwhite211 Mar 26, 2024
4dbf9b6
Reload after rank deletion
CarolineDenis Mar 26, 2024
45eb9a8
Add root rank as default to add new rank
CarolineDenis Mar 26, 2024
a25fbd3
set_rank_id code cleanup
acwhite211 Mar 26, 2024
82d801f
fix workbench unit test exception
acwhite211 Mar 26, 2024
c41707c
simplify set_rank_id function params
acwhite211 Mar 26, 2024
c0559c0
pass treeDef to rank resource api call
acwhite211 Mar 27, 2024
05a8592
require treedef before adding rank
acwhite211 Mar 27, 2024
171562f
Add form to add rank componenent
CarolineDenis Mar 29, 2024
3a548c3
Remove prop
CarolineDenis Mar 29, 2024
a822430
abstract orm_signal_handler
acwhite211 Apr 1, 2024
ac04d0a
unify orm_signal_handler
acwhite211 Apr 1, 2024
3d0cac8
Merge branch 'add_delete_tree_rank_api' of https://github.com/specify…
acwhite211 Apr 1, 2024
816337a
tree_extras models import change
acwhite211 Apr 1, 2024
13a0d48
pre_constraints_delete
acwhite211 Apr 1, 2024
5906c01
verify_rank_parent_chain_integretity
acwhite211 Apr 2, 2024
d529551
typo fix
acwhite211 Apr 2, 2024
0a47ccf
Merge remote-tracking branch 'origin/production' into add_delete_tree…
CarolineDenis May 7, 2024
175144c
check rank item count
acwhite211 May 13, 2024
3d3a7f6
add to url.py file
acwhite211 May 15, 2024
1d76fa9
Display Delete Blocker when delete used rank
CarolineDenis May 15, 2024
83a9dd8
Define ranks in lower case
CarolineDenis May 16, 2024
e5e850b
Simplify parent child rank
CarolineDenis May 16, 2024
15a49ac
Remove unecessary code
CarolineDenis May 16, 2024
36585f4
Fix exclude id
CarolineDenis May 16, 2024
db81246
Remove check fro str
CarolineDenis May 16, 2024
e482c7c
Remove unecessary treedef
CarolineDenis May 16, 2024
c1c56b8
Remove unecessary code
CarolineDenis May 16, 2024
6b751a3
Remove unecessary default since expection raised above
CarolineDenis May 16, 2024
de41d27
Raise warning when treedef none
CarolineDenis May 16, 2024
962be5d
Chnge string
CarolineDenis May 16, 2024
381d30e
Fix if statement
CarolineDenis May 16, 2024
cd8ea33
Remove unecessary check
CarolineDenis May 16, 2024
59b88ea
Remove checks
CarolineDenis May 16, 2024
f8f9734
Remove unecessary warning raise
CarolineDenis May 16, 2024
4a3b8b8
Raise expection when rank ids none
CarolineDenis May 16, 2024
e4f8f2d
Remove warning
CarolineDenis May 16, 2024
c0d86e8
remove unused code
acwhite211 May 17, 2024
05c5e73
simplify tree rank code
acwhite211 May 17, 2024
005b271
remove leftover code
acwhite211 May 20, 2024
605e2b6
Merge branch 'production' into add_delete_tree_rank_api
acwhite211 May 20, 2024
9880e02
Lint code with ESLint and Prettier
acwhite211 May 20, 2024
20e66ac
create abstract TreeRank model for save and delete logic override
acwhite211 May 21, 2024
fb53e79
Merge branch 'production' into add_delete_tree_rank_api
acwhite211 May 21, 2024
87182f0
Merge remote-tracking branch 'origin/production' into add_delete_tree…
CarolineDenis May 21, 2024
cb7b862
Lint code with ESLint and Prettier
CarolineDenis May 21, 2024
86d1729
Merge branch 'production' into add_delete_tree_rank_api
acwhite211 May 22, 2024
3a92011
Merge branch 'production' into add_delete_tree_rank_api
acwhite211 May 22, 2024
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
12 changes: 11 additions & 1 deletion specifyweb/businessrules/orm_signal_handler.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from inspect import getfullargspec
from typing import Callable, Literal, Optional, Hashable

from django.db.models import signals
Expand Down Expand Up @@ -26,7 +27,16 @@ def handler(sender, **kwargs):
def handler(sender, **kwargs):
if kwargs.get('raw', False):
return
rule(sender, kwargs['instance'])

instance = kwargs['instance']
created = kwargs.get('created', None)
argspec = getfullargspec(rule)
rule_has_created = 'created' in argspec.args

if created is not None and rule_has_created:
rule(sender, instance, created)
else:
rule(sender, instance)

return receiver(getattr(signals, signal), **receiver_kwargs)(handler)
return _dec
Expand Down
26 changes: 25 additions & 1 deletion specifyweb/businessrules/rules/tree_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,28 @@

from specifyweb.businessrules.orm_signal_handler import orm_signal_handler
from specifyweb.businessrules.exceptions import TreeBusinessRuleException
from specifyweb.specify.tree_extras import is_instance_of_tree_def_item
from specifyweb.specify.tree_ranks import *

logger = logging.getLogger(__name__)

@orm_signal_handler('pre_save')
def pre_tree_rank_initiation_handler(sender, obj):
if is_instance_of_tree_def_item(obj) and obj.pk is None: # is it a treedefitem?
if obj.pk is None: # is it a new object?
pre_tree_rank_init(obj)
verify_rank_parent_chain_integrity(obj, RankOperation.CREATED)
else:
verify_rank_parent_chain_integrity(obj, RankOperation.UPDATED)

@orm_signal_handler('post_save')
def post_tree_rank_initiation_handler(sender, obj, created):
if is_instance_of_tree_def_item(obj) and created: # is it a treedefitem?
post_tree_rank_save(sender, obj)

@orm_signal_handler('pre_delete')
def cannot_delete_root_treedefitem(sender, obj):
if hasattr(obj, 'treedef'): # is it a treedefitem?
if is_instance_of_tree_def_item(obj): # is it a treedefitem?
if sender.objects.get(id=obj.id).parent is None:
raise TreeBusinessRuleException(
"cannot delete root level tree definition item",
Expand All @@ -17,9 +32,18 @@ def cannot_delete_root_treedefitem(sender, obj):
"node": {
"id": obj.id
}})
pre_tree_rank_deletion(sender, obj)
verify_rank_parent_chain_integrity(obj, RankOperation.DELETED)

@orm_signal_handler('post_delete')
def post_tree_rank_deletion_handler(sender, obj):
if is_instance_of_tree_def_item(obj): # is it a treedefitem?
post_tree_rank_deletion(obj)

@orm_signal_handler('pre_save')
def set_is_accepted_if_prefereed(sender, obj):
if hasattr(obj, 'isaccepted'):
obj.isaccepted = obj.accepted_id == None



Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
*/

import type { IR, RA, RR } from '../../utils/types';
import { filterArray } from '../../utils/types';
import { getField } from './helpers';
import type { FilterTablesByEndsWith, TableFields } from './helperTypes';
import type { TableFields } from './helperTypes';
import { schema } from './schema';
import { LiteralField, Relationship } from './specifyField';
import type { SpecifyTable } from './specifyTable';
Expand All @@ -25,30 +24,6 @@ export const schemaAliases: RR<'', IR<string>> & {
},
};

const treeDefinitionFields = [
'fullNameSeparator',
'isEnforced',
'isInFullName',
'textAfter',
'textBefore',
];

const treeDefItem = (
table: SpecifyTable<FilterTablesByEndsWith<'TreeDefItem'>>
) =>
[
[],
(): void =>
filterArray(
treeDefinitionFields.map((fieldName) =>
table.getLiteralField(fieldName)
)
).forEach((field) => {
field.isReadOnly = true;
field.overrides.isReadOnly = true;
}),
] as const;

export const schemaExtras: {
readonly [TABLE_NAME in keyof Tables]?: (
table: SpecifyTable<Tables[TABLE_NAME]>
Expand Down Expand Up @@ -384,9 +359,4 @@ export const schemaExtras: {
}),
],
],
GeographyTreeDefItem: treeDefItem,
StorageTreeDefItem: treeDefItem,
TaxonTreeDefItem: treeDefItem,
GeologicTimePeriodTreeDefItem: treeDefItem,
LithoStratTreeDefItem: treeDefItem,
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ import type { FormType } from '../FormParse';
import type { SubViewSortField } from '../FormParse/cells';
import { augmentMode, ResourceView } from '../Forms/ResourceView';
import { useFirstFocus } from '../Forms/SpecifyForm';
import {
interactionPrepTables,
InteractionWithPreps,
} from '../Interactions/helpers';
import type { InteractionWithPreps } from '../Interactions/helpers';
import { interactionPrepTables } from '../Interactions/helpers';
import { InteractionDialog } from '../Interactions/InteractionDialog';
import { hasTablePermission } from '../Permissions/helpers';
import { relationshipIsToMany } from '../WbPlanView/mappingHelpers';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2371,6 +2371,9 @@ exports[`getMainTableFields 1`] = `
],
"GeographyTreeDefItem": [
"name",
"fullNameSeparator",
"textAfter",
"textBefore",
"title",
],
"GeologicTimePeriod": [
Expand All @@ -2382,6 +2385,9 @@ exports[`getMainTableFields 1`] = `
],
"GeologicTimePeriodTreeDefItem": [
"name",
"fullNameSeparator",
"textAfter",
"textBefore",
"title",
],
"Gift": [
Expand Down Expand Up @@ -2434,6 +2440,9 @@ exports[`getMainTableFields 1`] = `
],
"LithoStratTreeDefItem": [
"name",
"fullNameSeparator",
"textAfter",
"textBefore",
"title",
],
"Loan": [
Expand Down Expand Up @@ -2645,6 +2654,9 @@ exports[`getMainTableFields 1`] = `
],
"StorageTreeDefItem": [
"name",
"fullNameSeparator",
"textAfter",
"textBefore",
"title",
],
"Taxon": [
Expand All @@ -2665,6 +2677,9 @@ exports[`getMainTableFields 1`] = `
],
"TaxonTreeDefItem": [
"name",
"fullNameSeparator",
"textAfter",
"textBefore",
"title",
],
"TreatmentEvent": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,11 @@ import { propsToFormMode } from './useViewDefinition';
*/
export const FORBID_ADDING = new Set<keyof Tables>([
'TaxonTreeDef',
'TaxonTreeDefItem',
'GeographyTreeDef',
'GeographyTreeDefItem',
'StorageTreeDef',
'StorageTreeDefItem',
'GeologicTimePeriodTreeDef',
'GeologicTimePeriodTreeDefItem',
'LithoStratTreeDef',
'LithoStratTreeDefItem',
'Institution',
'Division',
'Discipline',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import { Button } from '../Atoms/Button';
import { Link } from '../Atoms/Link';
import { LoadingContext, ReadOnlyContext } from '../Core/Contexts';
import type {
AnySchema,
AnyInteractionPreparation,
AnySchema,
SerializedResource,
} from '../DataModel/helperTypes';
import type { SpecifyResource } from '../DataModel/legacyTypes';
Expand Down
106 changes: 106 additions & 0 deletions specifyweb/frontend/js_src/lib/components/TreeView/AddRank.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React from 'react';

import { commonText } from '../../localization/common';
import { interactionsText } from '../../localization/interactions';
import { treeText } from '../../localization/tree';
import type { RA } from '../../utils/types';
import { Button } from '../Atoms/Button';
import { Form, Label, Select } from '../Atoms/Form';
import type {
FilterTablesByEndsWith,
SerializedResource,
} from '../DataModel/helperTypes';
import { tables } from '../DataModel/tables';
import { ResourceView } from '../Forms/ResourceView';
import { Dialog } from '../Molecules/Dialog';
import { Submit } from '../Atoms/Submit';
import { useId } from '../../hooks/useId';

export function AddRank({
treeDefinitionItems,
}: {
readonly treeDefinitionItems: RA<
SerializedResource<FilterTablesByEndsWith<'TreeDefItem'>>
>;
}): JSX.Element {
const [state, setState] = React.useState<'add' | 'initial' | 'parent'>(
'initial'
);

const [parentRank, setParentRank] = React.useState(
treeDefinitionItems[0].resource_uri
);

const treeDef = treeDefinitionItems[0].treeDef;

const treeResource = React.useMemo(() => {
const resource = new tables[treeDefinitionItems[0]._tableName].Resource();
resource.set('treeDef', treeDef);
return resource;
}, [treeDef]);
const id = useId('add-rank');

return (
<>
<Button.Icon
icon="plus"
title={treeText.addNewRank()}
onClick={() => setState('parent')}
/>
{state === 'parent' && (
<Dialog
buttons={
<>
<Button.DialogClose>{commonText.cancel()}</Button.DialogClose>
<Submit.Save
onClick={() => {
treeResource.set('parent', parentRank);
setState('add');
}}
form={id('form')}
>
{interactionsText.continue()}
</Submit.Save>
</>
}
header={treeText.chooseParentRank()}
onClose={() => setState('initial')}
>
<Form id={id('form')}>
<Label.Block className="gap-2">
{treeText.chooseParentRank()}
<Select
className="w-full min-w-[theme(spacing.40)]"
value={parentRank}
onChange={({ target }): void => {
setParentRank(target.value);
}}
>
{treeDefinitionItems.map((rank, index) => (
<option key={index} value={rank.resource_uri}>
{rank.name}
</option>
))}
</Select>
</Label.Block>
</Form>
</Dialog>
)}
{state === 'add' && (
<ResourceView
dialog="modal"
isDependent={false}
isSubForm={false}
resource={treeResource}
title={treeText.addNewRank()}
onAdd={undefined}
onClose={(): void => setState('initial')}
onDeleted={undefined}
onSaved={() => {
globalThis.location.reload();
}}
/>
)}
</>
);
}
29 changes: 20 additions & 9 deletions specifyweb/frontend/js_src/lib/components/TreeView/Tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ import type {
import { deserializeResource } from '../DataModel/serializers';
import { ResourceView } from '../Forms/ResourceView';
import { getPref } from '../InitialContext/remotePrefs';
import { hasTablePermission } from '../Permissions/helpers';
import { useHighContrast } from '../Preferences/Hooks';
import { userPreferences } from '../Preferences/userPreferences';
import { AddRank } from './AddRank';
import type { Conformations, Row, Stats } from './helpers';
import { fetchStats } from './helpers';
import { TreeRow } from './Row';
Expand Down Expand Up @@ -47,7 +49,7 @@ export function Tree<SCHEMA extends AnyTree>({
searchBoxRef,
baseUrl,
setLastFocusedTree,
handleToggleEditingRanks,
onToggleEditingRanks: handleToggleEditingRanks,
}: {
readonly treeDefinitionItems: RA<
SerializedResource<FilterTablesByEndsWith<'TreeDefItem'>>
Expand All @@ -66,7 +68,7 @@ export function Tree<SCHEMA extends AnyTree>({
readonly searchBoxRef: React.RefObject<HTMLInputElement | null>;
readonly baseUrl: string;
readonly setLastFocusedTree: () => void;
readonly handleToggleEditingRanks: () => void;
readonly onToggleEditingRanks: () => void;
}): JSX.Element {
const highContrast = useHighContrast();

Expand Down Expand Up @@ -151,12 +153,21 @@ export function Tree<SCHEMA extends AnyTree>({
role="columnheader"
>
{index === 0 ? (
<Button.Icon
aria-pressed={isEditingRanks}
icon="pencil"
title={treeText.editRanks()}
onClick={handleToggleEditingRanks}
/>
<>
<Button.Icon
aria-pressed={isEditingRanks}
icon="pencil"
title={treeText.editRanks()}
onClick={handleToggleEditingRanks}
/>
{isEditingRanks &&
hasTablePermission(
treeDefinitionItems[0]._tableName,
'create'
) ? (
<AddRank treeDefinitionItems={treeDefinitionItems} />
) : null}
</>
) : null}
<Button.LikeLink
id={id(rank.rankId.toString())}
Expand Down Expand Up @@ -257,7 +268,7 @@ function EditTreeRank({
resource={resource}
onAdd={undefined}
onClose={handleClose}
onDeleted={undefined}
onDeleted={(): void => globalThis.location.reload()}
onSaved={(): void => globalThis.location.reload()}
/>
) : null}
Expand Down
Loading