Skip to content

Commit 8b3f717

Browse files
authored
log user creation timestamp (#3416)
* use SQL's NOW() fct to log creation of a new user account (validated or not). * at -> on * add new col * use patch for DB changes * forgot to remove addition of comment here * moving changes to patch 92 * move adding date from populate_test_db.sql to here * revert changes, see patch * adding table name :-/ * order in DB changed due to UPDATE of creation_timestamp for user [email protected] * fix test
1 parent cca6de7 commit 8b3f717

File tree

6 files changed

+69
-12
lines changed

6 files changed

+69
-12
lines changed

qiita_db/handlers/tests/test_user.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ def test_get(self):
5151

5252
obs = loads(obs.body)
5353
exp = {'data': [
54-
{'email': '[email protected]', 'name': 'Dude'},
5554
{'email': '[email protected]', 'name': 'Shared'},
5655
{'email': '[email protected]', 'name': 'Admin'},
57-
{'email': '[email protected]', 'name': 'Demo'}]}
56+
{'email': '[email protected]', 'name': 'Demo'},
57+
{'email': '[email protected]', 'name': 'Dude'}
58+
]}
5859
self.assertEqual(obs, exp)
5960

6061

qiita_db/support_files/patches/92.sql

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,16 @@ ALTER TABLE qiita.prep_template ADD current_human_filtering boolean DEFAULT Fals
3030
-- Adding a new column: reprocess_job_id to qiita.prep_template to keep track of
3131
-- the job that reprocessed this prep
3232
ALTER TABLE qiita.prep_template ADD reprocess_job_id uuid DEFAULT NULL;
33+
34+
-- Jun 19, 2024
35+
-- Adding a new column to the user table that logs when this account was created
36+
-- Usefull e.g. to prune non-verified=inactive user or to plot user growth
37+
38+
ALTER TABLE qiita.qiita_user
39+
ADD creation_timestamp timestamp without time zone DEFAULT NOW();
40+
41+
COMMENT ON COLUMN qiita.qiita_user.creation_timestamp IS 'The date the user account was created';
42+
43+
-- for testing: provide creation date for one of the existing users
44+
45+
UPDATE qiita.qiita_user SET creation_timestamp = '2015-12-03 13:52:42.751331-07' WHERE email = '[email protected]';

qiita_db/test/test_user.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ def setUp(self):
7575
'receive_processing_job_emails': True,
7676
'social_orcid': None,
7777
'social_researchgate': None,
78-
'social_googlescholar': None
78+
'social_googlescholar': None,
79+
'creation_timestamp': datetime(2015, 12, 3, 13, 52, 42, 751331)
7980
}
8081

8182
def tearDown(self):
@@ -88,7 +89,22 @@ def test_instantiate_unknown_user(self):
8889
with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError):
8990
qdb.user.User('[email protected]')
9091

91-
def _check_correct_info(self, obs, exp):
92+
def _check_correct_info(self, obs, exp, ts_before=None):
93+
"""Compares info dict of user with special handling of specific keys.
94+
95+
Parameters
96+
----------
97+
obs : dict
98+
Observed user info dictionary.
99+
exp : dict
100+
Expected user info dictionary.
101+
ts_before : datetime.datetime or None
102+
User.create records the creation timestamp through SQL's NOW().
103+
Since it is set by the database to the microsecond, we can't
104+
predict it a priori and therefore simply record timestamp before
105+
execution of user.create() and compare the relation.
106+
The DB creation_timestamp column is optional, i.e. can be None.
107+
"""
92108
self.assertEqual(set(exp.keys()), set(obs.keys()))
93109
for key in exp:
94110
# user_verify_code and password seed randomly generated so just
@@ -97,10 +113,14 @@ def _check_correct_info(self, obs, exp):
97113
self.assertEqual(len(obs[key]), 20)
98114
elif key == "password":
99115
self.assertEqual(len(obs[key]), 60)
116+
elif key == "creation_timestamp":
117+
self.assertTrue(((exp[key] is None) and (obs[key] is None))
118+
or (ts_before <= exp[key]))
100119
else:
101120
self.assertEqual(obs[key], exp[key])
102121

103122
def test_create_user(self):
123+
before = datetime.now()
104124
user = qdb.user.User.create('[email protected]', 'password')
105125

