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 ( -
{
)} +
+ +   + setSearchParam('pub-year', v)} + /> +
+ + ); }; 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."""