Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
5e92876
feat(schema-config): add atomic schema import endpoint
grantfitzsimmons Aug 25, 2026
e5c70d7
feat(schema-config): add schema import controls
grantfitzsimmons Aug 25, 2026
4c1fe2b
fix(schema-config): tolerate legacy import values
grantfitzsimmons Aug 25, 2026
29a7568
fix(schema-config): build localized import parents
grantfitzsimmons Aug 25, 2026
31afb51
fix(schema-config): validate import file structure
grantfitzsimmons Aug 25, 2026
714f512
refactor(schema-config): reuse localized schema config label
grantfitzsimmons Aug 26, 2026
527de3f
fix(schema-config): skip unavailable import references
grantfitzsimmons Aug 26, 2026
1c608a0
fix(schema-config): validate import language
grantfitzsimmons Aug 26, 2026
5279a97
Lint code with ESLint and Prettier
grantfitzsimmons Aug 26, 2026
4a65bd4
fix(schema-config): contain invalid import errors
grantfitzsimmons Aug 26, 2026
c9f5de4
Merge branch 'issue-6155-2' of https://github.com/specify/specify7 in…
grantfitzsimmons Aug 26, 2026
41379d2
fix(schema-config): separate import warnings
grantfitzsimmons Aug 26, 2026
ac5c556
fix(schema-config): finalize text
grantfitzsimmons Aug 26, 2026
56be230
Lint code with ESLint and Prettier
grantfitzsimmons Aug 26, 2026
00f4b02
fix schema localization import review comments
grantfitzsimmons Aug 26, 2026
abb176a
Merge remote-tracking branch 'origin/main' into issue-6155-2
Copilot Sep 2, 2026
f69ccaa
Lint code with ESLint and Prettier
Copilot Sep 2, 2026
e9ccbfc
Fix: Normalize locale keys before the lookup
CarolineDenis Sep 7, 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
175 changes: 175 additions & 0 deletions specifyweb/backend/context/tests/test_schema_localization_import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import json

from django.test import Client

from specifyweb.specify import models
from specifyweb.specify.tests.test_api import ApiTests


class SchemaLocalizationImportTests(ApiTests):
def setUp(self):
super().setUp()
self.container = models.Splocalecontainer.objects.create(
discipline=self.discipline, name='Accession', schematype=0
)
self.item = models.Splocalecontaineritem.objects.create(
container=self.container, name='accessionnumber'
)
self.client = Client()
self.client.force_login(self.specifyuser)
self.client.cookies['collection'] = str(self.collection.id)

def test_imports_schema_values_and_skips_unknown_entries(self):
response = self.client.post(
'/context/schema_localization_import.json',
data=json.dumps({
'language': 'en',
'schema': {
'accession': {
'format': 'Accession',
'name': 'Imported Accession',
'items': {
'accessionnumber': {
'isHidden': True,
'name': 'Imported Number',
'pickListName': 'Unavailable Picklist',
'webLinkName': 'Unavailable Web Link',
},
'removedfield': {'isHidden': True},
},
},
'removedtable': {'isHidden': True},
},
}),
content_type='application/json',
)

self.assertEqual(response.status_code, 200)
self.container.refresh_from_db()
self.item.refresh_from_db()
self.assertEqual(self.container.format, 'Accession')
self.assertTrue(self.item.ishidden)
self.assertIsNone(self.item.picklistname)
self.assertIsNone(self.item.weblinkname)
self.assertEqual(
models.Splocaleitemstr.objects.get(
containername=self.container, language='en'
).text,
'Imported Accession',
)
self.assertEqual(
models.Splocaleitemstr.objects.get(
itemname=self.item, language='en'
).text,
'Imported Number',
)

def test_import_updates_existing_countryless_string(self):
string = models.Splocaleitemstr.objects.create(
containername=self.container,
language='en',
country='',
text='Existing Accession',
)

response = self.client.post(
'/context/schema_localization_import.json',
data=json.dumps({
'language': 'en',
'schema': {'accession': {'name': 'Updated Accession'}},
}),
content_type='application/json',
)

self.assertEqual(response.status_code, 200)
self.assertEqual(
models.Splocaleitemstr.objects.filter(
containername=self.container, language='en'
).count(),
1,
)
string.refresh_from_db()
self.assertEqual(string.text, 'Updated Accession')

def test_import_updates_existing_case_insensitive_country_string(self):
string = models.Splocaleitemstr.objects.create(
containername=self.container,
language='en',
country='US',
text='Existing US Accession',
)

response = self.client.post(
'/context/schema_localization_import.json',
data=json.dumps({
'language': 'en-US',
'schema': {'accession': {'name': 'Updated US Accession'}},
}),
content_type='application/json',
)

self.assertEqual(response.status_code, 200)
self.assertEqual(
models.Splocaleitemstr.objects.filter(
containername=self.container, language='en'
).count(),
1,
)
string.refresh_from_db()
self.assertEqual(string.text, 'Updated US Accession')

def test_invalid_values_do_not_write(self):
response = self.client.post(
'/context/schema_localization_import.json',
data=json.dumps({
'schema': {'accession': {'isHidden': 'yes'}},
'language': 'en',
}),
content_type='application/json',
)

self.assertEqual(response.status_code, 400)
self.container.refresh_from_db()
self.assertIsNone(self.container.format)

def test_rejects_non_schema_json(self):
response = self.client.post(
'/context/schema_localization_import.json',
data=json.dumps({'not': 'a schema'}),
content_type='application/json',
)

self.assertEqual(response.status_code, 400)
self.container.refresh_from_db()
self.assertIsNone(self.container.format)

def test_rejects_invalid_language(self):
for language in ('en-us-extra', '@@', 'en-$%'):
with self.subTest(language=language):
response = self.client.post(
'/context/schema_localization_import.json',
data=json.dumps({
'language': language,
'schema': {'accession': {'name': 'Should Not Import'}},
}),
content_type='application/json',
)

self.assertEqual(response.status_code, 400)
self.assertFalse(
models.Splocaleitemstr.objects.filter(
containername=self.container, text='Should Not Import'
).exists()
)

def test_rejects_non_object_data_for_known_table(self):
response = self.client.post(
'/context/schema_localization_import.json',
data=json.dumps({
'language': 'en',
'schema': {'accession': 'invalid'},
}),
content_type='application/json',
)

self.assertEqual(response.status_code, 400)
2 changes: 1 addition & 1 deletion specifyweb/backend/context/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Defines the urls for the app context subsystem
"""

from django.urls import path
from django.urls import path

from . import views, user_resources, collection_resources
Expand Down Expand Up @@ -30,6 +29,7 @@
path('viewsets.json', views.viewsets),
path('datamodel.json', views.datamodel),
path('schema_localization.json', views.schema_localization),
path('schema_localization_import.json', views.schema_localization_import),
path('app.resource', views.app_resource),
path('available_related_searches.json', views.available_related_searches),
path('remoteprefs.properties', views.remote_prefs),
Expand Down
Loading
Loading