Commit 3f2554e9 authored by Romain Loth's avatar Romain Loth

esthetic (clarify comments and var names) + generalize passing db connection as arg

parent 9d295310
......@@ -68,7 +68,7 @@ def connect_db(config=REALCONFIG):
"""
Simple connection
TODO decide if we'll use one or multiple (<= atm multiple)
By default we use one new connection per function, but it can be passed to prevent that (in which case it should be closed at the end)
"""
return connect(
host=config['SQL_HOST'],
......@@ -79,11 +79,15 @@ def connect_db(config=REALCONFIG):
charset="utf8"
)
def doors_uid_to_luid(doors_uid):
def doors_uid_to_luid(doors_uid, cmx_db = None):
"""
Find corresponding luid
"""
db = connect_db()
if cmx_db:
db = cmx_db
else:
db = connect_db()
db_c = db.cursor()
stmt = """
......@@ -95,20 +99,25 @@ def doors_uid_to_luid(doors_uid):
luid = None
if n_rows > 1:
db.close()
if not cmx_db:
db.close()
raise ValueError("non unique doors_uid %s" % doors_uid)
elif n_rows == 1:
luid = db_c.fetchone()[0]
db.close()
if not cmx_db:
db.close()
return luid
def email_exists(email):
def email_exists(email, cmx_db = None):
"""
Tests if there is already a user with this email
"""
db = connect_db()
if cmx_db:
db = cmx_db
else:
db = connect_db()
db_c = db.cursor()
stmt = """
......@@ -119,7 +128,9 @@ def email_exists(email):
n_rows = db_c.execute(stmt)
exi_bool = (n_rows >= 1)
db.close()
if not cmx_db:
db.close()
return exi_bool
......@@ -270,23 +281,28 @@ def get_field_aggs(a_field,
return agg_rows
def rm_scholar(luid):
def rm_scholar(luid, cmx_db = None):
"""
Remove a scholar by id
(removals from sch_kw and sch_ht maps are triggered by cascade)
"""
db = connect_db()
if cmx_db:
db = cmx_db
else:
db = connect_db()
db_c = db.cursor()
stmt = 'DELETE FROM scholars WHERE luid = %s' % str(luid)
mlog("DEBUGSQL", "rm_scholar STATEMENT:\n-- SQL\n%s\n-- /SQL" % stmt)
dbresp = db_c.execute(stmt)
db.commit()
mlog('INFO', 'deleted user %i at his request' % int(luid))
db.close()
if not cmx_db:
db.close()
def get_full_scholar(uid):
def get_full_scholar(uid, cmx_db = None):
"""
uid : str
local user id aka luid
......@@ -295,10 +311,14 @@ def get_full_scholar(uid):
=> Retrieves one line from *scholars* table, with joined optional concatenated *affiliations*, *keywords* and *linked_ids*
=> Parse it all into a structured python user info dict
=> NB: None if user doesn't exist in comex_db (but may exist in doors db)
=> NB: None if user doesn't exist in cmx_db (but may exist in doors db)
"""
u_row = None
db = connect_db()
if cmx_db:
db = cmx_db
else:
db = connect_db()
db_c = db.cursor(DictCursor)
# one user + all linked infos concatenated in one row
......@@ -388,15 +408,20 @@ def get_full_scholar(uid):
if n_rows > 1:
raise IndexError("DB one_usr_stmt returned %i rows instead of 1 for user %s" % (n_rows, uid))
elif n_rows == 0:
mlog("WARNING", "DB get_full_scholar attempt got no rows for: %s" % uid)
urow_dict = db_c.fetchone()
# we won't use the connect
if not cmx_db:
db.close()
# break with None if no results
if urow_dict is None:
mlog("WARNING", "DB get_full_scholar attempt got no rows for: %s" % uid)
return None
# normal case: we got exactly 1 user
urow_dict = db_c.fetchone()
db.close()
# normal case <=> exactly one row
# Exemple data in urow_dict
# --------------------------
......@@ -406,15 +431,15 @@ def get_full_scholar(uid):
# 'home_url': 'http://localhost/regcomex/', 'hon_title': 'Student',
# 'initials': 'JFK', 'interests_text': 'Blablabla',
# 'job_looking_date': '2019_09_28T22:00:00.000Z',
# 'keywords': 'complex networks,complex systems,text mining,machine learning',
# 'keywords_nb': 4,
# 'hashtags': '#eccs15', 'hashtags_nb': 1,
# 'keywords': 'complex networks,complex systems,text mining,machine learning', 'keywords_nb': 4,
# 'last_modified_date': '2016-12-07T15:56:09.721Z',
# 'last_name': 'Kennedy',
# 'linked_ids': 'yoyo:42,foobar:XWING', 'linked_ids_nb': 2,
# 'linked_ids': 'twitter:@jfk,yoyo:42,foobar:XWING', 'linked_ids_nb': 3,
# 'middle_name': 'Fitzgerald',
# 'org': 'Centre National de la Recherche Scientifique (CNRS)',
# 'org_city': 'Paris', 'org_type': 'public R&D org',
# 'pic_fname': '12345.jpg', 'pic_url': None, 'position': 'Engineer',
# 'pic_fname': '12345.jpg', 'pic_url': None, 'position': 'Research Fellow',
# 'record_status': None, 'team_lab': 'ISCPIF'}
......@@ -458,7 +483,7 @@ def get_full_scholar(uid):
return urow_dict
def find_scholar(some_key, some_str_value):
def find_scholar(some_key, some_str_value, cmx_db = None):
"""
Get the luid of a scholar based on some str value
......@@ -466,7 +491,11 @@ def find_scholar(some_key, some_str_value):
but this function doesn't check it !
"""
luid = None
db = connect_db()
if cmx_db:
db = cmx_db
else:
db = connect_db()
db_c = db.cursor(DictCursor)
try:
......@@ -479,11 +508,14 @@ def find_scholar(some_key, some_str_value):
luid = first_row['luid']
except:
mlog('WARNING', 'unsuccessful attempt to identify a scholar on key %s' % some_key)
db.close()
if not cmx_db:
db.close()
return luid
def save_full_scholar(safe_recs, reg_db, uactive=True, update_user=None):
def save_full_scholar(safe_recs, cmx_db, uactive=True, update_user=None):
"""
For new registration:
-> add to *scholars* table, return new local uid
......@@ -544,7 +576,7 @@ def save_full_scholar(safe_recs, reg_db, uactive=True, update_user=None):
db_tgtcols.append('record_status')
db_qstrvals.append('"active"')
reg_db_c = reg_db.cursor()
cmx_db_c = cmx_db.cursor()
if not update_user:
# expected colnames "(doors_uid, last_modified_date, email, ...)"
......@@ -569,16 +601,16 @@ def save_full_scholar(safe_recs, reg_db, uactive=True, update_user=None):
mlog("DEBUG", "UPDATE" if update_user else "INSERT", "SQL statement:", full_statmt)
reg_db_c.execute(full_statmt)
cmx_db_c.execute(full_statmt)
if not update_user:
luid = reg_db_c.lastrowid
luid = cmx_db_c.lastrowid
else:
luid = update_user['luid']
reg_db.commit()
cmx_db.commit()
return luid
def update_scholar_cols(selected_safe_recs, reg_db, where_luid=None):
def update_scholar_cols(selected_safe_recs, cmx_db, where_luid=None):
"""
For modification of selected columns:
-> *update* row with the values that are present and are real columns
......@@ -612,7 +644,7 @@ def update_scholar_cols(selected_safe_recs, reg_db, where_luid=None):
db_tgtcols.append(colname)
db_qstrvals.append(quotedstrval)
reg_db_c = reg_db.cursor()
cmx_db_c = cmx_db.cursor()
set_full_str = ','.join([db_tgtcols[i] + '=' + db_qstrvals[i] for i in range(len(db_tgtcols))])
# UPDATE: full_statement with formated values
......@@ -620,35 +652,35 @@ def update_scholar_cols(selected_safe_recs, reg_db, where_luid=None):
set_full_str,
where_luid
)
reg_db_c.execute(full_statmt)
reg_db.commit()
cmx_db_c.execute(full_statmt)
cmx_db.commit()
return where_luid
def save_pairs_sch_tok(pairings_list, comex_db, map_table='sch_kw'):
def save_pairs_sch_tok(pairings_list, cmx_db, map_table='sch_kw'):
"""
Simply save all pairings (luid, kwid) or (luid, htid) in the list
"""
db_cursor = comex_db.cursor()
db_cursor = cmx_db.cursor()
for id_pair in pairings_list:
db_cursor.execute('INSERT INTO %s VALUES %s' % (map_table, str(id_pair)))
comex_db.commit()
cmx_db.commit()
mlog("DEBUG", "%s: saved %s pair" % (map_table, str(id_pair)))
def delete_pairs_sch_tok(uid, comex_db, map_table='sch_kw'):
def delete_pairs_sch_tok(uid, cmx_db, map_table='sch_kw'):
"""
Simply deletes all pairings (luid, *) in the table
"""
if map_table not in ['sch_kw', 'sch_ht']:
raise TypeError('ERROR: Unknown map_table')
db_cursor = comex_db.cursor()
db_cursor = cmx_db.cursor()
n = db_cursor.execute('DELETE FROM %s WHERE uid = "%s"' % (map_table, uid))
comex_db.commit()
cmx_db.commit()
mlog("DEBUG", "%s: DELETED %i pairings for %s" % (map_table, n, str(uid)))
def get_or_create_tokitems(tok_list, comex_db, tok_table='keywords'):
def get_or_create_tokitems(tok_list, cmx_db, tok_table='keywords'):
"""
kw_str -> lookup/add to *keywords* table -> kw_id
ht_str -> lookup/add to *hashtags* table -> ht_id
......@@ -675,7 +707,7 @@ def get_or_create_tokitems(tok_list, comex_db, tok_table='keywords'):
fill['idc'] = 'htid'
fill['strc']= 'htstr'
db_cursor = comex_db.cursor()
db_cursor = cmx_db.cursor()
found_ids = []
for tok_str in tok_list:
......@@ -695,7 +727,7 @@ def get_or_create_tokitems(tok_list, comex_db, tok_table='keywords'):
# ex: INSERT INTO keywords(kwstr) VALUES ("complexity")
db_cursor.execute('INSERT INTO %(tb)s(%(strc)s) VALUES ("%(q)s")' % fill)
comex_db.commit()
cmx_db.commit()
mlog("INFO", "Added '%s' to %s table" % (tok_str, tok_table))
......@@ -706,7 +738,7 @@ def get_or_create_tokitems(tok_list, comex_db, tok_table='keywords'):
return found_ids
def get_or_create_affiliation(org_info, comex_db):
def get_or_create_affiliation(org_info, cmx_db):
"""
(parent organization + lab) ---> lookup/add to *affiliations* table -> affid
......@@ -744,7 +776,7 @@ def get_or_create_affiliation(org_info, comex_db):
else:
db_constraints.append("%s IS NULL" % colname)
db_cursor = comex_db.cursor()
db_cursor = cmx_db.cursor()
n_matched = db_cursor.execute(
'SELECT affid FROM affiliations WHERE %s' %
......@@ -764,7 +796,7 @@ def get_or_create_affiliation(org_info, comex_db):
)
)
the_aff_id = db_cursor.lastrowid
comex_db.commit()
cmx_db.commit()
mlog("DEBUG", "Added affiliation '%s'" % str(db_qstrvals))
else:
raise Exception("ERROR: non-unique affiliation '%s'" % str(db_qstrvals))
......@@ -775,32 +807,44 @@ def get_or_create_affiliation(org_info, comex_db):
# for users coming in from doors with no profile yet, we keep their doors infos (email, also name in the future)
def save_doors_temp_user(doors_uid, doors_email):
db = connect_db()
def save_doors_temp_user(doors_uid, doors_email, cmx_db = None):
if cmx_db:
db = cmx_db
else:
db = connect_db()
db_c = db.cursor()
stmt = "INSERT IGNORE INTO doors_temp_user(doors_uid, email) VALUES (%s,%s)"
db_c.execute(stmt, (doors_uid, doors_email))
db.commit()
db.close()
if not cmx_db:
db.close()
def get_doors_temp_user(doors_uid):
def get_doors_temp_user(doors_uid, cmx_db = None):
info_row = None
db = connect_db()
if cmx_db:
db = cmx_db
else:
db = connect_db()
db_c = db.cursor(DictCursor)
db_c.execute('''SELECT *
FROM doors_temp_user
WHERE doors_uid = "%s"''' % doors_uid)
info_row = db_c.fetchone()
db.close()
if not cmx_db:
db.close()
return info_row
def rm_doors_temp_user(doors_uid):
db = connect_db()
def rm_doors_temp_user(doors_uid, cmx_db = None):
if cmx_db:
db = cmx_db
else:
db = connect_db()
db_c = db.cursor()
db_c.execute('''DELETE FROM doors_temp_user
WHERE doors_uid = "%s"''' % doors_uid)
db.commit()
db.close()
if not cmx_db:
db.close()
# another temp table, for the secret return tokens
......
......@@ -98,9 +98,9 @@ SOURCE_FIELDS = [
("hon_title", True, None),
("interests_text", True, None),
("gender", False, None), # M|F
("job_looking_date", True, "date"), # def null: not looking for a job
("home_url", True, "url"), # scholar's homepage
("pic_url", True, "url"),
("job_looking_date", True, "sdate"), # def null: not looking for a job
("home_url", True, "surl"), # scholar's homepage
("pic_url", True, "surl"),
("pic_file", False, None), # saved separately
# => for *scholars* table (optional)
......@@ -132,13 +132,13 @@ def inject_doors_params():
-> 'doors_connect'
(base_layout-rendered templates need it for login popup)
"""
if 'DOORS_PORT' not in config or config['DOORS_PORT'] in ['', '80', '443']:
if 'DOORS_PORT' not in config or config['DOORS_PORT'] in ['80', '443']:
context_dict = dict(
doors_connect= config['DOORS_HOST']
)
else:
context_dict = dict(
doors_connect= config['DOORS_HOST']
doors_connect= config['DOORS_HOST']+':'+config['DOORS_PORT']
)
return context_dict
......@@ -252,10 +252,11 @@ def user_api():
implemented "op" <=> verbs:
exists => bool
"""
if 'op' in request.args and request.args['op'] == "exists":
if 'email' in request.args:
email = sanitize(request.args['email'])
return(dumps({'exists':db.email_exists(email)}))
if 'op' in request.args:
if request.args['op'] == "exists":
if 'email' in request.args:
email = sanitize(request.args['email'])
return(dumps({'exists':db.email_exists(email)}))
else:
raise TypeError("user API query is missing the operation to perform (eg op=exists)")
......@@ -275,7 +276,7 @@ def login():
"login.html"
)
elif request.method == 'POST':
mlog("DEBUG", "login form received from "+request.path+", with keys:", [k for k in request.values])
mlog("DEBUG", "LOGIN: form received from "+request.path+", with keys:", [k for k in request.values])
# we used this custom header to mark ajax calls => called_as_api True
x_req_with = request.headers.get('X-Requested-With', type=str)
......@@ -309,10 +310,10 @@ def login():
try:
doors_uid = doors_login(email, pwd, config)
except Exception as err:
mlog("ERROR", "error in doors_login request")
mlog("ERROR", "LOGIN: error in doors_login request")
raise (err)
mlog("DEBUG", "doors_login returned doors_uid '%s'" % doors_uid)
mlog("DEBUG", "user.doors_login() returned doors_uid '%s'" % doors_uid)
if doors_uid is None:
# break: can't doors_login
......@@ -332,6 +333,7 @@ def login():
# normal user
user = User(luid)
else:
mlog("DEBUG", "LOGIN: encountered new doors id (%s), switching to empty user profile" % doors_uid)
# user exists in doors but has no comex profile nor luid yet
db.save_doors_temp_user(doors_uid, email) # preserve the email
user = User(None, doors_uid=doors_uid) # get a user.empty
......@@ -354,7 +356,7 @@ def login():
if not login_ok:
# break: failed to login_user()
notok_message = "There was an unknown problem with the login."
notok_message = "LOGIN There was an unknown problem with the login."
if called_as_api:
# menubar login will prevent redirect
return(nologin_message, 404)
......@@ -373,7 +375,7 @@ def login():
elif user.empty:
mlog('DEBUG',"empty user redirected to profile")
# we go straight to profile for the him to create infos
# we go straight to empty profile for the person to create infos
return(redirect(url_for('profile', _external=True)))
# normal call, normal user
......@@ -392,7 +394,7 @@ def login():
# if relative
if next_url[0] == '/':
next_url = url_for('rootindex', _external=True) + next_url[1:]
mlog("DEBUG", "reabsoluted next_url:", next_url)
mlog("DEBUG", "LOGIN: reabsoluted next_url:", next_url)
return(redirect(next_url))
else:
......@@ -572,7 +574,10 @@ def claim_profile():
luid = request.form['return_user_luid']
return_user = User(luid)
name = return_user.info.get('last_name')+', '+return_user.info.get('first_name', '')+' '+return_user.info.get('middle_name', '')
info = return_user.info
name = info['last_name']+', '+info['first_name']
if info['middle_name']:
name += ' '+info['middle_name']
# we do our doors request here server-side to avoid MiM attack on result
try:
......@@ -679,7 +684,7 @@ def register():
return render_template(
"thank_you.html",
debug_records = (clean_records if app.config['DEBUG'] else {}),
form_accepted = True,
form_accepted = form_accepted,
backend_error = False,
message = """
You can now visit elements of the members section:
......@@ -858,9 +863,9 @@ def sanitize(value, specific_type=None):
if not specific_type:
san_val = sub(r'[^\w@\.:,()# -]', '_', clean_val)
elif specific_type == "url":
elif specific_type == "surl":
san_val = sub(r'[^\w@\.: -/]', '_', clean_val)
elif specific_type == "date":
elif specific_type == "sdate":
san_val = sub(r'[^0-9/-:]', '_', clean_val)
if vtype not in [int, str]:
......
......@@ -79,8 +79,7 @@ class User(object):
doors but not in db)
=> no luid, but has doors_uid
This also causes trickier behaviour for get_id:
ie load_user() wants a *single id for both*,
NB load_user() wants a *single id for both*,
which is provided by self.get_id()
"""
mlog('DEBUG',
......@@ -225,7 +224,6 @@ def doors_login(email, password, config=REALCONFIG):
http_scheme = "https:"
# (TODO generalize this logic)
if config['DOORS_PORT'] in ['80', '443']:
# implicit port
doors_base_url = http_scheme + '//'+config['DOORS_HOST']
......@@ -276,6 +274,7 @@ def doors_register(email, password, name, config=REALCONFIG):
# eg doors_response.content = b'{"status":"registration email sent",
# "email":"john@locke.com"}''
answer = loads(doors_response.content.decode())
mlog("INFO", "/api/register answer",answer)
return answer['userID']
else:
return None
......@@ -71,7 +71,7 @@ cmxClt = (function(cC) {
// -> interaction elements (params, else default)
var emailId, duuidId, passId, pass2Id, captchaId, capcheckId
console.info('new AuthForm "'+auForm.id+'"[.type='+auForm.type+'] init params', afParams)
// console.info('new AuthForm "'+auForm.id+'"[.type='+auForm.type+'] init params', afParams)
emailId = afParams.emailId || 'email'
duuidId = afParams.duuidId || 'doors_uid'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment