From e9296a11ae9efdaf9faae6c7c00ec3e4a57eed97 Mon Sep 17 00:00:00 2001
From: Van Go <35277477+van-go@users.noreply.github.com>
Date: Thu, 3 Jul 2025 11:11:04 -0500
Subject: [PATCH 1/5] add back NH Type, Year Pub, and Clear filters
---
.../PublicationSearchToolbar.tsx | 56 ++++++++++++++++++-
1 file changed, 54 insertions(+), 2 deletions(-)
diff --git a/client/modules/datafiles/src/publications/PublicationSearchToolbar/PublicationSearchToolbar.tsx b/client/modules/datafiles/src/publications/PublicationSearchToolbar/PublicationSearchToolbar.tsx
index 282a69022..e753a92d1 100644
--- a/client/modules/datafiles/src/publications/PublicationSearchToolbar/PublicationSearchToolbar.tsx
+++ b/client/modules/datafiles/src/publications/PublicationSearchToolbar/PublicationSearchToolbar.tsx
@@ -1,7 +1,9 @@
-import { Button, Form, Input, Collapse } from 'antd';
+import { Button, Form, Input, Collapse, Select } from 'antd';
import React, { useRef, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useSyncExternalStore } from 'react';
+import * as dropdownOptions from '../../projects/forms/ProjectFormDropdowns';
+import styles from './PublicationSearchToolbar.module.css'
const { Panel } = Collapse;
@@ -71,8 +73,16 @@ export const PublicationSearchToolbar: React.FC = () => {
setSearchParams(params);
};
+ const currentYear = new Date(Date.now()).getUTCFullYear();
+ //Show events going back to 2015
+ const datesInRange = [];
+ for (let i = currentYear; i >= 2015; i--) {
+ datesInRange.push(i);
+ }
+ const yearOptions = [...datesInRange.map((y) => ({ label: y, value: y }))];
+
return (
-
{
)}
+
+
+
+
+
+
+
+
+
+
+
);
};
From 67a0e65e615b559841babf83053d4d96e72722ce Mon Sep 17 00:00:00 2001
From: Van Go <35277477+van-go@users.noreply.github.com>
Date: Wed, 9 Jul 2025 15:10:44 -0500
Subject: [PATCH 2/5] cert-updates
---
conf/nginx/certificates/ca.key | 30 ------------
conf/nginx/certificates/designsafe.dev.crt | 41 ++++++++--------
conf/nginx/certificates/designsafe.dev.ext | 6 ++-
conf/nginx/certificates/designsafe.dev.key | 55 +++++++++++-----------
4 files changed, 52 insertions(+), 80 deletions(-)
diff --git a/conf/nginx/certificates/ca.key b/conf/nginx/certificates/ca.key
index e7836846e..e69de29bb 100644
--- a/conf/nginx/certificates/ca.key
+++ b/conf/nginx/certificates/ca.key
@@ -1,30 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,A0674BB40262439F
-
-sj/ZggeiSee5FvBheTu07NJVuBS4DExYFzSYW9YBbNsFFI01d1h8gvSWBIc+kinq
-ZiTpjbXRYGmRqi9zv6bjHZUn+BR8fnENoJPgLIRiRoDnG/2GQ1JGE88TvDhFAZy9
-yxQgUK5+c43J3UBzFX3lf90S9rLUB381z0zKARLFSddn+xS9Gq4NGmfZZeoO79Vi
-AqlJfIDqRTkxrogBKsA5Nnb9KF+uOVWj51zRCMAgLZVBSgU4HhhTlziLXguq6KCR
-7ennB0ssqroCxsyOtJ2bLb9ktKJ7jVCcBc/Djb3mIzLp44dlSNN/TYa2kB5Z7yII
-b4VfjylxWAZZ6Y0vTVFVs246Sq4ol7kRYHfcplwvS2yGACbt6i/2sfkpMk5wUClh
-naC5bsyk7K8CTql1r9FpCm9hhp4/Dd4GTurrROpfubcZNMNFsDLiHpi14mSZ4af3
-RXrJ7sCf5h5JP7HDZkinY5ARnN9MrF21hqNa3GoB3RaOTYJrDXr0Yfs2SnoYAXYT
-fILLXa22kVA8QsACoCU4tUwBtm6LJ0MOj/Qs1/NCCjGWfw0xMWsIiNMJvw5BM9Rs
-9ttxdCEYASf3sck/m5Z3mALZtpwS1QYarM565TZPpdoegRP9CqSIOjNz2/J49CNl
-ZLXkFmWIMQ5RVbucXdQveGqaIpUAC/w4qIyFOqGCDT2GyvhDko/zYXx5Nwx4Yyhw
-7hM33/Xh7zEIotshyP+WpDCwt2dATMJwJdMXUz+iiuqjGNRzcTn+KaDp3Yq743/H
-zXymc+CyfNgGYE1n5qXwI+D1abDzdcXl7GFgpy+ITJNVQR5P5uZEqInyMYtFqIRV
-1wJ44lYMDREgOi1f8e5RtJYlzd/7m6tX27VqKcvVXlNS9kL4/vH41D2kcNf6iDis
-whSKCk49A3zxjYhOkDIBPFg7SI7ELBYxeB6Sv8neYNPVQZ0QlUhkkVUWbkbvZh2I
-2tH5EaHmapIUOINCrtV5SSC2WCPmXy7FB5bcRSozzpWW8HByZPnTLVrPhIiGXIXj
-+F444loDEhJOZukf2eOCzpF0RNdKqUq9CcwEcyh9QjO2QOa/phR7Jgs7qo48httW
-0IIASEzeZAb3H9xWMH+W9WvOhK0yhhj7UXj2TPCIizeuLrwm52p+t+ANosExfiEi
-VQVqYZXSHV2UXGMKyRTs/2PGmj99JcV/qoqR0qqcO1UPRMtTTEUsCGAS8i8GcfyH
-cZGo93KTPuPCgSAMLvyaO6RFeAlcvO0ElfsYXv/b1U/CCPTynT/e2wkRhmVqv46y
-8mi/E9VR0NH0JghSYuFV+lOFfzixlQOqMBKfMbJXNXKejia4YDq5a0KaNbe87DLD
-9zVrJIM8BOSPFqtrwtyLHU74iiDOs+i51eEdl1EQtjnimvFStTO4zJIsMFvyIfbn
-6tQBtuiDBDNjn7WPPSPgvb6NeVQ0ZgS24kcSo84uP+vbrT3ZtAqluTlxV4czYvUM
-sJATah9kTiLEL4mIGYWrqPerBdo1Ab4yy5ClzCm6cB2vxasdz9ek0wj5JRa0Ik+T
-NZJeP8JibymdICzHBEwPug3YSmngwH01gpEsjP1o4WcOCODnPpCUKg==
------END RSA PRIVATE KEY-----
diff --git a/conf/nginx/certificates/designsafe.dev.crt b/conf/nginx/certificates/designsafe.dev.crt
index d3fd9bfd5..84a8e7761 100644
--- a/conf/nginx/certificates/designsafe.dev.crt
+++ b/conf/nginx/certificates/designsafe.dev.crt
@@ -1,24 +1,21 @@
-----BEGIN CERTIFICATE-----
-MIID8DCCAtigAwIBAgIJAJGHL9vRRgcBMA0GCSqGSIb3DQEBCwUAMFsxCzAJBgNV
-BAYTAlVTMQswCQYDVQQIDAJUWDEPMA0GA1UEBwwGQXVzdGluMQ0wCwYDVQQKDARU
-QUNDMQwwCgYDVQQLDANXTUExETAPBgNVBAMMCGNlcC50ZXN0MB4XDTIzMDQwNTE4
-NTM1OFoXDTI1MDcwODE4NTM1OFowZDELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRl
-eGFzMQ8wDQYDVQQHDAZBdXN0aW4xDTALBgNVBAoMBFRBQ0MxDDAKBgNVBAsMA1dN
-QTEXMBUGA1UEAwwORGVzaWduc2FmZS5kZXYwggEiMA0GCSqGSIb3DQEBAQUAA4IB
-DwAwggEKAoIBAQClQQHj8nG/br0aYQsbX1+u9PEC9zp5+0/DvNwMjtaXGptai9YI
-xoYMpoNX3pbYERRynuy1zGGErZv6JxFVgd0xUs83yJ+4ulr9wGiGdCgWAqG7acnc
-AnBej4erG3qEVu8tvyf6lSoRFs4bXsH+S6zyBe6A1VIas6O8fArf0NxdTpf56tbR
-8WYUC1O3rgdvWLl6yo/+WTqTYp6Xs7gPknyLyJ3dwGB2rfrSy8bvJNPZ9y4q++zU
-tDyIt+xbkjYmyHpKYftCU1diWHbLNhxuGDL6u6wusFThUmyPzP4YjR/b0EL4MzTg
-j57LFI00skXfE8rhbu2/g8X/uJ9MZC6UUb0BAgMBAAGjga0wgaowdQYDVR0jBG4w
-bKFfpF0wWzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlRYMQ8wDQYDVQQHDAZBdXN0
-aW4xDTALBgNVBAoMBFRBQ0MxDDAKBgNVBAsMA1dNQTERMA8GA1UEAwwIY2VwLnRl
-c3SCCQCXpZU7zTBRGDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DAZBgNVHREEEjAQ
-gg5kZXNpZ25zYWZlLmRldjANBgkqhkiG9w0BAQsFAAOCAQEAF2vZVCFVRVk5QIba
-t6cZh5sAXTnHs4p2ffY7HJAUCOi/m5t0wKFcZ4DPfCUxq5OpgoxOTGVz80Z7vYEh
-bWkUNjpwulVcqLVoa8CNHedPIJpNn7ePcijF3ki8s+nWrXbbqc02zHTuTiXHarF9
-gILOu6H1rquAIoqJm8LKgrwqJzp7yOOg/nyUkx2pQe+X8jMAiq4XuQ1B7Q5x9VgH
-e3BksVwrOg6I1W8+Q1jBJV8zx1EmQkRjkflV5i1XEvYJsrGDNdj6lVEZDE1f2ttF
-zxxbCThkLwO1iLA7DlNQNLGr68Tja2daMPw6NGHTycddAw6Kwec7/pJYLyVieGXh
-bYW3lw==
+MIIDdzCCAl+gAwIBAgIUHL39MeXWry/ATKXUK9Y9ebweS48wDQYJKoZIhvcNAQEL
+BQAwZDELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMQ8wDQYDVQQHDAZBdXN0
+aW4xDTALBgNVBAoMBFRBQ0MxDDAKBgNVBAsMA1dNQTEXMBUGA1UEAwwORGVzaWdu
+c2FmZS5kZXYwHhcNMjUwNzA5MTk1NDUyWhcNMjYwNzA5MTk1NDUyWjBkMQswCQYD
+VQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxDzANBgNVBAcMBkF1c3RpbjENMAsGA1UE
+CgwEVEFDQzEMMAoGA1UECwwDV01BMRcwFQYDVQQDDA5EZXNpZ25zYWZlLmRldjCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOJ0q1UVJ6n38PvNgCUSt7Mb
+UO7zZ3JQ81uxQ1LW9VfM4HIraCqUiucqXM6wuHy4rcgj5luSZ7OLuTgbSNbOs/os
+gAHNZ+WcrH5Mg/jf82ur+Wnz087TcKZkmOfkkW1D83KW/AXhdt4EZsjbQxwUPuTG
+o7aLGh3+FxsYYjgOSz0SX0iHEu17R96LdKedQ6mriXLWvZ0n4jHP0qGxhTkLE/KK
+ULF1yvkWeY1bLarz86x4laEzVD5urahyZEkv7pkS45tIgb8asO9cW/koSAMgBGha
+3AIuCTNsFp6pIDRe08DBbjEb4VT/xkwvNhVvK4lTra6Fj20gmUXIDNkK8+LGM80C
+AwEAAaMhMB8wHQYDVR0OBBYEFD/+PNonvWy31xQkRaRpZjCdSfTNMA0GCSqGSIb3
+DQEBCwUAA4IBAQBz48ZTXrB+9j3O9iPmqtxG8cKJXYKDTPifP6z8t2uCl/NDR13n
+mlEybH40SB8F7vf3hss/KpTyprDJ4jrRjo/j8vdrkCMqOs11J/dgo9HHytu5GIDz
+RS1spwXfH2TSe5nd1qENpDYoSBWqBUTaCQ12n0m5gpy4V4r2W4F6m5VsBX8OvzGY
+178HxbuWyVWRsmGnOiLpHwm8XRpt5ecielABkfiWNsWZTHD8i/l9osE1WIuPNiN+
+v0UvxPPPFJeyxB7e5o+rinKowYBf3qhXP44woLreaiS5+qC0t/i4MwMiSx3oFA6z
+LKJvOrMEEiyDmL2Zfl5M89k/Smzj2Ekq9MlH
-----END CERTIFICATE-----
diff --git a/conf/nginx/certificates/designsafe.dev.ext b/conf/nginx/certificates/designsafe.dev.ext
index 97ae65db1..3306fe526 100644
--- a/conf/nginx/certificates/designsafe.dev.ext
+++ b/conf/nginx/certificates/designsafe.dev.ext
@@ -1,7 +1,11 @@
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
-keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
+keyUsage = digitalSignature, keyEncipherment
+extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = designsafe.dev
+DNS.2 = localhost
+IP.1 = 127.0.0.1
+IP.2 = ::1
diff --git a/conf/nginx/certificates/designsafe.dev.key b/conf/nginx/certificates/designsafe.dev.key
index d91e4c48c..1c4d1d22c 100644
--- a/conf/nginx/certificates/designsafe.dev.key
+++ b/conf/nginx/certificates/designsafe.dev.key
@@ -1,27 +1,28 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEpQIBAAKCAQEApUEB4/Jxv269GmELG19frvTxAvc6eftPw7zcDI7WlxqbWovW
-CMaGDKaDV96W2BEUcp7stcxhhK2b+icRVYHdMVLPN8ifuLpa/cBohnQoFgKhu2nJ
-3AJwXo+Hqxt6hFbvLb8n+pUqERbOG17B/kus8gXugNVSGrOjvHwK39DcXU6X+erW
-0fFmFAtTt64Hb1i5esqP/lk6k2Kel7O4D5J8i8id3cBgdq360svG7yTT2fcuKvvs
-1LQ8iLfsW5I2Jsh6SmH7QlNXYlh2yzYcbhgy+rusLrBU4VJsj8z+GI0f29BC+DM0
-4I+eyxSNNLJF3xPK4W7tv4PF/7ifTGQulFG9AQIDAQABAoIBAQCH4ratO/Uw1tyE
-znuVnI1PjnaIW8cn+vESIUBIy0PFqMlKYWY0fRpJWLr0DEK5lQHdZrV6oH8n3KI/
-xtRIHatHHbLrSfucqRCdTBQnS2iTAMMBGvI0CYVhHGEQ4F2UaO/wDBnRwcp/luMQ
-OpEGjC/ALAR8x+zlrAXdvZorhNFPnzgg/GvQ3+F4xJPPnuxa/O70yKb4kfWx4+SJ
-riUu0bhTvyqP2y49KWCoUATdRiHjg5IEZjYNM1pD3QamX4PS6icmhm0txA6ZO1Pv
-4kVV7Q4c3aMDxZ30sa74y51ojzV7QD3zJZxD6W3CKBK+APa7NQOY9J6lpIyvZ8dI
-zXU7tzKJAoGBANnsBj8+ZgRTXj4EOuc4Pok2ZCH/Lq8bs3j7OL4okXx6QNnUHgd6
-1ItnlkJZpDJIjX9ICpvIM7vupVW50BNclX2fiXiu4xVaLEHE162zBDlShVJMakwR
-gC4+k2geY1MWzePPe6I6q/7omjipQzIWfDLl7hiNEsJwJ2yo/VfhhDsHAoGBAMIh
-ECFv0qCAqMTFnKAnZpqhYIzBMBUgNkGaTSsmbVyBL8IQ7CsrTGCPmoGVyGKqJ9NZ
-0OXJhh6woYap8BJNyS0F/UBOg3n1DEARj1PaGxwz1ngguqOz0vK1Fd8JrNqtNgM2
-ftFRWiiVrITWSGo2O6Z4qo2H7wzo65q+5MM3Vl23AoGAFEAUjIf13u0IUub9ukLF
-vOZrA6W85tTCJrnhmfoXGuQZZqSJbdSCL7oegmfmFC4dx/gf1D4UYKBaiM7NgR8X
-XScYFTjbLT7F4g6ypBeAivsaHH2xWwQ9Tw7Nj91TCYOS+lpreLXPCc7/lchU0DC6
-tdgb+fgSCDN31mcVs7bnHL0CgYEAmpNpHLbFqDfKKqSPSpcVBBtrnahWePvlN+d9
-etZmTjovpP+Ejfs0Hc863+Q8YxPKEmh6Bf6pZNaQR4IZPvnhLpCOTx41Ym7VCk7x
-KXuKCrLcG1s0QhV16vx2Jdq2YIl7cKN754A3oglMDZnyn1//r1Z1t6x9lHIC6H0H
-+smdY5ECgYEA0YmyOMHvdl2ARJuTZ+/kzq+7XwEibMp8CdYglmUK+UqkgKHgRXdC
-5fpzBkwD/LzqgceNBwUW95S5AUE7L0jsGYdlr0aCQzfvtOXoaby+TWX467wll4oe
-5YM4PDa66T8cfQ7MdEGe19mUoAyQmGxHzCbBuk/i4GdVv0Uq4oSms0s=
------END RSA PRIVATE KEY-----
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDidKtVFSep9/D7
+zYAlErezG1Du82dyUPNbsUNS1vVXzOByK2gqlIrnKlzOsLh8uK3II+Zbkmezi7k4
+G0jWzrP6LIABzWflnKx+TIP43/Nrq/lp89PO03CmZJjn5JFtQ/NylvwF4XbeBGbI
+20McFD7kxqO2ixod/hcbGGI4Dks9El9IhxLte0fei3SnnUOpq4ly1r2dJ+Ixz9Kh
+sYU5CxPyilCxdcr5FnmNWy2q8/OseJWhM1Q+bq2ocmRJL+6ZEuObSIG/GrDvXFv5
+KEgDIARoWtwCLgkzbBaeqSA0XtPAwW4xG+FU/8ZMLzYVbyuJU62uhY9tIJlFyAzZ
+CvPixjPNAgMBAAECggEAA/GGq0ZhSLEv4d6oAN1E1CphXCvibQfyWPIrCzYBkzwW
+234chANOsOxYreO/brKTmiTf5c/UnPiNque0usLiOPTN7NocGVBDY7am5K5XsZQO
+1ZTApz0g3NDrJbyh+K5ifqgY7uxIcuDOPmitED/dAYRhBPSCqauLUPY5faLoFgHj
+wp2PQSn38Pne4c9wEBHH0hS0RFOnYVXjCTSdQ14+tdlupz2RkR0ANgoJe0KoNWlp
+kxmIyVALreP+mXNCu+cBYu9l35vOzo4TPDZFU+0/5uzadaySqdBdNFXxDwxqilid
+6fvquanD1+SU6mK53SJ/lEiHzegYK+vemk7Kj/yNWQKBgQDxorByJ8kOzkZI4zOe
+HpBjw3J6E6iWnJr21jyy7xkI0dhCynFOEwoitXcIh0P8ftImAQnna2ASSMu7iufp
+V7kDAhS7uGjZ+4li3vxgp0po9s2i0Fq9twTFqI/P/fckrWZSnotyXeF6FytFjYuK
+B+nmcIRBVbaNEKTNBEZK7J0I1QKBgQDv6vfMvnfSOjQK66b4JVupnRq49ZQ46TVy
+vQKm40Nlcxl2kBoCYa/ZN7r6gkLmK8W7KUAM6GebG4hEQyndZxgzRaMTsaNyQ/y+
+J69mVUF8OXi5cNnHyDdPAoP/vddRJAg4iutYrx2C2TsqBNtxiIKSvssrVjBv6Yi/
+JX1M/Gp7GQKBgQCFsdNYxiSbmYwP5g34TcbsPAbJ/riH5MxlCLJ6+onqdFjo06QP
+Y7925tqekGEQgob52hdFwQZd94MqGDgqxouqW2tnVihFe8RPPTt8qvMj0nKCxC+A
+ypPMXXB8z3MACyUTT3+uXr1T1R/vEtsDG0/SXaz/jVI9CNl2Sgguim50pQKBgQCf
+oRHxK4Wj3dAEuBhnIMwlRX+jMtrFJv3F8taR3cJY/MUjauuzS/XprDf/N651YQh1
+6BNSw0s0G5SF/r4bLt4eNyYzE01x2KSQjO0aRH2GvuKBWAG++Is8Sasz/McHmNbT
+cDL41cLn7ct8wLCVkMN4CfQ6SGWAAL1YmQQOth/O+QKBgCjPmh2KJseQ03jUoR5z
+LVcTkYPQX722jHoFzsWJm03FKKyfhX0wMZplWn3QkDyuj0FsXXjmHo6KOWTmbGvj
+uzMHNedj697yekGTXS/CEeAlV8CNDOGFWmIvdwr2S29HTdIUjXeGwaqlqiObhW0x
+PW8wgffkAJWGe5bEHc4qMCUV
+-----END PRIVATE KEY-----
From b4a2cc50061779ac5a3ee4945c10ffc08be69e2a Mon Sep 17 00:00:00 2001
From: Van Go <35277477+van-go@users.noreply.github.com>
Date: Wed, 19 Nov 2025 17:18:48 -0600
Subject: [PATCH 3/5] update to fetch and sum both Web of Knowledge and
Preprint Network citations
---
designsafe/apps/api/publications/operations.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/designsafe/apps/api/publications/operations.py b/designsafe/apps/api/publications/operations.py
index 6bdc30436..e860ac7d9 100644
--- a/designsafe/apps/api/publications/operations.py
+++ b/designsafe/apps/api/publications/operations.py
@@ -279,10 +279,11 @@ def clarivate_single_api(doi):
response.raise_for_status()
rspdict = response.json()
- citations = [
- source['count'] for source in rspdict['hits'][0]['citations']
- if source['db'] == 'WOK'
- ][0]
+ citations = sum(
+ source['count']
+ for source in rspdict['hits'][0]['citations']
+ if source['db'] in ('WOK', 'PPRN')
+ )
return citations
From e925d7c18abc3242dd01e795211307c6945fd49f Mon Sep 17 00:00:00 2001
From: Van Go <35277477+van-go@users.noreply.github.com>
Date: Wed, 19 Nov 2025 17:22:41 -0600
Subject: [PATCH 4/5] update
---
conf/nginx/certificates/designsafe.dev.ext | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/conf/nginx/certificates/designsafe.dev.ext b/conf/nginx/certificates/designsafe.dev.ext
index 3306fe526..97ae65db1 100644
--- a/conf/nginx/certificates/designsafe.dev.ext
+++ b/conf/nginx/certificates/designsafe.dev.ext
@@ -1,11 +1,7 @@
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
-keyUsage = digitalSignature, keyEncipherment
-extendedKeyUsage = serverAuth
+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = designsafe.dev
-DNS.2 = localhost
-IP.1 = 127.0.0.1
-IP.2 = ::1
From 12aae54711a2b4fd48aaa7268ae3e5f82367615c Mon Sep 17 00:00:00 2001
From: Van Go <35277477+van-go@users.noreply.github.com>
Date: Thu, 20 Nov 2025 13:50:42 -0600
Subject: [PATCH 5/5] move Clarivate endpoint to publications_v2
---
.../apps/api/publications/operations.py | 41 ------------------
designsafe/apps/api/publications/urls.py | 5 ++-
designsafe/apps/api/publications/views.py | 18 --------
.../publications_v2/operations/clarivate.py | 42 +++++++++++++++++++
designsafe/apps/api/publications_v2/urls.py | 3 ++
designsafe/apps/api/publications_v2/views.py | 17 ++++++++
6 files changed, 65 insertions(+), 61 deletions(-)
create mode 100644 designsafe/apps/api/publications_v2/operations/clarivate.py
diff --git a/designsafe/apps/api/publications/operations.py b/designsafe/apps/api/publications/operations.py
index e860ac7d9..5bff9556c 100644
--- a/designsafe/apps/api/publications/operations.py
+++ b/designsafe/apps/api/publications/operations.py
@@ -5,8 +5,6 @@
from designsafe.libs.elasticsearch.utils import new_es_client
from django.contrib.auth import get_user_model
from elasticsearch_dsl import Q
-import os
-import requests
import datetime
import json
import urllib
@@ -256,42 +254,3 @@ def initilize_publication(publication, status='publishing', revision=None, revis
# Refresh index so that search works in subsequent pipeline operations.
IndexedPublication._index.refresh(using=es_client)
return pub
-
-def clarivate_single_api(doi):
-
- base_url = 'https://api.clarivate.com/apis/wos-starter/v1/documents'
- apikey = os.environ.get('WOS_APIKEY')
-
- if not apikey:
- return {"error": "Clarivate API key is missing"}
-
- if not doi:
- return {"error": "DOI is required"}
-
- params = {'db': 'DRCI', 'q': f"DO={doi}"}
-
- try:
- response = requests.get(
- base_url,
- headers={'X-Apikey': apikey},
- params=params
- )
- response.raise_for_status()
- rspdict = response.json()
-
- citations = sum(
- source['count']
- for source in rspdict['hits'][0]['citations']
- if source['db'] in ('WOK', 'PPRN')
- )
-
- return citations
-
- except requests.exceptions.RequestException as e:
- return {
- "error": str(e),
- }
- except (KeyError, IndexError) as e:
- return {
- "error": "Unexpected response format",
- }
diff --git a/designsafe/apps/api/publications/urls.py b/designsafe/apps/api/publications/urls.py
index aba4465ef..7b50afc0d 100644
--- a/designsafe/apps/api/publications/urls.py
+++ b/designsafe/apps/api/publications/urls.py
@@ -1,5 +1,6 @@
from django.urls import re_path as url
-from designsafe.apps.api.publications.views import PublicationListingView, PublicationDetailView, PublicationDataCiteView, PublicationDataCiteEventsView, PublicationClarivateView
+from designsafe.apps.api.publications.views import PublicationListingView, PublicationDetailView, PublicationDataCiteView, PublicationDataCiteEventsView
+from designsafe.apps.api.publications_v2.views import PublicationClarivateView
from django.http import JsonResponse
from django.views.decorators.cache import cache_page
@@ -11,7 +12,7 @@
# GET /listing////
url(r'^data-cite/events$', cache_page(60*15)(PublicationDataCiteEventsView.as_view()), name='publication_datacite_usage'),
url(r'^data-cite/(?P\S+)$', cache_page(60*15)(PublicationDataCiteView.as_view()), name='publication_datacite'),
- url(r'^clarivate/$', cache_page(60*15)(PublicationClarivateView.as_view()), name='publication_clarivate'),
+ url(r'^clarivate/$', cache_page(60*15)(PublicationClarivateView.as_view()), name='publication_clarivate_legacy'),
url(r'^(?P[\w.-]+)/$', PublicationListingView.as_view(), name='publication_listing'),
url(r'^(?P[\w.-]+)/(?P[A-Z\-]+-[0-9]+)(v(?P[0-9]+))?/$', PublicationDetailView.as_view(), name='publication_detail'),
url(r'^(?P[\w.-]+)/(?P[\w.-]+)/$', PublicationDetailView.as_view(), name='legacy-publication_detail'),
diff --git a/designsafe/apps/api/publications/views.py b/designsafe/apps/api/publications/views.py
index 727f77472..8dd43e5e3 100644
--- a/designsafe/apps/api/publications/views.py
+++ b/designsafe/apps/api/publications/views.py
@@ -4,7 +4,6 @@
from django.conf import settings
from requests.exceptions import HTTPError
from designsafe.apps.api.publications import operations
-from designsafe.apps.api.publications.operations import clarivate_single_api
from designsafe.apps.projects.managers import datacite as DataciteManager
from django.utils.decorators import method_decorator
import json
@@ -73,20 +72,3 @@ def get(self, request):
return JsonResponse(events)
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
-
-"""
-API endpoint to retrieve Clarivate citation count for a single DOI.
-"""
-
-class PublicationClarivateView(BaseApiView):
- def get(self, request):
- doi = request.GET.get('doi', '')
-
- if not doi:
- return JsonResponse({'error': 'DOI parameter is required'}, status=400)
-
- try:
- citations = clarivate_single_api(doi)
- return JsonResponse({'doi': doi, 'citation_count': citations})
- except Exception as e:
- return JsonResponse({'error': str(e)}, status=500)
diff --git a/designsafe/apps/api/publications_v2/operations/clarivate.py b/designsafe/apps/api/publications_v2/operations/clarivate.py
new file mode 100644
index 000000000..9edb33064
--- /dev/null
+++ b/designsafe/apps/api/publications_v2/operations/clarivate.py
@@ -0,0 +1,42 @@
+import os
+import requests
+
+
+def clarivate_single_api(doi: str):
+ """Fetch Clarivate citation count for a DOI."""
+ base_url = 'https://api.clarivate.com/apis/wos-starter/v1/documents'
+ apikey = os.environ.get('WOS_APIKEY')
+
+ if not apikey:
+ return {"error": "Clarivate API key is missing"}
+
+ if not doi:
+ return {"error": "DOI is required"}
+
+ params = {'db': 'DRCI', 'q': f"DO={doi}"}
+
+ try:
+ response = requests.get(
+ base_url,
+ headers={'X-Apikey': apikey},
+ params=params
+ )
+ response.raise_for_status()
+ rspdict = response.json()
+
+ citations = sum(
+ source['count']
+ for source in rspdict['hits'][0]['citations']
+ if source['db'] in ('WOK', 'PPRN')
+ )
+
+ return citations
+
+ except requests.exceptions.RequestException as exc:
+ return {
+ "error": str(exc),
+ }
+ except (KeyError, IndexError):
+ return {
+ "error": "Unexpected response format",
+ }
diff --git a/designsafe/apps/api/publications_v2/urls.py b/designsafe/apps/api/publications_v2/urls.py
index c13b361e6..526504db4 100644
--- a/designsafe/apps/api/publications_v2/urls.py
+++ b/designsafe/apps/api/publications_v2/urls.py
@@ -1,12 +1,14 @@
"""Publication API routes"""
from django.urls import path, re_path
+from django.views.decorators.cache import cache_page
from designsafe.apps.api.publications_v2.views import (
PublicationListingView,
PublicationDetailView,
PublicationPublishView,
PublicationAmendView,
PublicationVersionView,
+ PublicationClarivateView,
)
urlpatterns = [
@@ -15,6 +17,7 @@
path("publish/", PublicationPublishView.as_view()),
path("amend/", PublicationAmendView.as_view()),
path("version/", PublicationVersionView.as_view()),
+ path("clarivate/", cache_page(60 * 15)(PublicationClarivateView.as_view())),
re_path(
r"^(?P[A-Z\-]+-[0-9]+)(v(?P[0-9]+))?/?$",
PublicationDetailView.as_view(),
diff --git a/designsafe/apps/api/publications_v2/views.py b/designsafe/apps/api/publications_v2/views.py
index 1cc2841ed..dfc474817 100644
--- a/designsafe/apps/api/publications_v2/views.py
+++ b/designsafe/apps/api/publications_v2/views.py
@@ -7,6 +7,7 @@
from designsafe.apps.api.views import BaseApiView, ApiException
from designsafe.apps.api.publications_v2.models import Publication
from designsafe.apps.api.publications_v2.elasticsearch import IndexedPublication
+from designsafe.apps.api.publications_v2.operations.clarivate import clarivate_single_api
from designsafe.apps.api.projects_v2.operations.project_publish_operations import (
publish_project_async,
amend_publication_async,
@@ -222,6 +223,22 @@ def get(self, request: HttpRequest):
return JsonResponse({"result": result, "total": total})
+class PublicationClarivateView(BaseApiView):
+ """API endpoint to retrieve Clarivate citation count for a single DOI."""
+
+ def get(self, request: HttpRequest):
+ doi = request.GET.get("doi", "")
+
+ if not doi:
+ return JsonResponse({"error": "DOI parameter is required"}, status=400)
+
+ try:
+ citations = clarivate_single_api(doi)
+ return JsonResponse({"doi": doi, "citation_count": citations})
+ except Exception as exc: # pragma: no cover - passthrough error handling
+ return JsonResponse({"error": str(exc)}, status=500)
+
+
class PublicationDetailView(BaseApiView):
"""View for retrieving publication details."""