Commit c5badbc5 authored by Romain Loth's avatar Romain Loth

Change DB schema 1 table => 3 tables + add missing cols (homepage, jobsearch,...

Change DB schema 1 table => 3 tables + add missing cols (homepage, jobsearch, position, affiliation_id)
parent 6ddedc31
......@@ -5,26 +5,80 @@
mysql -uroot -pvery-safe-pass -h $SQL_HOST -P 3306
# --- after connection to mysql
CREATE DATABASE comex_shared ;
CREATE DATABASE comex_shared CHARACTER SET utf8 COLLATE utf8_general_ci ;
USE comex_shared ;
CREATE TABLE comex_registrations (
doors_uid char(36) not null unique,
CREATE TABLE scholars (
doors_uid char(36) not null unique primary key,
last_modified_date char(24) not null,
email varchar(255) not null unique primary key,
initials varchar(7) not null,
email varchar(255) not null unique,
country varchar(60) not null,
first_name varchar(30) not null,
middle_name varchar(30),
last_name varchar(50) not null,
jobtitle varchar(30) not null,
keywords varchar(350) not null,
institution varchar(120) not null,
institution_type varchar(50) not null,
team_lab varchar(50),
institution_city varchar(50),
initials varchar(7) not null,
affiliation_id int(15) not null,
position varchar(30), -- eg Director
hon_title varchar(30), -- eg Doctor
interests_text varchar(1200),
community_hashtags varchar(350),
gender char(1),
pic_file mediumblob
job_looking_date char(24), -- null if not looking for a job
home_url varchar(120), -- homepage
pic_url varchar(120), -- an alternative to pic_file blob
pic_file mediumblob,
record_status varchar(10),
INDEX uid_index_sch (doors_uid),
INDEX country_index_sch (country),
INDEX affs_index_sch (affiliation_id)
) ;
-- affiliations: institutions and labs
CREATE TABLE affiliations(
affid int(15) not null auto_increment,
org varchar(120) not null,
org_type varchar(50) not null,
team_lab varchar(120),
org_city varchar(50),
INDEX affid_index_affs (affid),
PRIMARY KEY (affid),
-- we chose not to put org_type in unique key: should be entailed by other 3
-- TODO doesn't yet prevent entering the same info twice !!
UNIQUE KEY full_affiliation (org, team_lab, org_city)
);
ALTER TABLE scholars ADD FOREIGN KEY (affiliation_id) REFERENCES affiliations(affid) ;
-- keyword/subject terms
CREATE TABLE keywords(
kwid int(15) not null auto_increment,
kwstr char(50) not null unique, -- eg 'complex networks'
INDEX kwid_index_kws (kwid),
INDEX kwstr_index_kws (kwstr),
PRIMARY KEY (kwid)
);
-- relationship scholars <n=n> keywords
CREATE TABLE sch_kw(
uid char(36) not null,
kwid int(15) not null,
INDEX uid_index_schkw (uid),
INDEX kwid_index_schkw (kwid),
PRIMARY KEY (uid, kwid),
FOREIGN KEY (uid) REFERENCES scholars(doors_uid) ON DELETE CASCADE,
FOREIGN KEY (kwid) REFERENCES keywords(kwid)
);
-- linked identities (various users' ids on soc. media and research networks)
-- TODO use this :)
CREATE TABLE linked_ids(
linkid int(15) not null auto_increment,
uid char(36) not null,
ext_id_type char(50) not null, -- eg 'orcid','LinkedIn','Twitter'
ext_id char(50) not null, -- eg "0000-0002-1825-0097"
INDEX uid_index_eids (uid),
PRIMARY KEY (linkid),
FOREIGN KEY (uid) REFERENCES scholars(doors_uid) ON DELETE CASCADE
);
```
This diff is collapsed.
......@@ -18,24 +18,33 @@
*/
// the target columns in DB: tuple (name, mandatoryBool, maxChars (or nChars))
var COLS = [ ["doors_uid", false, 36, 'exact'],
var COLS = [ ["doors_uid", true, 36, 'exact'],
["last_modified_date", true, 24, 'exact'],
["email", true, 255],
["initials", true, 7],
["country", true, 60],
["first_name", true, 30],
["middle_name", false, 30],
["last_name", true, 50],
["jobtitle", true, 30],
["keywords", true, 350],
["institution", true, 120],
["institution_type", true, 50],
["team_lab", false, 50],
["institution_city", false, 50],
["initials", true, 7],
["position", false, 30],
["hon_title", false, 30],
["interests_text", false, 1200],
["community_hashtags", false, 350],
["gender", false, 1, 'exact'],
["pic_file", false, null]]
["job_looking_date", false, 24, 'exact'],
["home_url", false, 120],
["pic_url", false, 120],
["pic_file", false, null],
// ==> *scholars* table
["keywords", true, 350],
// ==> *keywords* table
["org", true, 120],
["org_type", true, 50],
["team_lab", false, 120],
["org_city", false, 50]]
// ==> *affiliations* table
// vars that will be used during the interaction
// NB other vars defined in main scope but just before their respective funs
......@@ -47,6 +56,9 @@ var regTimestamp = document.getElementById('last_modified_date')
var uidInput = document.getElementById('doors_uid')
var email = document.getElementById('email')
// dates up to 2049/12/31
var validDate = new RegExp( /^20[0-4][0-9]\/(?:0?[1-9]|1[0-2])\/(?:0?[1-9]|[1-2][0-9]|3[0-1])$/)
// str of the form: doors_hostname:doors_port
var doorsConnectParam = document.getElementById('doors_connect').value
......@@ -58,7 +70,8 @@ var captchaCheck = document.getElementById('my-captchaHash')
var subPage1Style = document.getElementById('subpage_1').style
var subPage2Style = document.getElementById('subpage_2').style
var teamCityDivStyle = document.getElementById('team_city_div').style
var otherInstDivStyle = document.getElementById('other_institution_div').style
var otherInstDivStyle = document.getElementById('other_org_div').style
var jobLookingDivStyle = document.getElementById('job_looking_div').style
// param for generation & validation
var realCaptchaLength = 5
......@@ -78,6 +91,7 @@ var doorsMessage = document.getElementById('doors_ret_message')
var passStatus = false
var emailStatus = false
var captchaStatus = false
var jobLookingDateStatus = false
submitButton.disabled = true
theForm.onkeyup = testAsYouGo
theForm.onchange = testAsYouGo
......@@ -97,11 +111,10 @@ function testAsYouGo() {
captchaStatus = (captcha.value.length == realCaptchaLength)
// for debug
checkPassStatus()
checkJobDateStatus()
// TODO all other mandatory fields should be checked here
if (passStatus && emailStatus && captchaStatus) {
if (passStatus && emailStatus && captchaStatus && jobLookingDateStatus) {
submitButton.disabled = false
}
else {
......@@ -369,14 +382,18 @@ function validateAndMsg() {
submitButton.disabled = true
mainMessage.style.display = 'block'
// objectify the form
wholeFormData = new FormData(theForm);
var valid = true
// TODO pass mainMessage ref as arg
mainMessage.innerHTML = "Validating the form..."
// also reformat the jobDate from "YYYY/MM/DD" to ISO
if (jobDate.value.length) jobDate.value = (new Date(jobDate.value)).toISOString()
// objectify the form
wholeFormData = new FormData(theForm);
var missingFields = []
var toolongFields = []
for (var i in COLS) {
......@@ -406,7 +423,7 @@ function validateAndMsg() {
}
// test length --------------------
else if (actualValue != null) {
else if (mandatory || (actualValue != null && actualValue != "")) {
if (isExactN) {
// should never happen => trigger error
......@@ -455,6 +472,9 @@ function validateAndMsg() {
// length is handled by each input's maxlength
// re change the jobDate from ISO to YYYY/MM/DD
if (jobDate.value.length) jobDate.value = jobDate.value.slice(0,10).replace(/-/g,"/")
// display (TODO setTimeout and fade)
mainMessage.innerHTML = errorMessage
return false
......@@ -588,11 +608,11 @@ nameInputs.forEach ( function(nameInput) {
if(/[A-Z]/.test(txt)) {
var capsArr = txt.match(/[A-Z]/g)
for (var i in capsArr) {
apparentInitials += capsArr[i] + '.'
apparentInitials += capsArr[i]
}
}
else {
apparentInitials += txt.charAt(0) + '.'
apparentInitials += txt.charAt(0)
}
}
}) ;
......@@ -633,22 +653,6 @@ var pass2 = document.getElementById('password2')
var passMsg = document.getElementById('password_message')
var passwords = [pass1, pass2]
// £DEBUG autofill ----------->8------
// fName.value = "Jean"
// lName.value = "Tartampion"
// initialsInput.value="JPP"
// document.getElementById('country').value = "France"
// email.value= makeRandomString(7)+"@om.fr"
// pass1.value="123456+789"
// pass2.value="123456+789"
// document.getElementById('jobtitle').value = "atitle"
// document.getElementById('keywords').value = "Blabla"
// document.getElementById('institution').value = "CNRS"
// basicEmailValidate(email.value)
// --------------------------->8------
passwords.forEach ( function(pass) {
// could also be attached to form onchange but then called often for nothing
pass.onkeyup = checkPassStatus
......@@ -686,7 +690,29 @@ function checkPassStatus() {
}
if (!passStatus) passMsg.style.color = colorRed
else passMsg.style.color = colorGreen
}
// jobLookingDateStatus ~~~> is job date a valid date?
var jobBool = document.getElementById('job_bool')
var jobDate = document.getElementById('job_looking_date')
var jobDateMsg = document.getElementById('job_date_message')
jobDate.onkeyup = checkJobDateStatus
jobDate.onchange = checkJobDateStatus
function checkJobDateStatus() {
jobLookingDateStatus = (jobBool.value == "No" || validDate.test(jobDate.value))
if (!jobLookingDateStatus) {
jobDateMsg.style.color = colorRed
jobDateMsg.innerHTML = 'Date is not yet in the valid format YYYY/MM/DD'
}
else {
jobDateMsg.style.color = colorGreen
jobDateMsg.innerHTML = 'Ok valid date!'
}
}
......@@ -725,13 +751,27 @@ $(function() {
});
// autocomplete hon_title
$(function() {
var $hontitlesInput = $('#hon_title')
var hontitlesList = ["Student", "PhD Student", "Doctor"]
$hontitlesInput.autocomplete({
source: hontitlesList,
autoFocus: true,
select: function( event, ui ) {
$hontitlesInput[0].style.fontWeight = "bold"
}
});
});
// autocomplete position
$(function() {
var $jobtitlesInput = $('#jobtitle')
var $jobtitlesInput = $('#position')
var jobtitlesList = ["Student", "Engineer", "capetown",
"PhD Student", "Dr", "Post-Doc",
"Lecturer", "Associate Professor", "Professor",
var jobtitlesList = ["Engineer", "Lecturer", "Associate Professor", "Professor",
"Research Fellow", "Research Director", "Chairman"]
$jobtitlesInput.autocomplete({
......@@ -744,9 +784,10 @@ $(function() {
});
});
// autocomplete institution
$(function() {
var $institutionInput = $('#institution')
var $orgInput = $('#org')
var orgList = [
"Centre National de la Recherche Scientifique (CNRS)",
......@@ -897,7 +938,7 @@ $(function() {
"Institute of Computer Science of Czech Republic (AV ČR)",
"Institute for Condensed Matter Physics of the National Academy of Sciences of Ukraine (ICMP)",]
$institutionInput.autocomplete({
$orgInput.autocomplete({
source: orgList,
autoFocus: true,
select: function( event, ui ) {
......@@ -906,7 +947,7 @@ $(function() {
// not tab because used to move on to next field
if(event.keyCode == 9) return false;
$institutionInput[0].style.fontWeight = "bold"
$orgInput[0].style.fontWeight = "bold"
}
});
});
......@@ -1283,3 +1324,17 @@ $(function() {
});
console.log("load OK")
// £DEBUG autofill ----------->8------
fName.value = "Jean"
lName.value = "Tartampion"
initialsInput.value="JPP"
document.getElementById('country').value = "France"
email.value= makeRandomString(7)+"@om.fr"
pass1.value="123456+789"
pass2.value="123456+789"
document.getElementById('position').value = "atitle"
document.getElementById('keywords').value = "Blabla"
document.getElementById('org').value = "CNRS"
basicEmailValidate(email.value)
// --------------------------->8------
......@@ -190,8 +190,15 @@
<h3 class="formcat"> About your job and research </h3>
<div class="question input-group">
<label for="jobtitle" class="smlabel input-group-addon">* Job Title</label>
<input id="jobtitle" name="jobtitle" maxlength="30"
<label for="hon_title" class="smlabel input-group-addon"> Title </label>
<input id="hon_title" name="hon_title" maxlength="30"
type="text" class="form-control autocomp" placeholder="titre"
onblur="makeBold(this)" onfocus="makeNormal(this)">
</div>
<div class="question input-group">
<label for="position" class="smlabel input-group-addon">* Job Title</label>
<input id="position" name="position" maxlength="30"
type="text" class="form-control autocomp" placeholder="titre"
onblur="makeBold(this)" onfocus="makeNormal(this)">
</div>
......@@ -210,16 +217,16 @@
<div class="question">
<div class="input-group">
<label for="institution" class="smlabel input-group-addon">* Parent Institution</label>
<input id="institution" name="institution" maxlength="120"
<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"'>
</div>
</div>
<div class="question">
<div class="input-group">
<label for="institution_type" class="smlabel input-group-addon">* Institution Type</label>
<select id="institution_type" name="institution_type"
<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'){otherInstDivStyle.display = 'block'} else {otherInstDivStyle.display='none'}">
<option selected value="university">University</option>
......@@ -234,10 +241,10 @@
</select>
</div>
<!-- Other institution type <=> only if previous choice == 5 -->
<div class="question conditional-q" id="other_institution_div">
<div class="question conditional-q" id="other_org_div">
<div class="input-group">
<label for="other_institution_type" class="smlabel input-group-addon">Other type</label>
<input id="other_institution_type" name="other_institution_type" maxlength="120"
<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>
......@@ -255,8 +262,8 @@
<!-- Lab city <=> only for France -->
<div class="question conditional-q" id="team_city_div">
<div class="input-group">
<label for="institution_city" class="smlabel input-group-addon">Lab city</label>
<input id="institution_city" name="institution_city" maxlength="50"
<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">
</div>
</div>
......@@ -280,8 +287,25 @@
</div>
</div>
<div class="question">
<div class="input-group">
<label for="home_url" class="smlabel input-group-addon">Homepage</label>
<input id="home_url" name="home_url" maxlength="120"
type="text" class="form-control autocomp" placeholder='eg "http://www.wittyexample.org/~me"'>
</div>
</div>
<div class="question">
<p class="legend">Link to a picture of yours...</p>
<div class="input-group">
<label for="pic_url" class="smlabel input-group-addon">Picture link</label>
<input id="pic_url" name="pic_url" maxlength="120"
type="text" class="form-control autocomp" placeholder='eg "http://www.wittyexample.org/~me/my_great_pic.png"'>
</div>
</div>
<div class="question" style="margin-bottom:.5em" >
<p class="legend">Upload a picture of yours (max source size: 200kB)</p>
<p class="legend">... or upload a picture (png, jpg or gif, max source size: 200kB)</p>
<!-- <p class="legend">Upload a picture of yours (max source size: 4MB, then the image will be reduced to 200kB)</p> -->
<div class="input-group">
<label for="pic_file" class="smlabel input-group-addon">Picture</label>
......@@ -320,6 +344,28 @@
<p class="legend">Optional, ~15 lines max (1200 chars)</p>
</div>
<div class="question">
<div class="input-group">
<label for="job_bool" class="smlabel input-group-addon">Looking for a job?</label>
<select id="job_bool" name="job_bool"
class="custom-select form-control"
onchange="if(this.value=='Yes'){jobLookingDivStyle.display = 'block'} else {jobLookingDivStyle.display='none';jobDate.value=''}">
<option selected value="No" onclick="jobDate.value=''">No</option>
<option value="Yes" onclick="jobLookingDivStyle.display = 'block'">Yes</option>
</select>
</div>
<!-- job_looking_date_div <=> only if previous choice == Yes -->
<div class="question conditional-q" id="job_looking_div">
<p class="legend">Until when are you interested in job offers like post-doc positions, etc. ? (YYYY/MM/DD)</p>
<div class="input-group">
<label for="job_looking_date" class="smlabel input-group-addon">Job search limit</label>
<input id="job_looking_date" name="job_looking_date" maxlength="10"
type="text" class="form-control" placeholder="ex: 2019/09/30">
</div>
<p id="job_date_message" class="legend red" style="font-weight:bold"></p>
</div>
</div>
<!-- CNIL WARNING -->
<h3 class="formcat"> About your data </h3>
......
......@@ -82,8 +82,8 @@
<div class="spacer col-sm-1 col-md-1">&nbsp;</div>
<div class="raw-responses col-sm-8 col-md-8" style="font-family:Calibri, sans-serif">
<h3>debug</h3>
{% for key in records %}
<p> {{key}} {{records[key]}} </p>
{% for key in debug_records %}
<p> {{key}} {{debug_records[key]}} </p>
{% endfor %}
<p style="font-family: monospace; font-weight: bold">
......
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