Commit a8ac0399 authored by Romain Loth's avatar Romain Loth

adapt dbcrud for new org table structure (profile, registration)

parent 326c2ed8
...@@ -25,19 +25,23 @@ SELECT * FROM ( ...@@ -25,19 +25,23 @@ SELECT * FROM (
FROM scholars FROM scholars
LEFT JOIN sch_org AS map_labs LEFT JOIN sch_org AS map_labs
ON map_labs.uid = luid ON map_labs.uid = luid
JOIN orgs AS labs LEFT JOIN (
-- class constraint can't appear later,
-- it would give no scholar when empty
SELECT * FROM orgs WHERE class='lab'
) AS labs
ON map_labs.orgid = labs.orgid ON map_labs.orgid = labs.orgid
WHERE (record_status = 'active' WHERE (record_status = 'active'
OR (record_status = 'legacy' AND valid_date >= NOW())) OR (record_status = 'legacy' AND valid_date >= NOW()))
AND labs.class = 'lab'
GROUP BY luid GROUP BY luid
) AS scholars_and_labs ) AS scholars_and_labs
LEFT JOIN sch_org AS map_insts LEFT JOIN sch_org AS map_insts
ON map_insts.uid = luid ON map_insts.uid = luid
JOIN orgs AS insts LEFT JOIN (
SELECT * FROM orgs WHERE class='inst'
) AS insts
ON map_insts.orgid = insts.orgid ON map_insts.orgid = insts.orgid
AND insts.class = 'inst'
GROUP BY luid GROUP BY luid
) AS scholars_and_orgs ) AS scholars_and_orgs
......
-- we pass through scholars
-- org1 => scholars => orgs2
-- (for suggestions and/or than mapping)
SELECT orgs.*,
GROUP_CONCAT( tgt_tostring ORDER BY tgt_freq DESC SEPARATOR '%%%')
AS related_insts
FROM orgs
LEFT JOIN (
SELECT sch_org.orgid AS src_orgid,
sch_org2.orgid AS tgt_orgid,
orgs2.tostring AS tgt_tostring,
count(*) AS tgt_freq
FROM sch_org
LEFT JOIN sch_org AS sch_org2
ON sch_org.uid = sch_org2.uid
JOIN orgs AS orgs2
ON sch_org2.orgid = orgs2.orgid
WHERE orgs2.class = 'inst'
AND sch_org.orgid != sch_org2.orgid
GROUP BY sch_org.orgid, sch_org2.orgid
) AS lab_relationship_to_inst_via_scholars ON src_orgid = orgs.orgid
WHERE orgs.orgid IN ( {$ids_str} )
AND orgs.name != '_NULL'
GROUP BY orgs.orgid
ORDER BY orgs.name, orgs.acro
;
-- a POSSible alternative would be create an org_org tabls
-- relationship organizations <=> organizations
-- formally many-to-many but one could say many-to-few :)
CREATE TABLE org_org(
orgid_src int(15) not null, -- @class 'lab'
orgid_tgt int(15) not null, -- @class 'inst'
sch_freq int(15) default 0, -- how often declared in sch records
-- (useful if unsure main parent org)
PRIMARY KEY (orgid_src, orgid_tgt),
FOREIGN KEY (orgid_src) REFERENCES orgs(orgid) ON DELETE CASCADE,
FOREIGN KEY (orgid_tgt) REFERENCES orgs(orgid) ON DELETE CASCADE
);
-- NB +/-1 to org -> org freq in org_org would be triggered indirectly by new scholars rows so made in profile saving at middle-ware lvl (dbcrud.py)
## dev overview ## dev overview
comex app contains: comex app contains:
...@@ -34,13 +33,10 @@ cd $INSTALL_DIR ...@@ -34,13 +33,10 @@ cd $INSTALL_DIR
sudo pip3 install -r setup/requirements.txt sudo pip3 install -r setup/requirements.txt
``` ```
Then to run the comex2 services in the simplest way just do: Then to run the comex2 server just do:
``` ```
cd services bash comex-run.sh
python3 comex_main_backend.py
``` ```
The form server is then accessible locally on `0.0.0.0:5000/services/user`
The tina api server is on `0.0.0.0:5000/services/api`
Check the parameters in `config/parametres_comex.ini` Check the parameters in `config/parametres_comex.ini`
...@@ -49,7 +45,7 @@ Finally, simply configure the serving of your php|www documentroot in nginx (cf ...@@ -49,7 +45,7 @@ Finally, simply configure the serving of your php|www documentroot in nginx (cf
------- -------
#### Advanced dev config #### Full dev config
1. external mysql database 1. external mysql database
2. external doors (or simulated by docker) 2. external doors (or simulated by docker)
3. gunicorn webserver (linked to 1 & 2 via `$SQL_HOST` and `$DOORS_HOST`) 3. gunicorn webserver (linked to 1 & 2 via `$SQL_HOST` and `$DOORS_HOST`)
...@@ -92,15 +88,8 @@ nano config/parametres_comex.ini ...@@ -92,15 +88,8 @@ nano config/parametres_comex.ini
###### If you have no doors server ###### If you have no doors server
For tests you can use a `minidoors` container For tests you can use a self-deployed doors container, available on [this repository](https://github.com/ISCPIF/doors-docker)
```
# build the docker image (once)
cd setup/dockers
docker build -t minidoors:latest minidoors/
# run the container (each time)
docker run -it -p 32789:8989 --name doors_test minidoors
```
##### 3) Run the regomex app with gunicorn ##### 3) Run the regomex app with gunicorn
``` ```
......
...@@ -46,7 +46,6 @@ CREATE TABLE scholars ( ...@@ -46,7 +46,6 @@ CREATE TABLE scholars (
) ; ) ;
CREATE TABLE locs( CREATE TABLE locs(
locname varchar(120), locname varchar(120),
lat float(6,4), lat float(6,4),
...@@ -54,12 +53,11 @@ CREATE TABLE locs( ...@@ -54,12 +53,11 @@ CREATE TABLE locs(
PRIMARY KEY (locname) PRIMARY KEY (locname)
) ; ) ;
-- table for all organization classes (team, lab, large institution) -- table for all organization classes (team, lab, large institution)
CREATE TABLE orgs( CREATE TABLE orgs(
orgid int(15) not null auto_increment, orgid int(15) not null auto_increment,
name varchar(120), -- full name name varchar(120), -- full name
acro varchar(20), -- acronym or short name acro varchar(30), -- acronym or short name
class varchar(25), -- "team|lab|inst" class varchar(25), -- "team|lab|inst"
-- like the calibre of the organization -- like the calibre of the organization
...@@ -78,15 +76,14 @@ CREATE TABLE orgs( ...@@ -78,15 +76,14 @@ CREATE TABLE orgs(
-- address... (...) -- address elements POSS NOT IMPLEMENTED -- address... (...) -- address elements POSS NOT IMPLEMENTED
reserved varchar(30), reserved varchar(30),
-- generated column, often useful for autocompletes etc -- tostring: generated column
-- ex "Instituto de Fisica de Cantabria (IFCA), Santander, Spain" -- ex "Instituto de Fisica de Cantabria (IFCA), Santander, Spain"
tostring varchar(800) AS (CONCAT( -- searchable + human readable, often useful for autocompletes etc
name, ' (', acro, ')', tostring varchar(800)
IF(locname IS NOT NULL , AS (CONCAT_WS( '',
CONCAT(', ', locname), CONCAT(name, ' '),
'') CONCAT('(',acro,')'),
)), CONCAT(', ', locname)) ),
PRIMARY KEY (orgid), PRIMARY KEY (orgid),
UNIQUE KEY full_org (name, acro, locname) UNIQUE KEY full_org (name, acro, locname)
...@@ -106,6 +103,7 @@ CREATE TABLE sch_org( ...@@ -106,6 +103,7 @@ CREATE TABLE sch_org(
-- POSS: relationship organizations <=> keywords -- POSS: relationship organizations <=> keywords
-- POSS: relationship organizations <=> organizations -- POSS: relationship organizations <=> organizations
-- cf. doc/data_mining_exemples/org_to_orgs.sql
-- keyword/subject terms -- keyword/subject terms
......
...@@ -303,19 +303,21 @@ SELECT * FROM ( ...@@ -303,19 +303,21 @@ SELECT * FROM (
FROM scholars FROM scholars
LEFT JOIN sch_org AS map_labs LEFT JOIN sch_org AS map_labs
ON map_labs.uid = luid ON map_labs.uid = luid
JOIN orgs AS labs LEFT JOIN (
SELECT * FROM orgs WHERE class='lab'
) AS labs
ON map_labs.orgid = labs.orgid ON map_labs.orgid = labs.orgid
WHERE (record_status = 'active' WHERE (record_status = 'active'
OR (record_status = 'legacy' AND valid_date >= NOW())) OR (record_status = 'legacy' AND valid_date >= NOW()))
AND labs.class = 'lab'
GROUP BY luid GROUP BY luid
) AS scholars_and_labs ) AS scholars_and_labs
LEFT JOIN sch_org AS map_insts LEFT JOIN sch_org AS map_insts
ON map_insts.uid = luid ON map_insts.uid = luid
JOIN orgs AS insts LEFT JOIN (
SELECT * FROM orgs WHERE class='inst'
) AS insts
ON map_insts.orgid = insts.orgid ON map_insts.orgid = insts.orgid
AND insts.class = 'inst'
GROUP BY luid GROUP BY luid
) AS scholars_and_orgs ) AS scholars_and_orgs
......
This diff is collapsed.
...@@ -25,7 +25,7 @@ __status__ = "Dev" ...@@ -25,7 +25,7 @@ __status__ = "Dev"
# ============== imports ============== # ============== imports ==============
from re import sub from re import sub, match
from os import path, remove from os import path, remove
from json import dumps from json import dumps
from datetime import timedelta from datetime import timedelta
...@@ -101,12 +101,12 @@ SOURCE_FIELDS = [ ...@@ -101,12 +101,12 @@ SOURCE_FIELDS = [
("pic_file", False, None), # saved separately ("pic_file", False, None), # saved separately
# => for *scholars* table (optional) # => for *scholars* table (optional)
("org", True, None), ("lab_label", True, None), # ~ /acro (name)/
("org_type", False, None), # predefined values ("lab_locname", True, None), # 'Paris, France'
( "other_org_type", True, None), # +=> org_type ("inst_label", True, None), # ~ /acro (name)/
("team_lab", True, None), ("inst_type", False, None), # predefined values
("org_city", True, None), ( "other_inst_type", True, None), # +=> org_type
# => for *affiliations* table # => for *orgs* table via sort_affiliation_records
("keywords", True, None), ("keywords", True, None),
# => for *keywords* table (after split str) # => for *keywords* table (after split str)
...@@ -752,6 +752,84 @@ def show_privacy(): ...@@ -752,6 +752,84 @@ def show_privacy():
########### SUBS ########### ########### SUBS ###########
def sort_affiliation_records(clean_records):
"""
Transform GUI side input data into at most 2 orgs objects for DB
In general:
1) the front-end inputs are less free than the DB structure
(DB could save an array of orgids but in the inputs they're only allowed max 2 atm : lab and inst)
2) each org has its microstructure:
- name, acronym, class, location (base properties)
- inst_type (specific to institutions)
- lab_code, url, contact <= not fillable in GUI yet
3) between themselves 2 orgs can have org_org relationships
TODO LATER (not a priority)
4) we want at least one of lab_label or inst_label to be NOT NULL
Choices:
- values are already sanitized by read_record_from_request
- We call label the concatenated name + acronym information,
handling here the possibilities for the input via str analysis
(just short name, just long name, both)
- We return a map with 2 key/value submaps for lab and institutions
"""
new_orgs = {'lab': None, 'inst': None}
for org_class in new_orgs:
# can't create org without some kind of label
if (org_class+"_label" not in clean_records
or not len(clean_records[org_class+"_label"])):
pass
else:
# submap
new_org_info = {}
# 1) label analysis
clean_input = clean_records[org_class+"_label"]
# custom split attempt
# eg 'CNRS (Centre National de la Recherche Scientifique)'
# vvvv vvvvvvvvvv
# acro name
test_two_groups = match(
r'([^\(]{1,30}) \(([^\)]+)\)',
clean_input
)
if test_two_groups:
new_org_info['acro'] = test_two_groups.groups()[0]
new_org_info['name'] = test_two_groups.groups()[1]
# fallback cases
elif len(clean_input) < 30:
new_org_info['acro'] = clean_input
else:
new_org_info['name'] = clean_input
# 2) enrich with any other optional org info
for detail_col in ['type', 'code', 'locname',
'url', 'contact_email', 'contact_name']:
# this is a convention in our templates
org_detail = org_class + '_' + detail_col
if org_detail in clean_records:
val = clean_records[org_detail]
if len(val):
new_org_info[detail_col] = val
# 3) keep
new_orgs[org_class] = new_org_info
return new_orgs
def save_form(clean_records, update_flag=False, previous_user_info=None): def save_form(clean_records, update_flag=False, previous_user_info=None):
""" """
wrapper function for save profile/register (all DB-related form actions) wrapper function for save profile/register (all DB-related form actions)
...@@ -767,11 +845,23 @@ def save_form(clean_records, update_flag=False, previous_user_info=None): ...@@ -767,11 +845,23 @@ def save_form(clean_records, update_flag=False, previous_user_info=None):
# A) a new DB connection # A) a new DB connection
reg_db = dbcrud.connect_db(config) reg_db = dbcrud.connect_db(config)
# B) read/fill the affiliation table to get associated id # B1) re-group the org fields into at most 2 org 'objects'
clean_records['affiliation_id'] = dbcrud.get_or_create_affiliation( declared_orgs = sort_affiliation_records(clean_records)
clean_records,
reg_db # B2) check our constraint (cf. also E.)
) if (declared_orgs['lab'] is None or declared_orgs['inst'] is None):
raise ValueError("At least 1 org (lab or institution) must be filled")
# B3) for each, read/fill the orgs table to get associated id(s) in DB
orgids = []
for oclass in ['lab', 'inst']:
if (declared_orgs[oclass]):
orgids.append(
dbcrud.get_or_create_org(declared_orgs[oclass], reg_db)
)
# B4) save the org <=> org mappings TODO LATER (not a priority)
# dbcrud.record_org_org_link(src_orgid, tgt_orgid, reg_db)
# C) create/update record into the primary user table # C) create/update record into the primary user table
# ---------------------------------------------------- # ----------------------------------------------------
...@@ -824,6 +914,10 @@ def save_form(clean_records, update_flag=False, previous_user_info=None): ...@@ -824,6 +914,10 @@ def save_form(clean_records, update_flag=False, previous_user_info=None):
map_table map_table
) )
# E) save the (uid <=> orgid) mapping(s)
for orgid in orgids:
dbcrud.record_sch_org_link(luid, orgid, reg_db)
# F) end connection # F) end connection
reg_db.close() reg_db.close()
...@@ -872,9 +966,9 @@ def read_record_from_request(request): ...@@ -872,9 +966,9 @@ def read_record_from_request(request):
clean_records[field] = request.form[field] clean_records[field] = request.form[field]
# special treatment for "other" subquestions # special treatment for "other" subquestions
if 'org_type' in clean_records: if 'inst_type' in clean_records:
if clean_records['org_type'] == 'other' and 'other_org_type' in clean_records: if clean_records['inst_type'] == 'other' and 'other_inst_type' in clean_records:
clean_records['org_type'] = clean_records['other_org_type'] clean_records['inst_type'] = clean_records['other_inst_type']
# splits for kw_array and ht_array # splits for kw_array and ht_array
for tok_field in ['keywords', 'hashtags']: for tok_field in ['keywords', 'hashtags']:
......
...@@ -140,3 +140,162 @@ class CountryConverter: ...@@ -140,3 +140,162 @@ class CountryConverter:
self.connDBLP.close() self.connDBLP.close()
return fails return fails
#! /usr/bin/python3
from re import sub
from sys import stdin, stderr
# settings
dont_touch_first_column = False
NCOLS = 1
# functions
def normalize_chars(my_str, rm_qt=False):
"""
Simplification des chaînes de caractères en entrée de la BDD
- normalisation
> espaces
> tirets
> guillemets
- déligatures
Goal: normalize input values more like ascii will be easier to process
"""
# print('normalize_chars IN: "%s"' % my_str)
# --------------
# E S P A C E S
# --------------
# tous les caractères de contrôle (dont \t = \x{0009} et \r = \x{000D}) --> espace
my_str = sub(r'[\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000B\u000C\u000D\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\u007F]', ' ', my_str)
# mais pas \n = \x{000A}
# Line separator
my_str = sub(r'\u2028',' ', my_str)
my_str = sub(r'\u2029',' ', my_str)
# U+0092: parfois quote parfois cara de contrôle
my_str = sub(r'\u0092', ' ', my_str)
# tous les espaces alternatifs --> espace
my_str = sub(r'[\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u202F\u205F\u3000\uFEFF]', ' ' , my_str)
# quelques puces courantes (bullets)
my_str = sub(r'^\s+►', ' • ', my_str)
my_str = sub(r'^\s+●', ' • ', my_str)
my_str = sub(r'^\s+◘', ' • ', my_str)
my_str = sub(r'^\s+→', ' • ', my_str)
my_str = sub(r'^\s+▪', ' • ', my_str)
my_str = sub(r'^\s+·', ' • ', my_str)
my_str = sub(r'^\s+\*', ' • ', my_str)
# pour finir on enlève les espaces en trop
# (dits "trailing spaces")
my_str = sub(r' +', ' ', my_str)
my_str = sub(r'^ +', '', my_str)
my_str = sub(r' +$', '', my_str)
# ------------------------
# P O N C T U A T I O N S
# ------------------------
# la plupart des tirets alternatifs --> tiret normal (dit "du 6")
# (dans l'ordre U+002D U+2010 U+2011 U+2012 U+2013 U+2014 U+2015 U+2212 U+FE63)
my_str = sub(r'[‐‑‒–—―−﹣]','-', my_str)
# le macron aussi parfois comme tiret
my_str = sub(r'\u00af','-', my_str)
# Guillemets
# ----------
# la plupart des quotes simples --> ' APOSTROPHE
my_str = sub(r"[‘’‚`‛]", "'", my_str) # U+2018 U+2019 U+201a U+201b
my_str = sub(r'‹ ?',"'", my_str) # U+2039 plus espace éventuel après
my_str = sub(r' ?›',"'", my_str) # U+203A plus espace éventuel avant
# la plupart des quotes doubles --> " QUOTATION MARK
my_str = sub(r'[“”„‟]', '"', my_str) # U+201C U+201D U+201E U+201F
# my_str = sub(r'« ?', '"', my_str) # U+20AB plus espace éventuel après
# my_str = sub(r' ?»', '"', my_str) # U+20AB plus espace éventuel avant
# deux quotes simples (préparées ci-dessus) => une double
my_str = sub(r"''", '"', my_str)
# if we need to remove single quotes
if rm_qt:
my_str = sub(r"'", '"', my_str)
# print('normalize_chars OUT: "%s"' % my_str)
return my_str
def normalize_forms(term_str, do_lowercase=False):
"""
Removes unwanted trailing punctuation
AND optionally puts everything to lowercase
ex /©""ecosystem services"";/ => /"ecosystem services"/
(benefits from normalize_chars upstream so there's less cases to consider)
largely inadequate to the enormity of the task
"""
# print('normalize_forms IN: "%s"' % term_str)
term_str = sub(r'^[,; ©]+', '', term_str)
term_str = sub(r'[,; ©]+$', '', term_str)
term_str = sub(r'"+', '"', term_str)
term_str = sub(r'/+', '/', term_str)
term_str = sub(r"'+", "'", term_str)
if do_lowercase:
term_str = term_str.lower()
# print('normalize_forms OUT: "%s"' % term_str)
return term_str
if __name__ == "__main__":
for i, line in enumerate(stdin):
fields = line.rstrip().split('\t')
if len(fields) > NCOLS:
print ("skipping line %i (%s)" % (i, fields), file=stderr)
continue
if dont_touch_first_column:
# some ID supposed in 1st col => kept unchanged
clean_fields = [fields[0]]
todo_fields = fields[1:]
else:
# normalize in all columns
clean_fields = []
todo_fields = fields
for field in todo_fields:
clean_lines = []
last_line = None
for line in field.split('%%%'):
# print(">> (doing line)", line)
clean_line = normalize_forms(normalize_chars(line))
if clean_line == '' and last_line == '':
last_line = clean_line
continue
else:
clean_lines.append(normalize_forms(normalize_chars(line)))
last_line = clean_line
# remove trailing lines
# TODO test if instead s/(?:%%%)+$// on clean_fields later is faster
for i in range(len(clean_lines)-1, 0, -1):
if not len(clean_lines[i]):
clean_lines.pop()
else:
break
clean_fields.append('%%%'.join(clean_lines))
# OUTPUT
print("\t".join(clean_fields))
...@@ -69,15 +69,14 @@ CREATE TABLE orgs( ...@@ -69,15 +69,14 @@ CREATE TABLE orgs(
-- address... (...) -- address elements POSS NOT IMPLEMENTED -- address... (...) -- address elements POSS NOT IMPLEMENTED
reserved varchar(30), reserved varchar(30),
-- generated column, often useful for autocompletes etc -- tostring: generated column
-- ex "Instituto de Fisica de Cantabria (IFCA), Santander, Spain" -- ex "Instituto de Fisica de Cantabria (IFCA), Santander, Spain"
tostring varchar(800) AS (CONCAT( -- searchable + human readable, often useful for autocompletes etc
name, ' (', acro, ')', tostring varchar(800)
IF(locname IS NOT NULL , AS (CONCAT_WS( '',
CONCAT(', ', locname), CONCAT(name, ' '),
'') CONCAT('(',acro,')'),
)), CONCAT(', ', locname)) ),
PRIMARY KEY (orgid), PRIMARY KEY (orgid),
UNIQUE KEY full_org (name, acro, locname) UNIQUE KEY full_org (name, acro, locname)
...@@ -97,6 +96,7 @@ CREATE TABLE sch_org( ...@@ -97,6 +96,7 @@ CREATE TABLE sch_org(
-- POSS: relationship organizations <=> keywords -- POSS: relationship organizations <=> keywords
-- POSS: relationship organizations <=> organizations -- POSS: relationship organizations <=> organizations
-- cf. doc/data_mining_exemples/org_to_orgs.sql
-- keyword/subject terms -- keyword/subject terms
......
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
*/ */
// 3 exposed vars for inline js controls // 3 exposed vars for inline js controls
var teamCityDiv = document.getElementById('team_city_div') var teamCityDiv = document.getElementById('lab_locname_div')
var otherInstDiv = document.getElementById('other_org_div') var otherInstDiv = document.getElementById('other_inst_div')
// TODO make relative to org_type and move inline snippet to extended form obj // TODO make relative to inst_type and move inline snippet to extended form obj
var otherOrgTypeInput = document.getElementById('other_org_type') var otherOrgTypeInput = document.getElementById('other_inst_type')
// reselecting current_user's info choices // reselecting current_user's info choices
function setupSavedItems(uinfo) { function setupSavedItems(uinfo) {
......
...@@ -39,8 +39,8 @@ var validateWithMessage = false ...@@ -39,8 +39,8 @@ var validateWithMessage = false
var shortRegVersion = true var shortRegVersion = true
var ignoredFields = [] var ignoredFields = []
if (shortRegVersion) { if (shortRegVersion) {
ignoredFields = ['gender', 'home_url', 'org', ignoredFields = ['gender', 'home_url', 'inst_label',
'hon_title', 'position', 'org_type', 'hon_title', 'position', 'inst_type',
'hashtags'] 'hashtags']
} }
...@@ -68,7 +68,7 @@ function testAsYouGo() { ...@@ -68,7 +68,7 @@ function testAsYouGo() {
} }
} }
var teamCityDivStyle = document.getElementById('team_city_div').style var teamCityDivStyle = document.getElementById('lab_locname_div').style
function registerDoorsAndSubmit(){ function registerDoorsAndSubmit(){
regfo.elMainMessage.innerHTML = "Registering with the test login portal<br/> and sending validation email..." regfo.elMainMessage.innerHTML = "Registering with the test login portal<br/> and sending validation email..."
......
...@@ -61,11 +61,25 @@ var cmxClt = (function() { ...@@ -61,11 +61,25 @@ var cmxClt = (function() {
["pic_file", false, "pref" , "f", "other_infos"], ["pic_file", false, "pref" , "f", "other_infos"],
// ==> *scholars* table // ==> *scholars* table
["org", false, "plsfill", "t", "org_infos"],
["org_type", false, "plsfill", "m", "org_infos"], // org field
["team_lab", true, "plsfill", "t", "org_infos"], // => name, acro in one field "label": #lab_label, #inst_label
["org_city", false, "pref" , "t", "org_infos"] // => all other fields
// ==> *affiliations* table // - are optional
// - if present, should be named: lab|inst + '_' + colname
// => TODO org details suggestions
// url, loc should have autofill when name or acro is chosen
// => POSS org <-> org suggestions
// once a lab is filled, we could propose the institution
["lab_label", false, "plsfill", "t", "org_infos"],
["lab_locname", false, "pref", "t", "org_infos"],
["inst_label", false, "pref", "t", "org_infos"],
["inst_type", false, "pref", "m", "org_infos"],
// ["lab_code", false, "pref", "t", "org_infos"],
// ["lab_url", false, "pref", "t", "org_infos"],
// ["inst_locname", false, "pref" , "t", "org_infos"],
// ["inst_url", false, "pref" , "t", "org_infos"],
// ==> *orgs* table via pretreatment org is inst or org is lab
] ]
// group "auto" === filled by controllers // group "auto" === filled by controllers
......
...@@ -347,73 +347,15 @@ ...@@ -347,73 +347,15 @@
class="panel-body ccsection-uform-body panel-collapse collapse out" class="panel-body ccsection-uform-body panel-collapse collapse out"
role="tabpanel" aria-expanded="false"> role="tabpanel" aria-expanded="false">
<div class="question input-group"> <div class="question input-group">
<label for="position" class="smlabel input-group-addon">* Job Position</label> <label for="position" class="smlabel input-group-addon">* Job Position</label>
<input id="position" name="position" maxlength="30" <input id="position" name="position" maxlength="30"
type="text" class="form-control autocomp" placeholder="titre" type="text" class="form-control autocomp" placeholder="titre"
onblur="cmxClt.makeBold(this)" onfocus="cmxClt.makeNormal(this)" onblur="cmxClt.makeBold(this)" onfocus="cmxClt.makeNormal(this)"
value="{{ current_user.info.position }}"> value="{{ current_user.info.position }}">
</div> </div>
<!-- ORG QUESTIONS -->
<div class="question">
<div class="input-group">
<label for="org" class="smlabel input-group-addon">Parent Institution</label>
<input id="org" name="org" maxlength="120"
type="text" class="form-control autocomp" placeholder='eg "CNRS" or "University of Oxford"'
value="{{ current_user.info.org }}">
</div>
</div>
<div class="question">
<div class="input-group">
<label for="org_type" class="smlabel input-group-addon">Institution Type</label>
<select id="org_type" name="org_type"
class="custom-select form-control"
onchange="if(this.value=='other'){otherInstDiv.style.display = 'block'} else {otherInstDiv.style.display='none';otherOrgTypeInput.value=''}">
<option selected disabled value="">Please select</option>
<option value="university">University</option>
<option value="public R&amp;D org">Public sector R&amp;D organization</option>
<option value="public other org">Other public sector organization</option>
<option value="private org">Private sector organization</option>
<option value="none">None at the moment</option>
<option value="other"
onclick="otherInstDiv.style.display = 'block'"
>Other</option>
</select>
</div>
<!-- Other institution type <=> only if previous choice == 5 -->
<div class="question conditional-q" id="other_org_div">
<div class="input-group">
<label for="other_org_type" class="smlabel input-group-addon">Other type</label>
<input id="other_org_type" name="other_org_type" maxlength="120"
type="text" class="form-control" placeholder="Clarify here the type of your parent institution">
</div>
</div>
</div>
<!-- TEAM QUESTIONS -->
<div class="question">
<div class="input-group">
<label for="team_lab" class="smlabel input-group-addon">* Lab / Team / Dept</label>
<input id="team_lab" name="team_lab" maxlength="120"
type="text" class="form-control" placeholder="More detailed affiliation, if relevant"
value="{{ current_user.info.team_lab }}">
</div>
</div>
<!-- Lab city <=> only for France -->
<div class="question conditional-q" id="team_city_div">
<div class="input-group">
<label for="org_city" class="smlabel input-group-addon">Lab city</label>
<input id="org_city" name="org_city" maxlength="50"
type="text" class="form-control" placeholder="Ville de votre institution"
value="{{ current_user.info.org_city }}">
</div>
</div>
{% include 'questions/org_details.html' %}
</div> <!-- /panel-body --> </div> <!-- /panel-body -->
<div class="panel-footer ccsection-footer">&nbsp;</div> <div class="panel-footer ccsection-footer">&nbsp;</div>
......
<!-- ORG QUESTIONS -->
<!-- lab or team and details -->
<div class="question">
<div class="input-group">
<label for="lab_label" class="smlabel input-group-addon">* Lab / Team / Dept</label>
<input id="lab_label" name="lab_label" maxlength="250"
type="text" class="form-control" placeholder="More detailed affiliation, if relevant"
value="{{ current_user.info.labs[0].tostring if current_user.info.labs|length > 0 }}">
</div>
</div>
<!-- lab locname <=> only for France -->
<div class="question conditional-q" id="lab_locname_div">
<div class="input-group">
<label for="lab_locname" class="smlabel input-group-addon">Lab city</label>
<input id="lab_locname" name="lab_locname" maxlength="50"
type="text" class="form-control" placeholder="Ville de votre institution"
value="{{ current_user.info.labs[0].locname if current_user.info.labs|length > 0 }}">
</div>
</div>
<!-- larger institution and details -->
<div class="question">
<div class="input-group">
<label for="inst_label" class="smlabel input-group-addon">Parent Institution</label>
<input id="inst_label" name="inst_label" maxlength="250"
type="text" class="form-control autocomp" placeholder='eg "CNRS" or "University of Oxford"'
value="{{ current_user.info.insts[0].tostring if current_user.info.insts|length > 0 }}">
</div>
</div>
<div class="question">
<div class="input-group">
<label for="inst_type" class="smlabel input-group-addon">Institution Type</label>
<select id="inst_type" name="inst_type"
class="custom-select form-control"
onchange="if(this.value=='other'){otherInstDiv.style.display = 'block'} else {otherInstDiv.style.display='none';otherOrgTypeInput.value=''}">
<option selected disabled value="">Please select</option>
<option value="university">University</option>
<option value="public R&amp;D org">Public sector R&amp;D organization</option>
<option value="public other org">Other public sector organization</option>
<option value="private org">Private sector organization</option>
<option value="none">None at the moment</option>
<option value="other"
onclick="otherInstDiv.style.display = 'block'"
>Other</option>
</select>
</div>
<!-- Other institution type <=> only if previous choice == 5 -->
<div class="question conditional-q" id="other_org_div">
<div class="input-group">
<label for="other_inst_type" class="smlabel input-group-addon">Other type</label>
<input id="other_inst_type" name="other_inst_type" maxlength="120"
type="text" class="form-control" placeholder="Clarify here the type of your parent institution">
</div>
</div>
</div>
...@@ -160,20 +160,20 @@ ...@@ -160,20 +160,20 @@
<div class="question"> <div class="question">
<div class="input-group"> <div class="input-group">
<label for="team_lab" class="smlabel input-group-addon">* Lab / Team / Dept</label> <label for="lab_label" class="smlabel input-group-addon">* Lab / Team / Dept</label>
<input id="team_lab" name="team_lab" maxlength="120" <input id="lab_label" name="lab_label" maxlength="120"
type="text" class="form-control" placeholder="Your lab" type="text" class="form-control" placeholder="Your lab"
placeholder="team_lab"> placeholder="lab_label">
</div> </div>
</div> </div>
<!-- Lab city <=> only for France --> <!-- Lab city <=> only for France -->
<div class="question conditional-q" id="team_city_div"> <div class="question conditional-q" id="lab_locname_div">
<div class="input-group"> <div class="input-group">
<label for="org_city" class="smlabel input-group-addon">Lab city</label> <label for="lab_locname" class="smlabel input-group-addon">Lab city</label>
<input id="org_city" name="org_city" maxlength="50" <input id="lab_locname" name="lab_locname" maxlength="50"
type="text" class="form-control" placeholder="Ville de votre institution" type="text" class="form-control" placeholder="Ville de votre institution"
placeholder="org_city"> placeholder="lab_locname">
</div> </div>
</div> </div>
......
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