Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
61 changes: 61 additions & 0 deletions specifyweb/backend/context/tests/test_system_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import json
from unittest.mock import patch

from django.test import Client

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


class TestSystemInfo(ApiTests):
def test_system_info_during_guided_setup_returns_safe_payload(self):
c = Client()
c.force_login(self.specifyuser)
c.cookies['collection'] = str(self.collection.id)

with patch('specifyweb.backend.context.views.is_guided_setup_complete', return_value=False):
response = c.get('/context/system_info.json')

self._assertStatusCodeEqual(response, 200)
payload = json.loads(response.content.decode())

self.assertFalse(payload['setup_complete'])
self.assertIsNone(payload['database_version'])
self.assertIsNone(payload['schema_version'])
self.assertIsNone(payload['institution'])
self.assertIsNone(payload['institution_guid'])
self.assertIsNone(payload['discipline'])
self.assertIsNone(payload['collection'])
self.assertIsNone(payload['collection_guid'])
self.assertIsNone(payload['isa_number'])
self.assertIsNone(payload['discipline_type'])
self.assertIsNone(payload['geography_is_global'])
self.assertIn('version', payload)
self.assertIn('specify6_version', payload)

def test_system_info_after_guided_setup_returns_full_payload(self):
Spversion.objects.create(appversion='7', schemaversion='2.10')

c = Client()
c.force_login(self.specifyuser)
c.cookies['collection'] = str(self.collection.id)

with patch('specifyweb.backend.context.views.is_guided_setup_complete', return_value=True):
response = c.get('/context/system_info.json')

self._assertStatusCodeEqual(response, 200)
payload = json.loads(response.content.decode())

self.assertTrue(payload['setup_complete'])
self.assertEqual(payload['database_version'], '7')
self.assertEqual(payload['schema_version'], '2.10')
self.assertEqual(payload['institution'], self.institution.name)
self.assertEqual(payload['institution_guid'], self.institution.guid)
self.assertEqual(payload['discipline'], self.discipline.name)
self.assertEqual(payload['collection'], self.collection.collectionname)
self.assertEqual(payload['collection_guid'], self.collection.guid)
self.assertEqual(payload['isa_number'], self.collection.isanumber)
self.assertEqual(payload['discipline_type'], self.discipline.type)
self.assertEqual(
payload['geography_is_global'], self.institution.issinglegeographytree
)
57 changes: 42 additions & 15 deletions specifyweb/backend/context/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
from specifyweb.backend.setup_tool.api import (
get_config_progress,
filter_ready_disciplines_for_config_tasks,
is_collection_available
is_collection_available,
is_guided_setup_complete,
)

def set_collection_cookie(response, collection_id): # pragma: no cover
Expand Down Expand Up @@ -713,27 +714,53 @@ def _build_system_data(*, filter_not_ready_collections: bool):
@skip_collection_access_check
def system_info(request):
"Return various information about this Specify instance."
spversion = Spversion.objects.get()
collection = request.specify_collection
discipline = collection.discipline if collection is not None else None
institution = Institution.objects.get()
setup_complete = is_guided_setup_complete()

spversion = Spversion.objects.first()
collection = None
discipline = None
if setup_complete:
try:
collection = request.specify_collection
except Collection.DoesNotExist:
collection = None
else:
discipline = collection.discipline if collection is not None else None
institution = Institution.objects.first()

database_version = spversion.appversion if spversion is not None else None
schema_version = spversion.schemaversion if spversion is not None else None

institution_name = institution.name if institution is not None else None
institution_guid = institution.guid if institution is not None else None
geography_is_global = (
institution.issinglegeographytree if institution is not None else None
)

if not setup_complete:
database_version = None
schema_version = None
institution_name = None
institution_guid = None
geography_is_global = None

info = dict(
version=settings.VERSION,
specify6_version=re.findall(r'SPECIFY_VERSION=(.*)', specify_jar.read('resources_en.properties').decode('utf-8'))[0],
database_version=spversion.appversion,
schema_version=spversion.schemaversion,
setup_complete=setup_complete,
database_version=database_version,
schema_version=schema_version,
stats_url=settings.STATS_URL,
stats_2_url=settings.STATS_2_URL,
database=settings.DATABASE_NAME,
institution=institution.name,
institution_guid=institution.guid,
discipline=discipline and discipline.name,
collection=collection and collection.collectionname,
collection_guid=collection and collection.guid,
isa_number=collection and collection.isanumber,
discipline_type=discipline and discipline.type,
geography_is_global=institution.issinglegeographytree
institution=institution_name,
institution_guid=institution_guid,
discipline=discipline.name if setup_complete and discipline is not None else None,
collection=collection.collectionname if setup_complete and collection is not None else None,
collection_guid=collection.guid if setup_complete and collection is not None else None,
isa_number=collection.isanumber if setup_complete and collection is not None else None,
discipline_type=discipline.type if setup_complete and discipline is not None else None,
geography_is_global=geography_is_global,
)
return HttpResponse(json.dumps(info), content_type='application/json')

