Parcourir la source

Merge branch 'release/v0.0.9'

Jérôme BUISINE il y a 4 ans
Parent
commit
91e62af35e

+ 0 - 14
links/config.py

@@ -1,14 +0,0 @@
-experiment_list = [
-    'MatchExtractsWithReference',
-    'AreSameImagesRandom',
-    'AreSameImagesReference',
-    'AreSameImagesReferenceOneExtract',
-    'PercentQualityRandom',
-    'IsImageCorrect',
-    'IsImageCorrectOneExtract'
-]
-
-default_host = 'https://diran.univ-littoral.fr'
-
-links_data_folder = 'links/media/data'
-expe_data_folder  = 'links/media/expe'

+ 1 - 1
links/generate/generate_experiment.py

@@ -36,7 +36,7 @@ def extract_data(line):
 
 def main():
 
-    parser = argparse.ArgumentParser(description="Compute specific dataset for model using of metric")
+    parser = argparse.ArgumentParser(description="Compute experiment data")
 
     parser.add_argument('--data', type=str, help='data links to use', required=True)
     parser.add_argument('--scenes', type=int, help="number of scenes", required=True)

+ 81 - 0
links/generate/generate_experiment_link_config.py

@@ -0,0 +1,81 @@
+# main imports
+import base64
+import json
+import re
+import argparse
+import sys, os
+import requests
+
+# modules imports
+sys.path.insert(0, '') # trick to enable import of main folder module
+
+# config imports
+import links.config  as cfg
+
+
+def encode_data(data):
+    json_data = json.dumps(data)
+    link_data = base64.b64encode(str(json_data).encode('utf-8'))
+    
+    return link_data
+
+
+def main():
+    # getting all scenes available name
+    scenes_list_url = cfg.default_host + '/api/listScenes'
+    res = requests.get(scenes_list_url)
+    data = json.loads(res.content)
+    scenes_name = data['data']
+
+    # getting all params
+    parser = argparse.ArgumentParser(description="Compute links for scenes of experiment")
+
+    parser.add_argument('--host', type=str, help='hostname choosen', default=cfg.default_host)
+    parser.add_argument('--experiment', type=str, help="experiment name to use", choices=cfg.experiment_list, required=True)
+    parser.add_argument('--experimentId', type=str, help="experiment id to use")
+    parser.add_argument('--output', type=str, help="output filename", required=True)
+
+    args = parser.parse_args()
+
+    p_host          = args.host
+    p_experiment    = args.experiment
+    p_experiment_id = args.experimentId
+    p_output        = args.output
+
+    p_scenes = None
+
+    # generate link for each scene
+    if p_experiment in cfg.scenes_list:
+        p_scenes = cfg.scenes_list_calibration[p_experiment]
+    else:
+        print("Pas de scènes disponibles par défaut dans cette expérience")
+        exit(0)
+
+    links = []
+    for scene in p_scenes:
+
+        data = {
+            'hostConfig': p_host,
+            'experimentId': p_experiment_id,
+            'experimentName': p_experiment,
+            'sceneName': scene
+        }
+
+        generated_link_info = encode_data(data)
+        generated_link = p_host + '/#/?q=' + bytes(generated_link_info).decode("utf-8")
+        links.append(generated_link)
+
+    filename_path = os.path.join(cfg.links_data_folder, p_output)
+
+    if not os.path.exists(cfg.links_data_folder):
+        os.makedirs(cfg.links_data_folder)
+
+    with open(filename_path, 'w') as f:
+
+        for id, link in enumerate(links):
+            f.write(p_scenes[id] + ';' + p_experiment + ';' + p_experiment_id + ';' + link + '\n')
+
+    print("Links are saved into.. %s" % filename_path)
+
+if __name__== "__main__":
+    main()

+ 1 - 1
links/templates/base.html

@@ -12,7 +12,7 @@
     
     {% block stylesheets %}
         <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
-        <link rel="stylesheet" type="text/css" href="{% static "css/links.css" %}">
+        <link rel="stylesheet" href="{% static "css/bootstrap.min.css" %}">
         <script src="https://kit.fontawesome.com/ee9d97bd14.js" crossorigin="anonymous"></script>
     {% endblock %}
 </head>

+ 0 - 47
links/templates/links/files.html