106126
# adding a couple of messages
@@ -131,8 +151,9 @@ def test_create_user(self):
131151
'email': '[email protected]',
132152
'social_orcid': None,
133153
'social_researchgate': None,
134-
'social_googlescholar': None}
135-
self._check_correct_info(obs, exp)
154+
'social_googlescholar': None,
155+
'creation_timestamp': datetime.now()}
156+
self._check_correct_info(obs, exp, before)
136157

137158
# Make sure new system messages are linked to user
138159
sql = """SELECT message_id FROM qiita.message_user
@@ -146,6 +167,7 @@ def test_create_user(self):
146167
qdb.util.clear_system_messages()
147168

148169
def test_create_user_info(self):
170+
before = datetime.now()
149171
user = qdb.user.User.create('[email protected]', 'password',
150172
self.userinfo)
151173
self.assertEqual(user.id, '[email protected]')
@@ -171,8 +193,9 @@ def test_create_user_info(self):
171193
'email': '[email protected]',
172194
'social_orcid': None,
173195
'social_researchgate': None,
174-
'social_googlescholar': None}
175-
self._check_correct_info(obs, exp)
196+
'social_googlescholar': None,
197+
'creation_timestamp': datetime.now()}
198+
self._check_correct_info(obs, exp, before)
176199

177200
def test_create_user_column_not_allowed(self):
178201
self.userinfo["email"] = "FAIL"
@@ -241,8 +264,20 @@ def test_get_info(self):
241264
'phone': '222-444-6789',
242265
'social_orcid': None,
243266
'social_researchgate': None,
244-
'social_googlescholar': None
267+
'social_googlescholar': None,
268+
'creation_timestamp': datetime(2015, 12, 3, 13, 52, 42, 751331)
245269
}
270+
271+
# test database is re-populated during testing several times.
272+
# Creation_timestamp depends on the percise timing of the repopulation,
273+
# i.e. we cannot predict its value. We just test that this date should
274+
# be within an hour and now. For the remainder of tests, we update
275+
# our expectation.
276+
self.assertTrue(datetime.now() - timedelta(hours=1) <
277+
self.user.info['creation_timestamp'] <
278+
datetime.now())
279+
expinfo['creation_timestamp'] = self.user.info['creation_timestamp']
280+
246281
self.assertEqual(self.user.info, expinfo)
247282

248283
def test_set_info(self):

qiita_db/test/test_util.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ def test_get_table_cols(self):
9393
exp = {"email", "user_level_id", "password", "name", "affiliation",
9494
"address", "phone", "user_verify_code", "pass_reset_code",
9595
"pass_reset_timestamp", "receive_processing_job_emails",
96-
"social_orcid", "social_researchgate", "social_googlescholar"}
96+
"social_orcid", "social_researchgate", "social_googlescholar",
97+
"creation_timestamp"}
9798
self.assertEqual(set(obs), exp)
9899

99100
def test_exists_table(self):

qiita_pet/handlers/user_handlers.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,9 @@ class UserProfileHandler(BaseHandler):
196196
def get(self):
197197
profile = UserProfile()
198198
profile.process(data=self.current_user.info)
199-
self.render("user_profile.html", profile=profile, msg="", passmsg="")
199+
self.render("user_profile.html", profile=profile, msg="", passmsg="",
200+
creation_timestamp=self.current_user.info[
201+
'creation_timestamp'])
200202

201203
@authenticated
202204
@execute_as_transaction
@@ -248,7 +250,9 @@ def post(self):
248250
else:
249251
passmsg = "Incorrect old password"
250252
self.render("user_profile.html", user=user.id, profile=form_data,
251-
msg=msg, passmsg=passmsg)
253+
msg=msg, passmsg=passmsg,
254+
creation_timestamp=self.current_user.info[
255+
'creation_timestamp'])
252256

253257

254258
class ForgotPasswordHandler(BaseHandler):

qiita_pet/templates/user_profile.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ <h3>User Information</h3>
2727
{% end %}
2828
</div>
2929
{% end %}
30+
{%if creation_timestamp is not None %}
31+
<div style="padding-left: 1em; padding-bottom: 1em; color: grey;">account created on {{creation_timestamp}}</div>
32+
{% end %}
3033
<div style="color:{% if msg.startswith('ERROR:') %}red{% else %}darkgreen{% end %};">{{msg}}</div>
3134
<button type="submit" class="btn btn-success">Save Edits</button>
3235
</form>

0 commit comments

Comments
 (0)