Expand Down
5 changes: 5 additions & 0 deletions specifyweb/backend/setup_tool/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ def get_setup_progress() -> dict:
def _setup_resources_complete(completed_resources: dict) -> bool:
return all(bool(resource_ready) for resource_ready in completed_resources.values())


def is_guided_setup_complete() -> bool:
"""Return whether all Guided Setup resources have been created."""
return _setup_resources_complete(get_setup_resource_progress())

def get_setup_resource_progress() -> dict:
"""Returns a dictionary of the status of database setup resources."""
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ import { Dialog } from '../Molecules/Dialog';
* For databases created in Specify 7 this check is currently unnessecary.
*/
export function VersionMismatch(): JSX.Element | null {
const systemInfo = getSystemInfo();
const [showVersionMismatch, setShowVersionMismatch] = React.useState(
getSystemInfo().specify6_version !== getSystemInfo().database_version &&
getSystemInfo().database_version !== '7'
systemInfo.setup_complete &&
systemInfo.database_version !== null &&
systemInfo.specify6_version !== systemInfo.database_version &&
systemInfo.database_version !== '7'
);
return showVersionMismatch ? (
<Dialog
Expand All @@ -29,8 +32,8 @@ export function VersionMismatch(): JSX.Element | null {
>
<p>
{mainText.versionMismatchDescription({
specifySixVersion: getSystemInfo().specify6_version,
databaseVersion: getSystemInfo().database_version,
specifySixVersion: systemInfo.specify6_version,
databaseVersion: systemInfo.database_version ?? '',
})}
</p>
<p>{mainText.versionMismatchSecondDescription()}</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ test('can fetch and parse system information', async () =>
database: 'specify',
database_version: '6.8.03',
discipline: 'Ichthyology',
discipline_type: 'fish',
geography_is_global: true,
institution: 'University of Kansas Biodiversity Institute',
institution_guid: '77ff1bff-af23-4647-b5d1-9d3c414fd003',
isa_number: '2014427',
schema_version: '2.9',
setup_complete: true,
specify6_version: '6.8.03',
stats_2_url: 'https://stats-2.specifycloud.org',
stats_url: 'https://sp7-stats.specifycloud.org/capture',
version: '(debug)',
}));
20 changes: 10 additions & 10 deletions specifyweb/frontend/js_src/lib/components/InitialContext/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,14 @@ export const fetchContext = fetchSystemInfo.then(async (systemInfo) => {

const parameters = {
version: systemInfo.version,
dbVersion: systemInfo.database_version,
institution: systemInfo.institution,
institutionGUID: systemInfo.institution_guid,
discipline: systemInfo.discipline,
collection: systemInfo.collection,
collectionGUID: systemInfo.collection_guid,
isaNumber: systemInfo.isa_number,
disciplineType: systemInfo.discipline_type,
dbVersion: systemInfo.database_version ?? '',
institution: systemInfo.institution ?? '',
institutionGUID: systemInfo.institution_guid ?? '',
discipline: systemInfo.discipline ?? '',
collection: systemInfo.collection ?? '',
collectionGUID: systemInfo.collection_guid ?? '',
isaNumber: systemInfo.isa_number ?? '',
disciplineType: systemInfo.discipline_type ?? '',
collectionObjectCount: counts?.Collectionobject ?? 0,
collectionCount: counts?.Collection ?? 0,
userCount: counts?.Specifyuser ?? 0,
Expand All @@ -132,13 +132,13 @@ export const fetchContext = fetchSystemInfo.then(async (systemInfo) => {
).catch(softFail);

const lambdaUrl = buildStatsLambdaUrl(stats2LambdaFunctionUrl);
if (lambdaUrl) {
if (lambdaUrl && systemInfo.collection_guid) {
if (shouldSkipLambdaStatsRequest(globalThis.location.hostname)) {
return;
}
const storageKey = buildStats2RequestKey(
lambdaUrl,
`${systemInfo.collection_guid}`
systemInfo.collection_guid
);
if (!shouldSendStats2Request(storageKey)) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@ import { load } from './index';
type SystemInfo = {
readonly version: LocalizedString;
readonly specify6_version: LocalizedString;
readonly database_version: LocalizedString;
readonly schema_version: LocalizedString;
readonly collection: string;
readonly collection_guid: LocalizedString;
readonly setup_complete: boolean;
readonly database_version: LocalizedString | null;
readonly schema_version: LocalizedString | null;
readonly collection: string | null;
readonly collection_guid: LocalizedString | null;
readonly database: string;
readonly discipline: string;
readonly institution: string;
readonly institution_guid: LocalizedString;
readonly isa_number: LocalizedString;
readonly discipline: string | null;
readonly institution: string | null;
readonly institution_guid: LocalizedString | null;
readonly isa_number: LocalizedString | null;
readonly stats_url: string | null;
readonly stats_2_url: string | null;
readonly discipline_type: string;
readonly geography_is_global: string;
readonly discipline_type: string | null;
readonly geography_is_global: boolean | null;
};

let systemInfo: SystemInfo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ export function Hierarchy({

const loading = React.useContext(LoadingContext);

const isGeographyGlobal = systemInfo.geography_is_global;
const isGeographyGlobal = systemInfo.geography_is_global ?? false;

const [
disciplineCreationOpen,
Expand Down
16 changes: 10 additions & 6 deletions specifyweb/frontend/js_src/lib/components/TreeView/CreateTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,15 @@ export function CreateTree<
SpecifyResource<AnySchema> | undefined
>(undefined);

const connectedCollection = getSystemInfo().collection;
const discipline = getSystemInfo().discipline;
const connectedCollection = getSystemInfo().collection ?? '';
const discipline = getSystemInfo().discipline ?? '';
Comment thread
coderabbitai[bot] marked this conversation as resolved.
const canCreateTrees = discipline.trim().length > 0;

// Start default tree creation
const handleClick = async (
resource: TaxonFileDefaultDefinition
): Promise<void> => {
if (!canCreateTrees) return;
setIsTreeCreationStarted(true);
return startTreeCreation(
resource.file,
Expand Down Expand Up @@ -233,13 +235,15 @@ export function ImportTree<SCHEMA extends AnyTree>({
TaxonFileDefaultDefinition | undefined
>(undefined);

const connectedCollection = getSystemInfo().collection;
const connectedDiscipline = getSystemInfo().discipline;
const connectedCollection = getSystemInfo().collection ?? '';
const connectedDiscipline = getSystemInfo().discipline ?? '';
const canCreateTrees = connectedDiscipline.trim().length > 0;

const handleClick = async (
resource: TaxonFileDefaultDefinition,
createMissingRanks?: boolean
): Promise<void> => {
if (!canCreateTrees) return;
setSelectedPopulatedTree(resource);
// Check for missing ranks if no preference for createMissingRanks was provided.
if (createMissingRanks === undefined) {
Expand Down Expand Up @@ -374,7 +378,7 @@ export function ImportTree<SCHEMA extends AnyTree>({
async function startTreeCreation(
url: string,
mappingUrl: string,
collection: string,
collectionName: string,
disciplineName: string,
rowCount: number,
treeName: string,
Expand All @@ -388,7 +392,7 @@ async function startTreeCreation(
body: {
url,
mappingUrl,
collection,
collectionName,
disciplineName,
rowCount,
treeName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ function navigator({
isTreeTable(table.name) && !valueIsTreeMeta(parentPartName);

const disciplineType = getSystemInfo().discipline_type?.toLowerCase();
const isNonGeoDiscipline = !geoPaleoDisciplines.includes(disciplineType);
const isGeoPaleoDiscipline =
disciplineType !== undefined &&
geoPaleoDisciplines.includes(disciplineType);
const isNonGeoDiscipline = !isGeoPaleoDiscipline;

if (
isNonGeoDiscipline &&
Expand Down Expand Up @@ -560,10 +563,10 @@ export function getMappingLineData({

const disciplineType =
getSystemInfo().discipline_type?.toLowerCase();
if (
field.name === 'age' &&
!geoPaleoDisciplines.includes(disciplineType)
) {
const isGeoPaleoDiscipline =
disciplineType !== undefined &&
geoPaleoDisciplines.includes(disciplineType);
if (field.name === 'age' && !isGeoPaleoDiscipline) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
{
"version": "(debug)",
"specify6_version": "6.8.03",
"setup_complete": true,
"database_version": "6.8.03",
"schema_version": "2.9",
"stats_url": "https://sp7-stats.specifycloud.org/capture",
"stats_2_url": "https://stats-2.specifycloud.org",
"database": "specify",
"institution": "University of Kansas Biodiversity Institute",
"institution_guid": "77ff1bff-af23-4647-b5d1-9d3c414fd003",
"discipline": "Ichthyology",
"collection": "KU Fish Voucher Collection",
"collection_guid": "3f55b3fa-292d-4170-bd46-66dca41d7f05",
"isa_number": "2014427"
"isa_number": "2014427",
"discipline_type": "fish",
"geography_is_global": true
}
Loading