Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
db8a365
Remove root node
alesan99 Jan 14, 2026
5f25e95
Add rank field configuration to forms
alesan99 Jan 14, 2026
95f37f1
Add a new treeDefault import feature
CarolineDenis Apr 18, 2025
b466c7c
create_default_trees_task fix
acwhite211 Jun 12, 2025
d5ab598
Add label to progress bar
alesan99 Nov 13, 2025
f3c8ee9
Lint code with ESLint and Prettier
alesan99 Dec 17, 2025
7d1c295
fix tests
alesan99 Jan 13, 2026
4e13355
Update default files
alesan99 Jan 14, 2026
c5bc748
WIP create trees using user's rank configuration
alesan99 Jan 14, 2026
55ff4c6
Fix storage tree default file
alesan99 Jan 14, 2026
5e16af4
WIP Add default tree files for remaining trees
alesan99 Jan 14, 2026
0e3e27a
Fix default files
alesan99 Jan 14, 2026
c2fd769
Fix applying user rank configuration
alesan99 Jan 14, 2026
9c4a4fd
fix(trees): increase tectonicunit rankids
grantfitzsimmons Jan 14, 2026
f642a00
show province/state
grantfitzsimmons Jan 14, 2026
3571e00
WIP allow pre-loading trees
alesan99 Jan 14, 2026
9992dac
Create Geography tree on startup
alesan99 Jan 15, 2026
53a3029
Preload taxon tree according to discipline type
alesan99 Jan 15, 2026
870d699
Create empty default tree for new disciplines
alesan99 Jan 15, 2026
d1c711c
Remove root from new taxon trees
alesan99 Jan 15, 2026
683d6e9
Add button to import tree into an empty tree
alesan99 Jan 15, 2026
644e78c
fix tests
alesan99 Jan 15, 2026
8dede09
Don't create root if it already exists
alesan99 Jan 15, 2026
20e9a38
Fix: Chnage full name separator to be a space
CarolineDenis Jan 16, 2026
197d733
Remove pre-load taxon tree option
alesan99 Jan 19, 2026
14ea945
Fix: Update localization
alesan99 Jan 23, 2026
a689b4d
correct parent ranks when creating a new tree
alesan99 Jan 23, 2026
29e0916
Update tree defaults
alesan99 Jan 23, 2026
cdd1f42
Lint code with ESLint and Prettier
alesan99 Jan 23, 2026
aaf0ff1
Rename setup tool tree functions
alesan99 Jan 23, 2026
4cd9f20
Merge branch 'issue-7593' of https://github.com/specify/specify7 into…
alesan99 Jan 23, 2026
4b593a5
Update tree defaults
alesan99 Jan 23, 2026
a45eb04
Remove taxon tree configuration
alesan99 Jan 23, 2026
acc6f83
Lint code with ESLint and Prettier
alesan99 Jan 23, 2026
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
2 changes: 1 addition & 1 deletion config/common/geography_tree.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"geography_tree": {
"tree": {
"treedef": {
"levels": [
{
Expand Down
44 changes: 44 additions & 0 deletions config/common/geologictimeperiod_tree.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"tree": {
"treedef": {
"levels": [
{
"name": "Time Root",
"rank": 0,
"enforced": true,
"infullname": false,
"fullnameseparator": ", "
},
{
"name": "Erathem/Era",
"rank": 100,
"enforced": false,
"infullname": false,
"fullnameseparator": ", "
},
{
"name": "System/Period",
"rank": 200,
"enforced": false,
"infullname": false,
"fullnameseparator": ", "
},
{
"name": "Series/Epoch",
"rank": 300,
"enforced": false,
"infullname": true,
"fullnameseparator": ", "
},
{
"name": "Stage/Age",
"rank": 400,
"enforced": false,
"infullname": true,
"fullnameseparator": ", "
}
]
},
"nodes": []
}
}
45 changes: 45 additions & 0 deletions config/common/lithostrat_tree.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"tree": {
"treedef": {
"levels": [
{
"name": "Surface",
"enforced": true,
"infullname": false,
"rank": 0
},
{
"name": "Super Group",
"enforced": false,
"infullname": false,
"rank": 100
},
{
"name": "Litho Group",
"enforced": false,
"infullname": false,
"rank": 200
},
{
"name": "Formation",
"enforced": false,
"infullname": false,
"rank": 300
},
{
"name": "Member",
"enforced": false,
"infullname": false,
"rank": 400
},
{
"name": "Bed",
"enforced": false,
"infullname": false,
"rank": 500
}
]
},
"nodes": []
}
}
4 changes: 2 additions & 2 deletions config/common/storage_tree.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"storage_tree": {
"tree": {
"treedef": {
"levels": [
{
Expand Down Expand Up @@ -53,7 +53,7 @@
{
"name": "Rack",
"enforced": false,
"infullname": "Rack",
"infullname": false,
"rank": 450
},
{
Expand Down
45 changes: 45 additions & 0 deletions config/common/tectonicunit_tree.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"tree": {
"treedef": {
"levels": [
{
"name": "Root",
"enforced": true,
"infullname": false,
"rank": 0
},
{
"name": "Superstructure",
"enforced": false,
"infullname": false,
"rank": 100
},
{
"name": "Tectonic Domain",
"enforced": false,
"infullname": false,
"rank": 200
},
{
"name": "Tectonic Subdomain",
"enforced": false,
"infullname": false,
"rank": 300
},
{
"name": "Tectonic Unit",
"enforced": false,
"infullname": true,
"rank": 400
},
{
"name": "Tectonic Subunit",
"enforced": false,
"infullname": true,
"rank": 500
}
]
},
"nodes": []
}
}
23 changes: 13 additions & 10 deletions specifyweb/backend/setup_tool/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from specifyweb.backend.setup_tool.prep_type_defaults import create_default_prep_types
from specifyweb.backend.setup_tool.setup_tasks import setup_database_background, get_active_setup_task, get_last_setup_error, set_last_setup_error
from specifyweb.celery_tasks import MissingWorkerError
from specifyweb.backend.setup_tool.tree_defaults import create_default_tree, update_tree_scoping
from specifyweb.backend.setup_tool.tree_defaults import start_default_tree_from_configuration, update_tree_scoping
from specifyweb.specify.models import Institution, Discipline
from specifyweb.backend.businessrules.uniqueness_rules import apply_default_uniqueness_rules
from specifyweb.specify.management.commands.run_key_migration_functions import fix_cots
Expand Down Expand Up @@ -193,6 +193,8 @@ def create_discipline(data):
existing_discipline = Discipline.objects.filter(id=existing_id).first()
if existing_discipline:
return {"discipline_id": existing_discipline.id}

is_first_discipline = Discipline.objects.count() == 0

# Resolve division
division_url = data.get('division')
Expand All @@ -215,14 +217,13 @@ def create_discipline(data):
# Assign a taxon tree. Not required, but its eventually needed for collection object type.
taxontreedef_url = data.get('taxontreedef', None)
taxontreedef = resolve_uri_or_fallback(taxontreedef_url, None, Taxontreedef)
if taxontreedef is not None:
if taxontreedef_url and taxontreedef is not None:
data['taxontreedef_id'] = taxontreedef.id

data.update({
'datatype_id': datatype.id,
'geographytreedef_id': geographytreedef.id,
'geologictimeperiodtreedef_id': geologictimeperiodtreedef.id,
'taxontreedef_id': taxontreedef.id if taxontreedef else None
'geologictimeperiodtreedef_id': geologictimeperiodtreedef.id
})

# Assign new Discipline ID
Expand All @@ -247,6 +248,12 @@ def create_discipline(data):
update_tree_scoping(geographytreedef, new_discipline.id)
update_tree_scoping(geologictimeperiodtreedef, new_discipline.id)

# Create a default taxon tree if the database is already set up.
if not is_first_discipline:
create_taxon_tree({
'discipline_id': new_discipline.id
})

return {"discipline_id": new_discipline.id}

except Exception as e:
Expand Down Expand Up @@ -365,9 +372,6 @@ def create_tectonicunit_tree(data):
return create_tree('Tectonicunit', data)

def create_tree(name: str, data: dict) -> dict:
# TODO: Use trees/create_default_trees
# https://github.com/specify/specify7/pull/6429

# Figure out which scoping field should be used.
use_institution = False
use_discipline = True
Expand All @@ -393,8 +397,7 @@ def create_tree(name: str, data: dict) -> dict:
ranks = data.pop('ranks', dict())

# Pre-load Default Tree
# TODO: trees/create_default_trees
preload_tree = data.pop('default', None)
preload_tree = data.pop('preload', None)

try:
kwargs = {}
Expand All @@ -404,7 +407,7 @@ def create_tree(name: str, data: dict) -> dict:
if use_discipline and discipline is not None:
kwargs['discipline'] = discipline

treedef = create_default_tree(name, kwargs, ranks, preload_tree)
treedef = start_default_tree_from_configuration(name, kwargs, ranks, preload_tree)

# Set as the primary tree in the discipline if its the first one
if use_discipline and discipline:
Expand Down
55 changes: 34 additions & 21 deletions specifyweb/backend/setup_tool/setup_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from celery.result import AsyncResult
from specifyweb.backend.setup_tool import api
from specifyweb.backend.setup_tool.app_resource_defaults import create_app_resource_defaults
from specifyweb.backend.setup_tool.tree_defaults import start_preload_default_tree
from specifyweb.specify.management.commands.run_key_migration_functions import fix_schema_config
from specifyweb.specify.models_utils.model_extras import PALEO_DISCIPLINES, GEOLOGY_DISCIPLINES
from specifyweb.celery_tasks import is_worker_alive, MissingWorkerError
Expand Down Expand Up @@ -80,67 +81,79 @@ def update_progress():
logger.debug('## SETTING UP DATABASE WITH SETTINGS:##')
logger.debug(data)

logger.debug('Creating institution')
logger.info('Creating institution')
api.create_institution(data['institution'])
update_progress()

logger.debug('Creating storage tree')
logger.info('Creating storage tree')
api.create_storage_tree(data['storagetreedef'])
update_progress()

logger.debug('Creating division')
logger.info('Creating division')
api.create_division(data['division'])
update_progress()

discipline_type = data['discipline'].get('type', '')
is_paleo_geo = discipline_type in PALEO_DISCIPLINES or discipline_type in GEOLOGY_DISCIPLINES
default_tree = {
'fullnamedirection': 1,
'ranks': {
'0': True
}
'ranks': {}
}

# if is_paleo_geo:
# Create an empty chronostrat tree no matter what because discipline needs it.
logger.debug('Creating Chronostratigraphy tree')
logger.info('Creating Chronostratigraphy tree')
default_chronostrat_tree = default_tree.copy()
default_chronostrat_tree['fullnamedirection'] = -1
api.create_geologictimeperiod_tree(default_chronostrat_tree)
chronostrat_result = api.create_geologictimeperiod_tree(default_chronostrat_tree)
chronostrat_treedef_id = chronostrat_result.get('treedef_id')

logger.debug('Creating geography tree')
logger.info('Creating geography tree')
uses_global_geography_tree = data['institution'].get('issinglegeographytree', False)
api.create_geography_tree(data['geographytreedef'], global_tree=uses_global_geography_tree)
geography_result = api.create_geography_tree(data['geographytreedef'].copy(), global_tree=uses_global_geography_tree)
geography_treedef_id = geography_result.get('treedef_id')

logger.debug('Creating discipline')
logger.info('Creating discipline')
discipline_result = api.create_discipline(data['discipline'])
discipline_id = discipline_result.get('discipline_id')
default_tree['discipline_id'] = discipline_id
update_progress()

if is_paleo_geo:
logger.debug('Creating Lithostratigraphy tree')
logger.info('Creating Lithostratigraphy tree')
api.create_lithostrat_tree(default_tree.copy())

logger.debug('Creating Tectonic Unit tree')
logger.info('Creating Tectonic Unit tree')
api.create_tectonicunit_tree(default_tree.copy())

logger.debug('Creating taxon tree')
logger.info('Creating taxon tree')
if data['taxontreedef'].get('discipline_id') is None:
data['taxontreedef']['discipline_id'] = discipline_id
api.create_taxon_tree(data['taxontreedef'])
taxon_result = api.create_taxon_tree(data['taxontreedef'].copy())
taxon_treedef_id = taxon_result.get('treedef_id')
update_progress()

logger.debug('Creating collection')
api.create_collection(data['collection'])
logger.info('Creating collection')
collection_result = api.create_collection(data['collection'])
collection_id = collection_result.get('collection_id')
update_progress()

logger.debug('Creating specify user')
api.create_specifyuser(data['specifyuser'])
logger.info('Creating specify user')
specifyuser_result = api.create_specifyuser(data['specifyuser'])
specifyuser_id = specifyuser_result.get('user_id')

logger.debug('Finalizing database')
logger.info('Finalizing database')
fix_schema_config()
create_app_resource_defaults()

# Pre-load trees
logger.info('Starting default tree downloads')
if is_paleo_geo:
start_preload_default_tree('Geologictimeperiod', discipline_id, collection_id, chronostrat_treedef_id, specifyuser_id)
if data['geographytreedef'].get('preload'):
start_preload_default_tree('Geography', discipline_id, collection_id, geography_treedef_id, specifyuser_id)
if data['taxontreedef'].get('preload'):
start_preload_default_tree('Taxon', discipline_id, collection_id, taxon_treedef_id, specifyuser_id)

update_progress()
except Exception as e:
logger.exception(f'Error setting up database: {e}')
Expand Down
Loading