Skip to content
Open
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
2 changes: 1 addition & 1 deletion specifyweb/backend/context/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
re_path(r'^api_endpoints.json$', views.api_endpoints),
re_path(r'^api_endpoints_all.json$', views.api_endpoints_all),
re_path(r'^user.json$', views.user),
re_path(r'^stats_counts.json$', views.stats_counts),
re_path(r'^system_info.json$', views.system_info),
re_path(r'^server_time.json$', views.get_server_time),
re_path(r'^domain.json$', views.domain),
Expand All @@ -40,5 +41,4 @@
path('collection_resource/', collection_resources.collection_resources),
path('collection_resource/<int:resourceid>/', collection_resources.collection_resource),


]
17 changes: 16 additions & 1 deletion specifyweb/backend/context/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
PermissionTargetAction, \
check_permission_targets, skip_collection_access_check, query_pt, \
CollectionAccessPT
from specifyweb.specify.models import Collection, Institution, \
from specifyweb.specify.models import Collection, Collectionobject, Institution, \
Specifyuser, Spprincipal, Spversion, Collectionobjecttype
from specifyweb.specify.models_utils.schema import base_schema
from specifyweb.specify.models_utils.serialize_datamodel import datamodel_to_json
Expand Down Expand Up @@ -655,6 +655,7 @@ def system_info(request):
database_version=spversion.appversion,
schema_version=spversion.schemaversion,
stats_url=settings.STATS_URL,
stats_2_url=settings.STATS_2_URL,
database=settings.DATABASE_NAME,
institution=institution.name,
institution_guid=institution.guid,
Expand Down Expand Up @@ -1018,3 +1019,17 @@ def schema_language(request):
dict(zip(('language', 'country', 'variant'), row))
for row in schema_languages
], safe=False)

@require_http_methods(['GET', 'HEAD'])
@cache_control(max_age=86400, public=True)
@login_maybe_required
def stats_counts(request):
"""Get the count of collection objects, collections, and users."""
co_count = Collectionobject.objects.count()
collection_count = Collection.objects.count()
user_count = Specifyuser.objects.count()
return JsonResponse({
'Collectionobject': co_count,
'Collection': collection_count,
'Specifyuser': user_count,
})
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,67 @@ type SystemInfo = {
readonly institution_guid: LocalizedString;
readonly isa_number: LocalizedString;
readonly stats_url: string | null;
readonly stats_2_url: string | null;
readonly discipline_type: string;
};

type StatsCounts = {
readonly Collectionobject: number;
readonly Collection: number;
readonly Specifyuser: number;
};

let systemInfo: SystemInfo;

function buildStatsLambdaUrl(base: string | null | undefined): string | null {
if (!base) return null;
let u = base.trim();

if (!/^https?:\/\//i.test(u)) u = `https://${u}`;

const hasRoute = /\/(prod|default)\/[^\s/]+/.test(u);
if (!hasRoute) {
const stage = 'prod';
const route = 'AggrgatedSp7Stats';
u = `${u.replace(/\/$/, '') }/${stage}/${route}`;
}
return u;
}

export const fetchContext = load<SystemInfo>(
'/context/system_info.json',
'application/json'
).then((data) => {
).then(async (data) => {
systemInfo = data;
if (systemInfo.stats_url !== null)
ping(

if (systemInfo.stats_url !== null) {
let counts: StatsCounts | null = null;
try {
counts = await load<StatsCounts>('/context/stats_counts.json', 'application/json');
} catch {
// If counts fetch fails, proceed without them.
counts = null;
}

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,
collectionObjectCount: counts?.Collectionobject ?? 0,
collectionCount: counts?.Collection ?? 0,
userCount: counts?.Specifyuser ?? 0,
};

await ping(
formatUrl(
systemInfo.stats_url,
{
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,
},
parameters,
/*
* I don't know if the receiving server handles GET parameters in a
* case-sensitive way. Thus, don't convert keys to lower case, but leave
Expand All @@ -56,7 +92,22 @@ export const fetchContext = load<SystemInfo>(
),
{ errorMode: 'silent' }
).catch(softFail);

/*
* Await ping(
* formatUrl(systemInfo.stats_2_url, parameters, false),
* { errorMode: 'silent' }
* ).catch(softFail);
*/

const lambdaUrl = buildStatsLambdaUrl(systemInfo.stats_2_url);
if (lambdaUrl) {
await ping(formatUrl(lambdaUrl, parameters, false), { errorMode: 'silent' })
.catch(softFail);
}
}

return systemInfo;
});

export const getSystemInfo = (): SystemInfo => systemInfo;
export const getSystemInfo = (): SystemInfo => systemInfo;
2 changes: 2 additions & 0 deletions specifyweb/settings/specify_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@
# Usage stats are transmitted to the following address.
# Set to None to disable.
STATS_URL = "https://stats.specifycloud.org/capture"
# STATS_2_URL = "https://stats-2.specifycloud.org/prod/AggrgatedSp7Stats"
STATS_2_URL = "pj9lpoo1pc.execute-api.us-east-1.amazonaws.com"

# Workbench uploader log directory.
# Must exist and be writeable by the web server process.
Expand Down