@@ -1,47 +0,0 @@
-{% extends 'base.html' %}
-
-{% load static %}
-
-{% block title %}
-    List of files
-{% endblock %}
-
-{% block content %}
-    
-    <h3>Links files of experiments</h3>
-    <br />
-
-    <div class="files-expe">
-
-        <br/>
-        {% if folder %}
-
-            <!-- List of items which has identifier when user pass experiment -->
-            <h3>List of files</h3>
-            <ul class="list-group">
-
-                {% for file in folder %} 
-                                    
-                    <li class="list-group-item">
-                        
-                        <div class="row">
-                            <div class="col-md-11">{{file}} </div>
-                            <div class="col-md-1 files-list" data-redirect-path="{{file}}"><i class="fas fa-arrow-circle-right"></i></div>
-                        </div>
-                    </li>
-                {% endfor %} 
-            </ul>
-
-
-        {% else %}
-            <div class="alert alert-warning" role="alert">
-                <i>Experiment folder is empty</i>
-            </div>
-        {% endif %}
-    </div>
-{% endblock %}
-
-{% block javascripts %}
-    <script src="{% static "js/files.js" %}"></script>
-{% endblock %}
-

+ 77 - 0
links/templates/links/index.html

@@ -0,0 +1,77 @@
+{% extends 'base.html' %}
+
+{% load static %}
+
+{% block title %}
+    Links list
+{% endblock %}
+
+{% block content %}
+    
+    <br />
+    <h3>SIN3D-app links generator</h3>
+    <br />
+
+    <hr />
+
+    <div class="row">
+        <div class="col-md-4 offset-md-1">
+            <h5>Generate new SIN3D-app link</h5>
+
+            <p align="center">
+                <img src="media/expe.png" width="30%">
+            </p>
+
+            <div id="generateInfo" class="alert alert-dismissible alert-success" style="display:none;">
+                <button id="generateInfoClose" type="button" class="close" data-dismiss="alert">&times;</button>
+                <strong>Well done!</strong> You successfully read <a href="#" class="alert-link">this important alert message</a>.
+            </div>
+
+            <form method="POST" action="#">
+                
+                {% csrf_token %}
+
+                <div class="form-group">
+                    <label for="guildId">Experiment ID</label>
+                    <select class="form-control" id="guildId" name="guildId">
+                        {% for key, value in configurations.items %}
+                            <option value="{{key}}">{{value}}</option>
+                        {% endfor %}
+                    </select>
+                </div>
+                <div class="form-group">
+                    <label for="userId">User ID</label>
+                    <input type="text" id="userId" class="form-control" name="userId" placeholder="Enter your user identifier"/>
+                    <div id="userIdFeedback"></div>
+                </div>
+
+                <button id="generateButton" type="button" class="btn btn-success" disabled>Generate</button>
+            </form>
+        </div>
+        <div id="displayLinks" class="col-md-6 offset-md-1">
+        </div>
+    </div>
+
+    <br />
+    <hr />
+    <br />
+{% comment %} 
+    <ul class="list-group">
+        <li class="list-group-item">                
+            <div class="row">
+                <div class="col-md-11"><a id="calibration-link" href="#">Calibration link</a></div>
+            </div>
+        </li>
+    </ul> {% endcomment %}
+
+{% endblock %}
+
+{% block javascripts %}
+    <script type="text/javascript"> 
+        // Utils informations
+    </script>
+    <script src="{% static 'js/displayLinks.js' %}"></script>
+    <script src="{% static 'js/links.js' %}"></script>
+
+{% endblock %}
+

+ 0 - 58
links/templates/links/links.html

@@ -1,58 +0,0 @@
-{% extends 'base.html' %}
-
-{% load static %}
-
-{% block title %}
-    Links list
-{% endblock %}
-
-{% block content %}
-    
-    <h3>Select your user identifier</h3>
-    <br />
-
-    <div class="row">
-        <div class="col-md-4 offset-md-4">
-            <form method="GET" action="/expe">
-                <div class="form-group">
-                    <input type="number" min="0" class="form-control" name="userId" placeholder="Enter your user identifier"/>
-                </div>
-            </form>
-        </div>
-    </div>
-
-    <div style="display:none" class="alert alert-danger" role="alert">
-        User identifier does not exist
-    </div>
-
-    <br />
-    <hr />
-    <br />
-
-    <ul class="list-group">
-        <li class="list-group-item">                
-            <div class="row">
-                <div class="col-md-11"><a id="calibration-link" href="#">Calibration link</a></div>
-                <div class="col-md-1"></div>
-            </div>
-        </li>
-    </ul>
-
-    <br />
-    <hr />
-    <br />
-
-    <ul class="list-group" id="links-list">
-    
-    </ul>
-
-{% endblock %}
-
-{% block javascripts %}
-    <script type="text/javascript"> 
-        // Utils informations
-        var links = "{{links}}"
-    </script>
-    <script src="{% static "js/links.js" %}"></script>
-{% endblock %}
-

