From 9de3f38d0aa13d82a940a74c0a597e01232a9794 Mon Sep 17 00:00:00 2001 From: Stefan Janssen Date: Wed, 5 Jun 2024 23:33:36 +0200 Subject: [PATCH] Add social handles (#3412) * user profile can now also store social handles from google scholar, ORCID and ResearchGate * set DEFAULT NULL to new qiita.qiita_user columns * extending existing test * extending tests * add tests for validator functions for WTform StringFields * operate on str instead of field.data --- qiita_db/support_files/populate_test_db.sql | 2 +- qiita_db/support_files/qiita-db-unpatched.sql | 5 +- qiita_db/test/test_user.py | 20 +- qiita_db/test/test_util.py | 3 +- qiita_pet/handlers/user_handlers.py | 184 +++++++++++++++++- .../static/img/logo_social_googlescholar.png | Bin 0 -> 7606 bytes qiita_pet/static/img/logo_social_orcid.png | Bin 0 -> 9579 bytes .../static/img/logo_social_researchgate.png | Bin 0 -> 1317 bytes qiita_pet/templates/user_profile.html | 14 +- qiita_pet/test/test_user_handlers.py | 80 +++++++- 10 files changed, 286 insertions(+), 22 deletions(-) create mode 100644 qiita_pet/static/img/logo_social_googlescholar.png create mode 100644 qiita_pet/static/img/logo_social_orcid.png create mode 100644 qiita_pet/static/img/logo_social_researchgate.png diff --git a/qiita_db/support_files/populate_test_db.sql b/qiita_db/support_files/populate_test_db.sql index 12035c788..46c0aaed7 100644 --- a/qiita_db/support_files/populate_test_db.sql +++ b/qiita_db/support_files/populate_test_db.sql @@ -50,7 +50,7 @@ INSERT INTO qiita.user_level VALUES (7, 'wet-lab admin', 'Can access the private -- Data for Name: qiita_user; Type: TABLE DATA; Schema: qiita; Owner: antoniog -- -INSERT INTO qiita.qiita_user VALUES ('test@foo.bar', 4, '$2a$12$gnUi8Qg.0tvW243v889BhOBhWLIHyIJjjgaG6dxuRJkUM8nXG9Efe', 'Dude', 'Nowhere University', '123 fake st, Apt 0, Faketown, CO 80302', '111-222-3344', NULL, NULL, NULL, false); +INSERT INTO qiita.qiita_user VALUES ('test@foo.bar', 4, '$2a$12$gnUi8Qg.0tvW243v889BhOBhWLIHyIJjjgaG6dxuRJkUM8nXG9Efe', 'Dude', 'Nowhere University', '123 fake st, Apt 0, Faketown, CO 80302', '111-222-3344', NULL, NULL, NULL, false, '0000-0002-0975-9019', 'Rob-Knight', '_e3QL94AAAAJ'); INSERT INTO qiita.qiita_user VALUES ('shared@foo.bar', 4, '$2a$12$gnUi8Qg.0tvW243v889BhOBhWLIHyIJjjgaG6dxuRJkUM8nXG9Efe', 'Shared', 'Nowhere University', '123 fake st, Apt 0, Faketown, CO 80302', '111-222-3344', NULL, NULL, NULL, false); INSERT INTO qiita.qiita_user VALUES ('admin@foo.bar', 1, '$2a$12$gnUi8Qg.0tvW243v889BhOBhWLIHyIJjjgaG6dxuRJkUM8nXG9Efe', 'Admin', 'Owner University', '312 noname st, Apt K, Nonexistantown, CO 80302', '222-444-6789', NULL, NULL, NULL, false); INSERT INTO qiita.qiita_user VALUES ('demo@microbio.me', 4, '$2a$12$gnUi8Qg.0tvW243v889BhOBhWLIHyIJjjgaG6dxuRJkUM8nXG9Efe', 'Demo', 'Qiita Dev', '1345 Colorado Avenue', '303-492-1984', NULL, NULL, NULL, false); diff --git a/qiita_db/support_files/qiita-db-unpatched.sql b/qiita_db/support_files/qiita-db-unpatched.sql index 1ce86de39..a61b4645d 100644 --- a/qiita_db/support_files/qiita-db-unpatched.sql +++ b/qiita_db/support_files/qiita-db-unpatched.sql @@ -1888,7 +1888,10 @@ CREATE TABLE qiita.qiita_user ( user_verify_code character varying, pass_reset_code character varying, pass_reset_timestamp timestamp without time zone, - receive_processing_job_emails boolean DEFAULT false + receive_processing_job_emails boolean DEFAULT false, + social_orcid character varying DEFAULT NULL, + social_researchgate character varying DEFAULT NULL, + social_googlescholar character varying DEFAULT NULL ); diff --git a/qiita_db/test/test_user.py b/qiita_db/test/test_user.py index 2dced88d9..666746c36 100644 --- a/qiita_db/test/test_user.py +++ b/qiita_db/test/test_user.py @@ -72,7 +72,10 @@ def setUp(self): 'pass_reset_code': None, 'pass_reset_timestamp': None, 'user_verify_code': None, - 'receive_processing_job_emails': True + 'receive_processing_job_emails': True, + 'social_orcid': None, + 'social_researchgate': None, + 'social_googlescholar': None } def tearDown(self): @@ -125,7 +128,10 @@ def test_create_user(self): 'address': None, 'user_level_id': 5, 'receive_processing_job_emails': False, - 'email': 'testcreateuser@test.bar'} + 'email': 'testcreateuser@test.bar', + 'social_orcid': None, + 'social_researchgate': None, + 'social_googlescholar': None} self._check_correct_info(obs, exp) # Make sure new system messages are linked to user @@ -162,7 +168,10 @@ def test_create_user_info(self): 'user_verify_code': '', 'user_level_id': 5, 'receive_processing_job_emails': True, - 'email': 'testcreateuserinfo@test.bar'} + 'email': 'testcreateuserinfo@test.bar', + 'social_orcid': None, + 'social_researchgate': None, + 'social_googlescholar': None} self._check_correct_info(obs, exp) def test_create_user_column_not_allowed(self): @@ -229,7 +238,10 @@ def test_get_info(self): 'pass_reset_timestamp': None, 'user_verify_code': None, 'receive_processing_job_emails': False, - 'phone': '222-444-6789' + 'phone': '222-444-6789', + 'social_orcid': None, + 'social_researchgate': None, + 'social_googlescholar': None } self.assertEqual(self.user.info, expinfo) diff --git a/qiita_db/test/test_util.py b/qiita_db/test/test_util.py index a8df7ed3c..26fff3004 100644 --- a/qiita_db/test/test_util.py +++ b/qiita_db/test/test_util.py @@ -92,7 +92,8 @@ def test_get_table_cols(self): obs = qdb.util.get_table_cols("qiita_user") exp = {"email", "user_level_id", "password", "name", "affiliation", "address", "phone", "user_verify_code", "pass_reset_code", - "pass_reset_timestamp", "receive_processing_job_emails"} + "pass_reset_timestamp", "receive_processing_job_emails", + "social_orcid", "social_researchgate", "social_googlescholar"} self.assertEqual(set(obs), exp) def test_exists_table(self): diff --git a/qiita_pet/handlers/user_handlers.py b/qiita_pet/handlers/user_handlers.py index 116c69432..d75316a80 100644 --- a/qiita_pet/handlers/user_handlers.py +++ b/qiita_pet/handlers/user_handlers.py @@ -6,8 +6,11 @@ # The full license is in the file LICENSE, distributed with this software. # ----------------------------------------------------------------------------- +import re + from tornado.web import authenticated, HTTPError from wtforms import Form, StringField, BooleanField, validators +from wtforms.validators import ValidationError from qiita_pet.handlers.base_handlers import BaseHandler from qiita_pet.handlers.api_proxy import user_jobs_get_req @@ -21,6 +24,157 @@ class UserProfile(Form): + def validate_general(value: str, infomsg: str, url_prefix: str): + """Validate basic user inputs, i.e. check for leading/trailing + whitespaces and leading URL prefix, like http://scholar.google.com/ + + Parameters + ---------- + value : str + The WTform user input string. + infomsg : str + An error message to inform the user how to extract the correct + value. + url_prefix : str + The URL prefix of the social network + + Returns + ------- + None in case of empty input, otherwise the input value + + Raises + ------ + ValidationError if + a) input has leading or trailing whitespaces + b) input starts with the given url_prefix + """ + if (value is None) or (value == ""): + # nothing to complain, as input is empty + return None + + if value != value.strip(): + raise ValidationError( + 'Please remove all leading and trailing whitespaces from your ' + 'input.
%s' % infomsg) + + if len(url_prefix) > 0: + isPrefix = re.search("^%s" % url_prefix, value) + if isPrefix is not None: + raise ValidationError( + 'Please remove the "%s" part from your input.
%s' % ( + isPrefix[0], infomsg)) + + # if there is still no error raised, we return the actual value of the + # user input + return value + + def validator_orcid_id(form: Form, field: StringField): + """A WTForm validator to check if user input follows ORCID syntax. + + Parameters + ---------- + form : wtforms.Form + The WTform form enclosing the user input field. + field : wtforms.StringField + The WTform user input field. + + Returns + ------- + True, if user input is OK. + + Raises + ------ + ValidationError if user input is not valid + """ + infomsg = ('Enter only your 16 digit numerical ORCID identifier, where' + ' every four digits are separated with a dash "-". An ' + 'example is: 0000-0002-0975-9019') + value = UserProfile.validate_general( + field.data, infomsg, 'https://orcid.org') + if value is None: + return True + + if re.search(r"^\d{4}-\d{4}-\d{4}-\d{4}$", value) is None: + raise ValidationError( + "Your input does not follow the required format.
%s" % + infomsg) + + def validator_gscholar_id(form, field): + """A WTForm validator to check if user input follows google scholar ID + syntax. + + Parameters + ---------- + form : wtforms.Form + The WTform form enclosing the user input field. + field : wtforms.StringField + The WTform user input field. + + Returns + ------- + True, if user input is OK. + + Raises + ------ + ValidationError if user input is not valid + """ + infomsg = ('To retrieve your google scholar ID, surf to your profile ' + 'and copy the URL in your browser. It might read like ' + 'https://scholar.google.com/citations?user=_e3QL94AAAAJ&' + 'hl=en
Ignore everything left of the "?". The right ' + 'part is a set of key=value pairs, separated by "&" ' + 'characters. Find the key "user=", the right part up to ' + 'the next "&" is your google scholar ID, in the example: ' + '"_e3QL94AAAAJ"') + # we need a regex here, since we don't know the TLD the user is + # presenting to us + value = UserProfile.validate_general( + field.data, infomsg, r'https://scholar.google.\w{1,3}/citations\?') + if value is None: + return True + + if '&' in value: + raise ValidationError( + 'Your input contains multiple key=value pairs (we found at ' + 'least one "&" character).
%s' % infomsg) + if 'user=' in value: + raise ValidationError( + 'Please remove the key "user" and the "=" character from ' + 'your input.
%s' % infomsg) + if value.startswith('='): + raise ValidationError( + 'Please remove leading "=" characters from your input.' + '
%s' % infomsg) + + def validator_rgate_id(form, field): + """A WTForm validator to check if user input follows ResearchGate + user names. + + Parameters + ---------- + form : wtforms.Form + The WTform form enclosing the user input field. + field : wtforms.StringField + The WTform user input field. + + Returns + ------- + True, if user input is OK. + + Raises + ------ + ValidationError if user input is not valid + """ + infomsg = ('To retrieve your ResearchGate ID, surf to your profile ' + 'and copy the URL in your browser. It might read like ' + 'https://www.researchgate.net/profile/Rob-Knight
' + 'Your ID is the part right of the last "/", in the example:' + ' "Rob-Knight"') + value = UserProfile.validate_general( + field.data, infomsg, 'https://www.researchgate.net/profile/') + if value is None: + return True + name = StringField("Name", [validators.required()]) affiliation = StringField("Affiliation") address = StringField("Address") @@ -28,6 +182,13 @@ class UserProfile(Form): receive_processing_job_emails = BooleanField( "Receive Processing Job Emails?") + social_orcid = StringField( + "ORCID", [validator_orcid_id], description="0000-0002-0975-9019") + social_googlescholar = StringField( + "Google Scholar", [validator_gscholar_id], description="_e3QL94AAAAJ") + social_researchgate = StringField( + "ResearchGate", [validator_rgate_id], description="Rob-Knight") + class UserProfileHandler(BaseHandler): """Displays user profile page and handles profile updates""" @@ -44,11 +205,11 @@ def post(self): msg = "" user = self.current_user action = self.get_argument("action") + form_data = UserProfile() if action == "profile": - # tuple of colmns available for profile + # tuple of columns available for profile # FORM INPUT NAMES MUST MATCH DB COLUMN NAMES not_str_fields = ('receive_processing_job_emails') - form_data = UserProfile() form_data.process(data=self.request.arguments) profile = {name: data[0].decode('ascii') if name not in not_str_fields else @@ -59,16 +220,19 @@ def post(self): for field in form_data: if field.name not in not_str_fields: field.data = field.data[0].decode('ascii') - try: - user.info = profile - msg = "Profile updated successfully" - except Exception as e: - msg = "ERROR: profile could not be updated" - LogEntry.create('Runtime', "Cound not update profile: %s" % - str(e), info={'User': user.id}) + if form_data.validate() is False: + msg = ("ERROR: profile could not be updated" + " as some of your above inputs must be corrected.") + else: + try: + user.info = profile + msg = "Profile updated successfully" + except Exception as e: + msg = "ERROR: profile could not be updated" + LogEntry.create('Runtime', "Cound not update profile: %s" % + str(e), info={'User': user.id}) elif action == "password": - form_data = UserProfile() form_data.process(data=user.info) oldpass = self.get_argument("oldpass") newpass = self.get_argument("newpass") diff --git a/qiita_pet/static/img/logo_social_googlescholar.png b/qiita_pet/static/img/logo_social_googlescholar.png new file mode 100644 index 0000000000000000000000000000000000000000..e9bd65a3acdd866b280f1aa5d1f67f9dd441646c GIT binary patch literal 7606 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 literal 0 HcmV?d00001 diff --git a/qiita_pet/static/img/logo_social_researchgate.png b/qiita_pet/static/img/logo_social_researchgate.png new file mode 100644 index 0000000000000000000000000000000000000000..23c283b5d408c7a165819fd2d5f7a18e453d2814 GIT binary patch literal 1317 zcmZ`%X;4#F6uvQe352DVmH?I_T0>>i5XLRi4qGayK}=HxwM^Iof>i88Dh_s1mR1Y|+Kb$%DoO{3bo$ovEy_+5w zu+NORfd~LF^Y`;*!fJ0=Mp&4Ai!z#F!9{s7yZ|V_VxkX43=hNom<#~UI{?5-1z-_E zyfFaIQUK7f0if{#u!_%raM%NYb-2KTEIK5B?lMGLipWcGvbTZAL=XXTfl-eLk(Cm9 z4SU=g0V^v7$|BG$!pTL1ZUJPz?UD*XUpY=Dghzs00J;keI27qC$4Z4*sR;j00g;tK zFN6NmyO0C=Z(*UkRD^}#y8+&6{%0iU3%#HYRwjf2q|giM3t@ovJUEQOt6c(V5Xb*3 z1v4~VLx5Bal$8b=?0sMOMyIw^RGb>^d_1eg z%S7M4Y_;vXcSSq!n^tKmziT2d+t4Q=5B#y?S@q7rn$7){Msm?*?LhX#Gp&B%_SEpn zkxu2p%z+zigLnWjp3pk)UoY0~ws1BN z`;`NS)H@Zwlayc{fInE*)Il;QoND)o?x{}Fqg#33c&-l)@!M^S6<@Sh(VPA%2WO1= z)kUn`N9xSc1;u6NJeze;chp#@-ysP7JebY2*WGMJ1mSz<(CNXtecm= z^V%PC?$`-hs;Iv1W(mTHjNn8@MUAM|<&`9&3rJemP#34`Cvir`f&FbyoQX~i4MIUf zLoBnPSxp`D3Z|1CQS|;i-dyh7#!pe%G!~Rj zX4%>IAMg1^6Xt^t+A^EV!nFRLWgPuu7$uqNkmGwOCf|c>$9T@K$Ca@)_tfs3LPCaP zip{kh!ia=(tC`b#qKWM`E;mc7$k$PH#uG;rM~IUvMpJC7*k~q+h4h+M|19Pm!)70V3Zp_yd{ViAV$&Rb$0rss z4xGl*7M##x-g*Q`9X8f(YmGex|16nH58_6IawBQs-$%j%s1z#2)s5=vwu?oj(%ju? pR5xb|g+`&o7q^7`uOaSq#7R!#`x`Lx@x!nI_|pS?%e>ji{{TS`eMA5N literal 0 HcmV?d00001 diff --git a/qiita_pet/templates/user_profile.html b/qiita_pet/templates/user_profile.html index b83efffad..66da290d9 100644 --- a/qiita_pet/templates/user_profile.html +++ b/qiita_pet/templates/user_profile.html @@ -14,9 +14,17 @@

User Information

{% for form_item in profile %} -
- - {% raw form_item(class_='form-control') %} +
+
+ {% if form_item.id.startswith('social_') %} + + {% end %} + {% raw form_item.label %} +
+ {% raw form_item(class_='form-control', placeholder=form_item.description) %} + {% if form_item.errors %} +
{% for e in form_item.errors %}{%raw e%}
{% end %}
+ {% end %}
{% end %}
{{msg}}
diff --git a/qiita_pet/test/test_user_handlers.py b/qiita_pet/test/test_user_handlers.py index b0724eba6..42fd46d8a 100644 --- a/qiita_pet/test/test_user_handlers.py +++ b/qiita_pet/test/test_user_handlers.py @@ -7,8 +7,11 @@ # ----------------------------------------------------------------------------- from unittest import main +from wtforms.validators import ValidationError +from wtforms import StringField from qiita_pet.test.tornado_test_base import TestHandlerBase +from qiita_pet.handlers.user_handlers import UserProfile class TestUserProfile(TestHandlerBase): @@ -17,7 +20,6 @@ class TestUserProfile(TestHandlerBase): class TestUserProfileHandler(TestHandlerBase): - def test_get(self): response = self.get('/profile/') self.assertEqual(response.code, 200) @@ -37,10 +39,84 @@ def test_post_profile(self): 'affiliation': ['NEWNAME'], 'address': ['ADDRESS'], 'name': ['TESTDUDE'], - 'phone': ['111-222-3333']} + 'phone': ['111-222-3333'], + 'social_orcid': [''], + 'social_googlescholar': [''], + 'social_researchgate': ['']} response = self.post('/profile/', post_args) self.assertEqual(response.code, 200) + def test_validators_social(self): + # None or empty should be valid + obs = UserProfile.validate_general(None, "", "") + self.assertEqual(obs, None) + obs = UserProfile.validate_general("", "", "") + self.assertEqual(obs, None) + + # having white spaces should raise errors + with self.assertRaises(ValidationError): + obs = UserProfile.validate_general(" infix", "", "") + with self.assertRaises(ValidationError): + obs = UserProfile.validate_general("infix ", "", "") + with self.assertRaises(ValidationError): + obs = UserProfile.validate_general(" infix ", "", "") + obs = UserProfile.validate_general("infix", "", "") + self.assertEqual(obs, 'infix') + + with self.assertRaises(ValidationError): + obs = UserProfile.validate_general( + "http://kurt.com/id1234", "msg", r"http://kurt.\w{1,3}/") + + def test_validator_orcid_id(self): + field = StringField("testfield") + + field.data = "0000-0002-0975-9019" + obs = UserProfile.validator_orcid_id(None, field) + self.assertEqual(obs, None) + + field.data = "https://orcid.org/0000-0002-0975-9019" + with self.assertRaises(ValidationError): + obs = UserProfile.validator_orcid_id(None, field) + + field.data = "wrong" + with self.assertRaises(ValidationError): + obs = UserProfile.validator_orcid_id(None, field) + + def test_validator_gscholar_id(self): + field = StringField("testfield") + + field.data = "_e3QL94AAAAJ" + obs = UserProfile.validator_gscholar_id(None, field) + self.assertEqual(obs, None) + + field.data = ('https://scholar.google.com/citations?user=_e3QL94AAAAJ&' + 'hl=en') + with self.assertRaises(ValidationError): + obs = UserProfile.validator_gscholar_id(None, field) + + field.data = 'user=_e3QL94AAAAJ&hl=en' + with self.assertRaises(ValidationError): + obs = UserProfile.validator_gscholar_id(None, field) + + field.data = 'user=_e3QL94AAAAJ' + with self.assertRaises(ValidationError): + obs = UserProfile.validator_gscholar_id(None, field) + + field.data = '=_e3QL94AAAAJ' + with self.assertRaises(ValidationError): + obs = UserProfile.validator_gscholar_id(None, field) + + def test_validator_rgate_id(self): + field = StringField("testfield") + + field.data = "Rob-Knight" + obs = UserProfile.validator_rgate_id(None, field) + self.assertEqual(obs, None) + + field.data = 'https://www.researchgate.net/profile/Rob-Knight' + with self.assertRaises(ValidationError): + obs = UserProfile.validator_rgate_id(None, field) + class TestUserJobsHandler(TestHandlerBase): def test_get(self):