+ 3 - 2
links/urls.py

@@ -8,8 +8,9 @@ from . import views
 app_name = 'expe'
 
 urlpatterns = [
-    path('', views.list_files, name='list_files'),
-    path('links', views.user_links, name='user_links'),
+    path('', views.load_index, name='generate_link'),
+    path('check', views.check_user_id, name='check_user_id'),
+    path('generate', views.generate_user_link, name='generate_user_link'),
 ]
 
 if settings.DEBUG is True:

+ 112 - 33
links/views.py

@@ -2,15 +2,37 @@
 from django.shortcuts import render
 from django.http import HttpResponse
 from django.conf import settings
+from django.shortcuts import redirect
 from django.contrib.auth.decorators import login_required
 from django.http import Http404
 
 # main imports
 import os
 import json
+import base64
 
-from . import config  as cfg
+# db imports
+from pymongo import MongoClient
 
+# generate mongo db connection and let access to collection
+connection = MongoClient()
+
+db = connection['sin3d']
+contributors_collection = db['sin3d-contributors']
+configurations_collection = db['sin3d-configuration']
+
+def encode_data(data):
+    json_data = json.dumps(data)
+    link_data = base64.b64encode(str(json_data).encode('utf-8'))
+    
+    return link_data
+
+def generate_link(data):
+    # generate custom link
+    generated_link_info = encode_data(data)
+    generated_link = data['hostConfig'] + '/#/?q=' + bytes(generated_link_info).decode("utf-8")
+
+    return generated_link
 
 def get_base_data(expe_name=None):
     '''
@@ -23,53 +45,110 @@ def get_base_data(expe_name=None):
     return data
 
 
-def list_files(request):
+def load_index(request):
+    '''
+    Prepare data information for main page
+    - experiment identifiers available from configuration
+    '''
 
-    # get param 
-    # TODO : implement view which list all expe links file
+    data = {}
 
-    experiment_path = cfg.expe_data_folder
+    # get all configurations
+    configurations = configurations_collection.find()
+    
+    data['configurations'] = {}
 
-    files = []
+    for config in configurations:
+        
+        guild_id = config['guild_id']
+        experiment_id = config['config']['experimentId']
 
-    if os.path.exists(experiment_path):
-        files = sorted(os.listdir(experiment_path))
+        data['configurations'][guild_id] = experiment_id
 
-    data = get_base_data()
-    data['folder'] = files
+    return render(request, 'links/index.html', data)
 
-    return render(request, 'links/files.html', data)
+def check_user_id(request):
 
+    data = {'status': True, 'message': None}
 
-def user_links(request):
+    if request.method == 'POST':
+        
+        form_input = json.loads(request.body)
 
-    filename = request.GET.get('filename')
+        guild_id = form_input['guildId']
+        user_id = form_input['userId']
 
-    if filename is None:
-        # send 404 error
-        raise Http404("Page does not exist")
-    
-    filepath = os.path.join(cfg.expe_data_folder, filename)
+        # check guild id 
 
-    if not os.path.exists(filepath):
-        # send 404 error
-        raise Http404("File asked does not exist")
+        if len(user_id) > 5:
+            guild = configurations_collection.find_one({'guild_id': int(guild_id)})
 
-    # read data and send it
-    with open(filepath, 'r') as f:
-        lines = [l.replace('\n', '') for l in f.readlines()]
+            if ' ' in user_id:
+                data['message'] = 'Space character is not authorized'
+                data['status'] = False
+            elif guild is None:
+                data['message'] = 'Experiment identifier does not exist'
+                data['status'] = False
+            else:
+                user_account = contributors_collection.find_one({'guild_id': int(guild_id), 'user_id': user_id})
 
-    links = {}
-    for line in lines:
-        data = line.split(';')
-        links[data[0]] = data[1:]
-    
-    data = get_base_data()
-    data['links'] = json.dumps(links)
-    
-    return render(request, 'links/links.html', data)
+                if user_account is not None:
+                    # create user link
+                    data['message'] = 'User identifier already used'
+                    data['status'] = False
+                else:
+                    # user already exists
+                    data['message'] = 'Valid user identifier'
+                    data['status'] = True
+        else:
+            data['message'] = 'User identifier need at least 6 characters'
+            data['status'] = False
+
+    return HttpResponse(json.dumps(data), content_type='application/json')
+
+def generate_user_link(request):
+    '''
+    Generate link is possible for user, otherwise send an issue with error code
+    '''
+
+    data = {'status': False}
+    # create link and add it to local storage of navigator
+    if request.method == 'POST':
+
+        form_input = json.loads(request.body)
+
+        guild_id = form_input['guildId']
+        user_id = form_input['userId']
+
+        if len(user_id) > 5 and ' ' not in user_id:
+            guild_config = configurations_collection.find_one({'guild_id': int(guild_id)})
+
+            if guild_config is not None:
 
+                user_account = contributors_collection.find_one({'guild_id': int(guild_id), 'user_id': user_id})
+                print(user_account)
 
+                if user_account is None:
+                    # we can create link and save it
+                    user_config = guild_config['config']
+                    user_config['userId'] = user_id
 
+                    contributors_collection.insert_one({
+                        'user_id': user_id, 
+                        'username': user_id, 
+                        'guild_id': guild_config['guild_id'], 
+                        'guild_name': guild_config['guild_name'], 
+                        'discord': False, 
+                        'config': user_config
+                    })
 
+                    data['config'] = user_config
+                    data['link'] = generate_link(user_config)
+                    data['status'] = True
+                    data['message'] = 'Your experiment link was generated'
+            else:
+                data['message'] = 'Experiment link already generated'
+        else:
+            data['message'] = 'Invalid experiment id or user id'
 
+    return HttpResponse(json.dumps(data), content_type='application/json')

+ 2 - 1
requirements.txt

@@ -1,3 +1,4 @@
 Django
 numpy
-requests
+requests
+pymongo

Fichier diff supprimé car celui-ci est trop grand
+ 12 - 0
static/css/bootstrap.min.css


+ 0 - 29
static/css/links.css

@@ -1,29 +0,0 @@
-body {
-    background-color: lightgrey;
-}
-
-.files-expe{
-    margin-left: 20%;
-    margin-right: 20%;
-    text-align: left;
-}
-
-.already-used{
-    background-color: lightslategrey;
-}
-
-.already-used a{
-    color:black;
-}
-
-.already-used a:hover{
-    text-decoration: none;
-}
-
-.files-expe a:hover{
-    text-decoration: none;
-}
-
-.files-expe a{
-    color:black;
-}

+ 88 - 0
static/js/displayLinks.js

@@ -0,0 +1,88 @@
+
+const linksBlock = document.getElementById('displayLinks')
+
+var localStorage = window.localStorage
+
+async function loadLinks(){
+    linksBlock.innerHTML = ''
+
+    let title = document.createElement('h5')
+    title.innerHTML = 'Your generated SIN3D-app links'
+
+    linksBlock.append(title)
+
+    // retrieve expe links if exists
+    if (localStorage.getItem('p3d-user-links')){
+
+        expes_link = JSON.parse(localStorage.getItem('p3d-user-links'))
+
+        for (expe in expes_link){
+
+            let card = document.createElement('div')
+            card.className = 'card border-light mb-12'
+            card.style.border = '1px solid'
+            
+            let cardHeader = document.createElement('div')
+            cardHeader.className = 'card-header'
+            cardHeader.innerHTML = expe
+
+            let cardBody = document.createElement('div')
+
+            let cardBodyList = document.createElement('ul')
+            cardBodyList.className = 'list-group border-light'
+
+            for (experimentId in expes_link[expe]){
+                
+                for (userId in expes_link[expe][experimentId]){
+                    
+                    let userValue = expes_link[expe][experimentId][userId]
+
+                    let cardBodyItem = document.createElement('li')
+                    cardBodyItem.className = 'list-group-item d-flex justify-content-between align-items-center border-light'
+                    cardBodyItem.style.border = '1px solid'
+                    cardBodyItem.innerHTML = '<ul class="fa-ul">\
+                    <li><p><i class="fa-li fas fa-flask"></i> ' + experimentId + '</p></li> \
+                    <li><p><i class="fa-li fas fa-id-badge"></i> '  + userId + '</p></li>\
+                    </ul>\
+                    <a href="' + userValue.link + '"><span class="badge badge-primary badge-pill">Launch experiment <i class="fas fa-external-link-alt"></i></span></a>'
+                    cardBodyList.append(cardBodyItem)
+                }
+            }
+
+            cardBody.append(cardBodyList)
+            card.append(cardHeader)
+            card.append(cardBody)
+            linksBlock.append(card)
+        }
+    }
+    else{
+        let card = document.createElement('div')
+        card.className = 'card border-warning mb-12'
+        card.style.border = '1px solid'
+        
+        let cardHeader = document.createElement('div')
+        cardHeader.className = 'card-header'
+        cardHeader.innerHTML = 'Generated links information'
+
+        let cardBody = document.createElement('div')
+        cardBody.className = 'card-body'
+    
+        let bodyTitle = document.createElement('h4')
+        bodyTitle.className = 'card-title'
+        bodyTitle.innerHTML = 'No experiment links available'
+
+        let bodyText = document.createElement('p')
+        bodyText.className = 'card-text'
+        bodyText.innerHTML = 'If you want to generate your experiment link, please use the form on the left'
+
+        cardBody.append(bodyTitle)
+        cardBody.append(bodyText)
+
+        card.append(cardHeader)
+        card.append(cardBody)
+
+        linksBlock.append(card)
+    }
+}
+
+loadLinks()

+ 0 - 19
static/js/files.js

@@ -1,19 +0,0 @@
-const toggleVisible = ele => ele.style.display = ele.style.display === 'none' ? 'block' : 'none'
-const toggleClass = (ele, class1, class2) => ele.className = ele.className === class1 ? class2 : class1
-
-window.addEventListener('DOMContentLoaded', () => {
-    // Display list of files from day folder
-    // need to parse as `Array`
-    Array.from(document.getElementsByClassName('files-list')).forEach(item => {
-        item.addEventListener('click', event => {
-            event.preventDefault()
-            currentElem = event.currentTarget
-
-            // get list element
-            let filePath = currentElem.getAttribute('data-redirect-path')
-
-            // use of base url obtained from Django using `{{BASE}}`
-            window.location = baseUrl + 'links?filename=' + filePath
-        })
-    })
-})

+ 141 - 119
static/js/links.js

@@ -1,154 +1,176 @@
 const toggleVisible = ele => ele.style.display = ele.style.display === 'none' ? 'block' : 'none'
 const toggleClass = (ele, class1, class2) => ele.className = ele.className === class1 ? class2 : class1
 
-const links_data = JSON.parse(links.replace(/&quot;/g, '"'))
-const links_nb_elem = Object.keys(links_data).length
-const KEYCODE_ENTER       = 13
+const guildIdField = document.getElementById('guildId')
+const userIdField = document.getElementById('userId')
+const generateButton = document.getElementById('generateButton')
+const generateInfo = document.getElementById('generateInfo')
 
-const alertDiv = document.getElementsByClassName('alert-danger')[0]
-const domLiCalibrationLink = document.getElementById('calibration-link').closest('li')
+const userIdFeedback = document.getElementById('userIdFeedback')
 
-function loadDataList(elem, list){
-    userId = elem.value
 
+async function loadExperiment(){
+    var urlParams = new URLSearchParams(window.location.search);
 
-    if (userId.length > 0){
+    if(urlParams.get('id')){
+        current_guild_id = urlParams.get('id')
 
-        // remove event listener of each element by default
-        if (list.children.length > 0){
-            for (var element of list.children){
-                element.removeEventListener('click', elemClick)
+        for (let option of guildIdField.options){
+            if (option.value == current_guild_id){
+                option.defaultSelected = true
             }
         }
+    }
+}
 
-        list.innerHTML = ""
+// check enter key issue
+async function searchUserId(){
 
-        // check if nomber of links can let access to userId
-        if (userId > (links_nb_elem - 1) || userId < 0){
-            alertDiv.setAttribute('style', 'display:block')
-            domLiCalibrationLink.setAttribute('style', 'display:none')
-        }else{
-            alertDiv.setAttribute('style', 'display:none')
-            domLiCalibrationLink.setAttribute('style', 'display:block')
-            
-            // load and generate CalibrationMeasurement experiment link
-            firstLink = links_data[userId][0].split(':::')[1]
-            generateCalibrationLink(firstLink)
-        
-            let currentLinks = links_data[userId]
+    // reinit feedback div and form
+    // userIdFeedback.innerText = ''
+    userIdField.className = 'form-control'
+    generateButton.disabled = true
+
+    // get form value
+    let guildId = guildIdField.value
+    let userId = userIdField.value
+
+    if (!userId.length){
+        return
+    }
+
+    const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value
+
+    let data = {'guildId': guildId, 'userId': userId}
     
-            currentLinks.forEach((element, index) => {
-        
-                if (element.length > 0){
+    fetch('/check', {
+        method: 'POST',
+        body: JSON.stringify(data),
+        headers: {
+            'Content-type': 'application/json; charset=utf-8',
+            'X-CSRFToken': csrfToken
+        }
+    })
+    .then((resp) => 
+        resp.json()
+    )
+    .then(res => {
+        // update status field and enable link generation if valid
+        userIdFeedback.innerText = res.message
         
-                    data = element.split(':::')
-
-                    // add of div elements
-                    rowDiv = document.createElement('div')
-                    rowDiv.setAttribute('class', 'row')
-
-                    rowDivLeft = document.createElement('div')
-                    rowDivLeft.setAttribute('class', 'col-md-11')
-
-                    rowDivRight = document.createElement('div')
-                    rowDivRight.setAttribute('class', 'col-md-1')
-
-                    // create link
-                    currentLink = document.createElement('a')
-                    currentLink.setAttribute('href', data[1])
-                    currentLink.innerHTML = 'Link ' + (index + 1) + ': ' + data[0]
-                    
-                    // add of elements
-                    rowDivLeft.appendChild(currentLink)
-                    rowDiv.appendChild(rowDivLeft)
-                    rowDiv.appendChild(rowDivRight)
-
-                    currentLi = document.createElement('li')
-                    currentLi.setAttribute('class', 'list-group-item')
-                    currentLi.appendChild(rowDiv)
-
-                    list.appendChild(currentLi)
-                }
-            });
+        if (res.status){
+            userIdField.className = 'form-control is-valid'
+            userIdFeedback.className = 'valid-feedback'
+            generateButton.disabled = false
+        }else{
+            userIdField.className = 'form-control is-invalid'
+            userIdFeedback.className = 'invalid-feedback'
+            generateButton.disabled = true
         }
-    }else{
-        domLiCalibrationLink.setAttribute('style', 'display:none')
-    }
+    })
 }
 
-function elemClick(event){
-    event.preventDefault()
-    
-    currentElem = event.currentTarget
-    
-    // Add <i class="fas fa-check"></i>
-    divLeft = currentElem.getElementsByClassName('col-md-1')[0]
+async function guildIdChanged(){
+    // reinit field if guild value changed
+    userIdField.className = 'form-control'
+    userIdFeedback.className = ''
+    userIdFeedback.innerText = ''
+    userIdField.value = ''
+    generateButton.disabled = true
+}
 
-    if (divLeft.children.length <= 0){
-        iconElem = document.createElement('li')
-        iconElem.setAttribute('class', 'fas fa-check')
-        divLeft.appendChild(iconElem)
+async function generateLink(){
 
-        // update `li` class element
-        currentElem.setAttribute('class', 'list-group-item already-used')
-    }
+    // reinit field if guild value changed
+    let guildId = guildIdField.value
+    let userId = userIdField.value
 
-    // retrieve and open link in new tab
-    link = currentElem.getElementsByTagName('a')[0]
-    url = link.getAttribute('href')
-    var win = window.open(url, '_blank');
-    win.focus();
-}
+    const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value
 
-function generateCalibrationLink(link){
+    let data = {'guildId': guildId, 'userId': userId}
+    
+    fetch('/generate', {
+        method: 'POST',
+        body: JSON.stringify(data),
+        headers: {
+            'Content-type': 'application/json; charset=utf-8',
+            'X-CSRFToken': csrfToken
+        }
+    })
+    .then((resp) => 
+        resp.json()
+    )
+    .then(res => {
+        // update status field and enable link generation if valid
+        console.log(res)
+        user_config = res.config
+        user_link = res.link
+
+        if (res.status){
+             // Add link to local storage
+            var localStorage = window.localStorage
+
+            // link composed of {'experimentId', 'link'}
+            expes_link = {}
+            
+            // retrieve expe links if exists
+            if (localStorage.getItem('p3d-user-links')){
+                expes_link = JSON.parse(localStorage.getItem('p3d-user-links'))
+            }
 
-    // get and rebuild b64 link part using current info and calibration experiment
-    b64Part = link.split('?q=')[1]
-    jsonLinkData = JSON.parse(atob(b64Part))
+            if (!expes_link[user_config.experimentName]){
+                // create for this experiment
+                expes_link[user_config.experimentName] = {}
+            }
 
-    // Use default information about calibration experiment
-    jsonLinkData['experimentName'] = 'CalibrationMeasurement'
-    jsonLinkData['sceneName'] = '50_shades_of_grey'
+            if (!expes_link[user_config.experimentName][user_config.experimentId]){
+                // create for this experiment and experiment id
+                expes_link[user_config.experimentName][user_config.experimentId] = {}
+            }
 
-    b64LinkData = btoa(JSON.stringify(jsonLinkData)).replace(/[=]/g, '')
-    calibrationLink = jsonLinkData['hostConfig'] + '/#/?q=' + b64LinkData
+            if (!expes_link[user_config.experimentName][user_config.experimentId][user_config.userId]){
+                // create for this experiment, experiment id and user id
+                expes_link[user_config.experimentName][user_config.experimentId][user_config.userId] = {'config': user_config, 'link': user_link}
+            }
 
-    // add to calibration DOM element the new generated link
-    domCalibrationLink = document.getElementById('calibration-link')
-    domCalibrationLink.setAttribute('href', calibrationLink)
-}
+            localStorage.setItem('p3d-user-links', JSON.stringify(expes_link))
+            
+            generateInfo.className = 'alert alert-dismissible alert-success'
+            generateInfo.style.display = 'block'
 
-window.addEventListener('DOMContentLoaded', () => {
+            generateInfo.innerHTML = '<button id="generateInfoClose" type="button" class="close" data-dismiss="alert">&times;</button> \
+            Well done! You successfully generate <a href="' + user_link + '" class="alert-link">your experiment link</a>. \
+            <strong>This link is unique and associated with your browser</strong>..'
 
-    const inputElement = document.getElementsByName('userId')[0]
-    const linksList = document.getElementById('links-list')
+        }else{
+            generateInfo.className = 'alert alert-dismissible alert-danger'
+            generateInfo.style.display = 'block'
 
-    // add to calibration event listener
-    domCalibrationLink = document.getElementById('calibration-link')
-    domLiCalibrationLink.addEventListener('click', elemClick)
+            generateInfo.innerHTML = '<button id="generateInfoClose" type="button" class="close" data-dismiss="alert">&times;</button> \
+            <strong>An error occured!</strong> ' + res.message + '.'
+        }
 
-    // first load data if userId is choosed
-    loadDataList(inputElement, linksList)
-    
-    inputElement.addEventListener('keyup', event => {
-        event.preventDefault()
-        currentElem = event.currentTarget
+        let generateInfoClose = document.getElementById('generateInfoClose')
 
-        loadDataList(currentElem, linksList)
+        // link function
+        generateInfoClose.addEventListener('click', (e) => {
+            toggleVisible(generateInfo)
+        })
 
-        for (var element of linksList.children){
-            element.addEventListener('click', elemClick)
-        }
+        // reset form
+        loadLinks()
+        guildIdChanged()
+       
     })
-})
+}
 
-// check enter key issue
-const checkKey = e => {
-    
-    if (e.keyCode === KEYCODE_ENTER) {
-       e.preventDefault()
-    }
- }
+// check by default
+searchUserId()
+
+document.addEventListener('DOMContentLoaded', loadExperiment)
+userIdField.addEventListener('keyup', searchUserId)
+guildIdField.addEventListener('change', guildIdChanged)
+generateButton.addEventListener('click', generateLink)
  
  // implement `key` events
- document.addEventListener('keydown', checkKey)
+ // document.addEventListener('keydown', checkKey)