From 1f843e2b408c1c5b38690a8f709512c338003464 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Tue, 11 Mar 2025 15:37:04 +0100
Subject: [PATCH 01/59] add craft-form.php, pampa-craft.py and
 craft_render_result.py

---
 www/cgi-bin/pampa/craft_render_result.py | 228 +++++++++++++++++++++++
 www/cgi-bin/pampa/pampa-craft.py         | 226 ++++++++++++++++++++++
 www/html/pampa/craft-form.php            | 139 ++++++++++++++
 3 files changed, 593 insertions(+)
 create mode 100644 www/cgi-bin/pampa/craft_render_result.py
 create mode 100644 www/cgi-bin/pampa/pampa-craft.py
 create mode 100644 www/html/pampa/craft-form.php

diff --git a/www/cgi-bin/pampa/craft_render_result.py b/www/cgi-bin/pampa/craft_render_result.py
new file mode 100644
index 0000000..51bbab4
--- /dev/null
+++ b/www/cgi-bin/pampa/craft_render_result.py
@@ -0,0 +1,228 @@
+import cgitb;
+
+cgitb.enable()
+import json
+import os, sys
+from html_utils import *
+import common
+import zipfile
+import operator
+
+"""
+NOTES
+
+- en ajoutant de nouvelles tables de peptides par défaut autres que mammals dans nos données, il faut adapter dans la partie de génération de l'arbre taxonomique section 'reduction by taxonomic groups' la vérification que toutes les tables (groupes taxonomiques) ont été inclues.
+
+"""
+
+
+
+def href_dl(link, name):
+    """
+    Construit une balise <a> ouvrant la page sur un nouvel onglet.
+    """
+    result = f'<a href="{link}" download="{name}" target="_blank">{name}</a>'
+    return result
+
+def zip_results(file_names, run_id, job_name=None):
+    """
+    Produit un fichier ZIP contenant tous les fichiers de résultat : 
+    - out.tsv
+    - detail_out.tsv
+    - report_out.txt
+    Ce fichier ZIP pourra être téléchargé par l'utilisateur.
+    """
+    with zipfile.ZipFile(f"{common.RESULT_DIR}{run_id}/data_result_{run_id}.zip", "a", compression=zipfile.ZIP_DEFLATED) as dwl_zip:
+        for fn in file_names:
+            abs_path = common.RESULT_DIR + run_id + '/' + fn
+            if os.path.exists(abs_path):
+                if job_name is not None:
+                    parts = os.path.splitext(fn)
+                    dwl_name = parts[0] + "_" + job_name + parts[1]
+                else: 
+                    dwl_name = fn
+                dwl_zip.write(abs_path, arcname=dwl_name)
+
+def results_output(run_id, job_name=None):
+    """
+    Produit la liste des fichiers téléchargeables par l'utilisateur.
+    """
+ 
+    output_files_title = {
+        "out_"+run_id+".tsv": "Assignments: ",
+        "detail_out_"+run_id+".tsv": "More details: ",
+        "report_out_"+run_id+".txt": "Report on the run: ",
+        "table_out_"+run_id+".tsv": "Peptides table built: ",
+        "data_result_"+run_id+".zip": "Downloads all results and data: "
+    }
+    head_out = "All results are available in the following files."
+    html = head_out
+    
+    html += "<ul>"
+    listdir = os.listdir(path=common.RESULT_DIR+run_id)
+    for fn in output_files_title:
+        if fn in listdir:
+            if job_name is not None:
+                parts = os.path.splitext(fn)
+                dwl_name = parts[0] + "_" + job_name + parts[1]
+            else: 
+                dwl_name = fn
+            html += li(output_files_title[fn] + href_dl(fn, dwl_name))
+    html += "</ul>"
+
+    return  html
+
+def write_main_page(run_id, taxo_used, job_name=None):
+    """Write a HTML page. The result page shown first when the PAMPA analysis is done."""
+    html = ""
+
+    # Insert HTML header and page head
+    html += open(f"{common.HTML_PATH}/header.php", "r").read()
+    html += '''
+    <div class="frametitle"><h1 id="title">Pampa-Craft</h1></div>
+    <div id="center_sup">
+        <div class="theme-border" style="display:none"></div>
+        <div id="link_home" style="display:inline-block">
+            <a href="/" class="text_onglet">
+                <img src="/Style/icon/home_w.png" alt="home_general"/>
+            </a>
+        </div>
+        <div class="tabs" id="menu_central" style="display:inline-block">'''
+    html += open(f"{common.HTML_PATH}/menu_central.txt", "r").read()
+    html += '''</div></div><div id="main"><div id="center">'''
+
+    html += f'<h2>Results for job {run_id}{f" ({job_name})" if job_name else ""}</h2>'
+
+    # Show warnings if they exist
+    warning_file = common.RESULT_DIR + run_id + "/warning.log"
+    if os.path.exists(warning_file):
+        with open(warning_file) as fileOut:
+            content = fileOut.read().strip()
+            if content:
+                html += '''
+                <div id="warnings_hidden" style="display: block;">
+                    <p>Warning(s) were raised during the execution. <a id="show_warnings">View report</a></p>
+                </div>
+                <div id="warnings_shown" style="display: none;">
+                    <p>Warning(s) were raised during the execution. <a id="hide_warnings">Hide</a></p>
+                    <table class="vide">'''
+                for line in content.split('\n'):
+                    if line:
+                        html += f'<tr><td>{line}</td></tr>'
+                html += '''</table></div><br>'''
+
+    # Load the TSV file and convert to JSON
+    import json
+    taxo_data = []
+    with open(taxo_used, "r") as file:
+        headers = file.readline().strip().split("\t")
+        for line in file:
+            values = line.strip().split("\t")
+            taxo_data.append(dict(zip(headers, values)))
+    html += '''
+    <h3>Peptide Table</h3>
+    <p>editable preview of the results file. <b>Use the bouton bellow to download edited result.</b></p>
+    <div id="myGrid" style="height: 500px; margin-bottom: 20px;" class="ag-theme-alpine"></div>
+    <div style="margin-top: 10px;">
+        <button onclick="gridApi.exportDataAsCsv()">Download Results (.TSV)</button>
+    </div>
+
+    <script src="https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js"></script>
+    <script type="text/javascript">
+    function initGrid() {
+            let gridApi;
+            const originalData = ''' + json.dumps(taxo_data) + ''';
+    
+            const gridOptions = {
+                rowData: originalData,
+                columnDefs: ''' + json.dumps([{"field": h, "editable": True} for h in headers]) + ''',
+                pagination: true,
+                paginationPageSize: 20,
+                defaultColDef: {
+                    sortable: true,
+                    filter: true,
+                    resizable: true,
+                    editable: true
+                },
+                onCellValueChanged: function(event) {
+                    console.log('Cell modified:', event);
+                },
+                enableCellChangeFlash: true
+            };
+    
+            function exportData() {
+                gridApi.exportDataAsCsv({
+                    fileName: 'taxonomy_export.tsv'
+                });
+            }
+    
+            gridApi = agGrid.createGrid(document.querySelector("#myGrid"), gridOptions);}
+        if(document.readyState === 'loading') {
+    document.addEventListener('DOMContentLoaded', initGrid);
+        } else {
+    initGrid();
+    }
+    
+    </script>
+    <div id="status" style="margin-top: 10px; color: #666;"></div>
+    '''
+
+    # Download section
+    html += '''
+    <h3>Download</h3>''' + results_output(run_id, job_name=job_name) + '<br>'
+
+    # Retrieve results
+    html += f'''
+    <h3>Retrieve results with an ID</h3>
+    <p>Your result remains available for at least one month.</p>
+    <p>To access it, you can either save the URL or <a id="copy" data-copy="{run_id}">copy the ID</a> {run_id}.</p>
+    <br>'''
+
+    # Insert page footer
+    html += '</div></div>'
+    html += open(f"{common.HTML_PATH}/footer.php", "r").read()
+    html += '</body></html>'
+
+    # Save the HTML file
+    open(common.RESULT_DIR + run_id + "/results.php", "w").write(html)
+
+
+
+def write_no_results_page(run_id, job_name=None):
+    """Write a HTML page. The result page shown first when the PAMPA analysis is done."""
+    html = ""
+
+    # Insert HTML header and page head
+    html += (open(f"{common.HTML_PATH}/header.php", "r").read())
+    html += '<div class="frametitle"><h1 id="title">Pampa</h1></div><div id="center_sup"><div class="theme-border" style="display:none"></div><div id="link_home" style="display:inline-block"><a href="/" class="text_onglet"><img src="/Style/icon/home_w.png" alt="home_general"/></a></div><div class="tabs" id="menu_central" style="display:inline-block">'
+    html += open(f"{common.HTML_PATH}/menu_central.txt", 'r').read()
+    html += '</div></div><div id="main"><div id="center">'
+
+    html += (f'<h2>Results for job {run_id}{f" ({job_name})" if job_name else ""}</h2>')
+
+    html += '<h4><font color="red">No results</font></h4>'
+    open(common.RESULT_DIR + run_id + "/results.php", "w").write(html)
+
+
+# Main program
+def main():
+    # Ce script est lancé avec un paramètre : le nom d'un fichier json
+    # ce fichier contient diverses informations qui vont guider la construction de la page de résultats.
+    json_params_file = sys.argv[1]
+    params = json.load(open(json_params_file, "r"))
+    run_id = params["run_id"]
+
+    if "job_name" in params:
+        job_name = params['job_name']
+    else:
+        job_name = None
+    table_files = params["peptides_path"]
+    fasta_file = params["sequences_files"]
+
+    zip_results(["out_"+run_id+".tsv", "report_out_"+run_id+".txt"], run_id, job_name=job_name)
+
+    write_main_page(run_id, common.RESULT_DIR+ run_id + "/out_"+run_id+".tsv", job_name=job_name)
+
+
+main()
+
diff --git a/www/cgi-bin/pampa/pampa-craft.py b/www/cgi-bin/pampa/pampa-craft.py
new file mode 100644
index 0000000..fe2a6d0
--- /dev/null
+++ b/www/cgi-bin/pampa/pampa-craft.py
@@ -0,0 +1,226 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+import cgitb;
+
+cgitb.enable()
+import os
+import cgi
+import json
+import re
+import sys
+import common
+import shutil
+import zipfile
+from tools import FormTools
+
+# Fonction permettant de vérifier les données soumises dans le formulaire, retour dans un dictionnaire (req)
+# Fonction qui sera modifiée pour le traitement de vos données
+def extract_request(form):
+    """ check the validity of the different fields of form
+
+    Cette section vérifie la validité des argument soumis et les conditionne afin de lancer PAMPA comme souhaité.
+    """
+    error_found = False
+    error_messages = []
+    req = {}
+
+    # fichier de log où les points importants du traitement de la requête seront incris
+    log = open('log.txt', 'w')
+    log.write(f'Starting log file\n')  # log
+
+    # Sauvegarde :
+    #       - fichiers CSV spectra dans le dossier result/run_id/spectra/
+    #       - fichiers FASTA sequences dans le dossier result/run_id/sequences/
+    #       - fichier TSV peptides_table dans result/run_id/
+    #       - fichier TSV taxonomy (optionnel ?) dans result/run_id/
+
+    # Nom du fichier output fixé a partir du run_id
+
+    # Resolution par defaut 0.01 ?
+
+    # dossier de résultats utilisateur
+    resdir = common.RESULT_DIR + form['run_id'].value
+    log.write(f"Result directory : {resdir}\n")
+
+    # table des peptides par défaut
+    # attention: certains de ces chemins (path) renvoient vers des fichiers qui n'existent peut-être pas encore par manque de données (ex: birds, reptiles, fish)
+    peptides_path_dict = {"mammals": common.PEPTIDES_MAMMALS_FILE, "birds": common.PEPTIDES_BIRDS_FILE,
+                          "fishes": common.PEPTIDES_FISHES_FILE, "reptiles": common.PEPTIDES_REPTILES_FILE}
+
+    req['files_uploaded'] = []  # for the render_result
+
+    # req['pampa_version'] = form['pampa_version']
+
+    # Job name parsing (optionnal)
+    if "job_name" in form:
+        job_name = form["job_name"].value.strip().replace(' ', '_')
+        if job_name == "":
+            log.write("Job name: None\n")  # log
+        elif len(job_name) <= 30:
+            req['job_name'] = job_name
+            log.write(f"Job name: {job_name}\n")  # log
+        else:
+            error_messages.append(f"The job name cannot exceed 30 characters. Current size: {len(job_name)}.")
+    else:
+        log.write("Job name: None\n")
+
+    if 'peptides_file' in form and form['peptides_file'].filename != "":
+        f = form['peptides_file'].filename
+        fn = os.path.basename(f)
+        uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['peptides_file']], resdir, extensions_list=[".tsv"],
+                                                              unzip=False)
+        req["peptides_path"] = resdir + '/' + fn  # for the command
+        req['files_uploaded'].extend(uploaded)  # for the render_result
+        for fn in uploaded:
+            log.write(f"file {fn}\n")
+        for fn in not_uploaded:
+            error_messages.append(f"{fn} : The taxonomy file must be in TSV format.")
+    else:
+        error_messages.append("If you choose to upload your own taxonomy, a taxonomy file is required.")
+
+    # Spectra files parsing
+    rel_sequences_dir = "sequences"
+    abs_sequences_dir = os.path.join(resdir, rel_sequences_dir)  # destination directory
+    req['sequences_dir'] = abs_sequences_dir  # for the command
+    log.write(f"sequences_dir: {req['sequences_dir']}\n")  # log
+
+    if 'spectra_example_1' in form:
+        # Cas où les spectres à analyser sont les spectres de l'exemple 1 (copie de fichiers interne plutôt qu'upload des spectres utilisateur)
+        FormTools.extract_files_from_list([common.SPECTRA_EXAMPLE_1])
+    elif 'sequences_files' in form:
+        # spectres utilisateurs
+        fileitems = form['sequences_files']
+        if not isinstance(fileitems, list):
+            fileitems = [fileitems]
+        if fileitems[0].filename == "":
+            error_messages.append("At least one FASTA file is required.")
+        else:
+            uploaded, not_uploaded = FormTools.extract_files_from_fileitems(fileitems, abs_sequences_dir,
+                                                                  extensions_list=[".txt", ".fasta"], unzip=True)
+            req['files_uploaded'].extend(uploaded)
+            for fn in uploaded:
+                log.write(f"file {fn}\n")
+                req['sequences_file'] = fn
+            for fn in not_uploaded:
+                if fn in uploaded:
+                    error_messages.append(
+                        f'The File {fn} appears to be present several times in the data supplied.')
+                else:
+                    error_messages.append(f"{fn} : Sequences must be in FASTA or in a ZIP archive.")
+    else:
+        error_messages.append("At least one spectrum is required.")
+
+    if 'limit_file' in form and form['limit_file'].filename != "":
+        f = form['limit_file'].filename
+        fn = os.path.basename(f)
+        uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['limit_file']], resdir, extensions_list=[".txt"],
+                                                              unzip=False)
+        req["limit_path"] = resdir + '/' + fn  # for the command
+        req['files_uploaded'].extend(uploaded)  # for the render_result
+        for fn in uploaded:
+            log.write(f"file {fn}\n")
+        for fn in not_uploaded:
+            error_messages.append(f"{fn} : The taxonomy file must be in TSV format.")
+
+
+    log.write("Recap downloads :\n")
+    for fn in req['files_uploaded']:
+        log.write(f"dwl {fn}\n")
+
+    req["error_messages"] = error_messages
+    log.write("Error messages :\n")
+    for e in error_messages:
+        log.write(f"error: {e}\n")
+    log.close()
+    return req
+
+
+# Lance le programme en fonction des données contenant dans req
+# Fonction à modifier pour l'adapter à votre programme
+def launch_software(run_id, req):
+    # Build the command line for PAMPA
+    peptides_arg = req['peptides_path']
+
+    output_arg = "out_" + run_id + ".tsv"
+
+    sequences_arg = req["sequences_dir"]
+
+    command = "python pampa_craft.py --homology -p " + peptides_arg + " -d " + sequences_arg + " -o " + common.RESULT_DIR + run_id + "/" + output_arg
+    if 'limit_path' in req:
+        limit_arg = req["limit_path"]
+        command += " -l " + limit_arg
+    command += " --web"
+    open("acommand.txt", "w").write(f"{command}")  # log
+
+    # Launch the analysis
+    cwd = format(os.getcwd())
+    os.chdir(format(os.getcwd()) + "/pampa/")
+    os.system(command)
+    os.chdir(cwd)
+
+    # Écrit un fichier de résultats d'erreurs si PAMPA a planté, sinon affiche les résultats
+    if open(common.RESULT_DIR + run_id + "/error.log", "r").read() != "":
+        # Manage PAMPA errors (error.log)
+        json_file = common.RESULT_DIR + run_id + "/render_error_pampa.json"
+        os.system(f"python render_error_pampa.py {run_id}")
+
+    else:
+        # Params in json for render_result.py -> le json est écrit dans le dossier result/{run_id}
+        json_file = common.RESULT_DIR + run_id + "/render_result_params.json"
+        render_result_dict = {"run_id": run_id}
+        if "job_name" in req:
+            render_result_dict["job_name"] = req['job_name']  # nom du job
+        if "peptides_path" in req:
+            render_result_dict["peptides_path"] = req['peptides_path']
+        if "sequences_dir" in req:
+            render_result_dict["sequences_files"] = req['sequences_dir']  # tables de peptides utilisées
+
+        json.dump(render_result_dict, open(json_file, "w"))
+        os.system(f"python craft_render_result.py {json_file}")
+
+
+# Vérifie si il n'y a pas d'erreur au retour des données soumises par le formulaire
+def process_request(form):
+    run_id = form['run_id'].value
+    error = 0
+    error_page = []
+
+    req = extract_request(form)
+
+    if len(req['error_messages']) > 0:
+        error_page = req['error_messages']
+        error = 1
+        json_file = common.RESULT_DIR + run_id + "/render_error_params.json"
+        render_error_dict = {"result_path": common.RESULT_DIR, "run_id": run_id, "error_list": req['error_messages']}
+        json.dump(render_error_dict, open(json_file, "w"))
+        os.system(f"python render_error.py {json_file}")
+    else:
+        launch_software(run_id, req)
+
+    return (error, error_page)
+
+
+def main():
+    fs = cgi.FieldStorage()
+
+    error, error_page = process_request(fs)
+
+    sys.stdout.write("Content-Type: application/json")
+
+    sys.stdout.write("\n")
+    sys.stdout.write("\n")
+
+    result = {}
+    if error == 0 or error == 1:
+        result['success'] = True
+        result['run_id'] = fs['run_id'].value
+        result['path_result'] = common.RESULT_DIR + fs['run_id'].value + "/results.php"
+    else:
+        result['success'] = False
+
+    sys.stdout.write(json.dumps(result, indent=1))
+    sys.stdout.write("\n")
+
+
+main()
+
diff --git a/www/html/pampa/craft-form.php b/www/html/pampa/craft-form.php
new file mode 100644
index 0000000..7e0f061
--- /dev/null
+++ b/www/html/pampa/craft-form.php
@@ -0,0 +1,139 @@
+<?php include('header.php') ?>
+
+
+<body>
+  <div class="frametitle">
+   <h1 id="title">Pampa-Craft</h1>
+  </div>
+
+  <div id="center_sup">
+     <div class="theme-border" style="display:none"></div>
+     <div id="link_home" style="display:inline-block"><a href="/" class="text_onglet"><img src="/Style/icon/home_w.png" alt="home_general"/></a></div>
+   <div class="tabs" id="menu_central" style="display:inline-block"><?php include("menu_central.txt")?></div>
+  </div>
+<div id="main">
+  <div id="center">
+
+    <form id="formulaire-craft" method="post" enctype="multipart/form-data">
+
+    <input type="hidden" name="pampa_version" value="assign"/>
+
+    <div class="formulaire">
+      <table class="vide pampa_choice">
+        <tr>
+          <td class="label">
+            <h2>Name of the job <span style="font-weight: normal">(optional) :</span></h2>
+          </td>
+          <td>
+            <input type="text" name="job_name" maxlength="30"/>
+          </td>
+        </tr>
+      </table>
+    </div>
+
+    <table class="vide table-center pampa-choice">
+
+    <tr>
+      <td>
+        <div>
+          <input type="radio" id="allpeptides" name="drone" value="allpeptides" checked/>
+          <label for="allpeptides">Allpeptides</label>
+        </div>
+      </td>
+      <td>
+        <div>
+          <input type="radio" id="deamidation" name="drone" value="deamidation"/>
+          <label for="deamidation">Deamidation</label>
+        </div>
+      </td>
+      <td>
+        <div>
+          <input type="radio" id="fillin" name="drone" value="fillin"/>
+          <label for="fillin">Fillin</label>
+        </div>
+      </td>
+      <td>
+        <div>
+          <input type="radio" id="homology" name="drone" value="homology"/>
+          <label for="homology">Homology</label>
+        </div>
+      </td>
+      <td>
+        <div>
+          <input type="radio" id="selection" name="drone" value="selection"/>
+          <label for="selection">Selection</label>
+        </div>
+      </td>
+    </table>
+    <br>
+
+    <div class="formulaire">
+      <h2>Peptides Table</h2>
+      <table class="vide">
+        <tr>
+          <td class="label">
+            <B>Upload</B> the peptides table that you want to complete in (.csv or .tsv)
+          </td>
+        </tr>
+        <tr id="peptide_input">
+          <td>
+            <input type="file" name="peptides_file" value="file" accept=".tsv" multiple required/>
+          </td>
+        </tr>
+      </table>
+      <br>
+    </div>
+
+    <div class="formulaire">
+      <h2>Fasta File</h2>
+        <table class="vide">
+          <tr>
+            <td class="label">
+              <B>Upload</B> The fasta file that you will use to complete your table.
+            </td>
+          </tr>
+          <tr id="sequences_input">
+            <td>
+              <input type="file" name="sequences_files" value="file" accept=".fasta,.zip" multiple required/>
+            </td>
+          </tr>
+        </table>
+        <br>
+
+      <b>Limit File</b>
+        <table class="vide">
+          <tr>
+            <td class="label">
+              <B>Upload</B> The limit file (optional, doc : https://github.com/touzet/pampa/wiki/Limiting-searches).
+            </td>
+          </tr>
+          <tr id="limit_input">
+            <td>
+              <input type="file" name="limit_file" value="file" accept=".txt"/>
+            </td>
+          </tr>
+        </table>
+     </div>
+
+    <div class="center">
+      <input type="submit" id="reset-craft" name="reset" value="Reset" />
+      <input type="submit" id="run" name="button" value="Run" />
+      <input type="hidden" name="command" value="request" />
+    </div>
+
+  </form>
+
+</div><!--bloc -->
+</div><!-- main-->
+
+<!-- chargement de la librairie php lib.inc -->
+   <?php require("../lib.inc")?>
+<!-- appel de la fonction footer qui permet d'afficher au bas de la page (nom du logiciel, un lien vers le mail, la date de modif -->
+<!-- A modifier en fonction de votre logiciel -->
+   <?php footer("Pampa","Pampa", "areski.flissi@univ-lille.fr","2025"); ?>
+
+
+
+</body>
+</html>
+
-- 
GitLab


From 59f8a841562fcf540e61fe232ebcd4d0d2e3608c Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Tue, 11 Mar 2025 15:42:07 +0100
Subject: [PATCH 02/59] add tools.py to avoid code duplication (need pampa.py
 to be modified)

---
 www/cgi-bin/pampa/tools.py | 119 +++++++++++++++++++++++++++++++++++++
 1 file changed, 119 insertions(+)
 create mode 100644 www/cgi-bin/pampa/tools.py

diff --git a/www/cgi-bin/pampa/tools.py b/www/cgi-bin/pampa/tools.py
new file mode 100644
index 0000000..dc7d2cf
--- /dev/null
+++ b/www/cgi-bin/pampa/tools.py
@@ -0,0 +1,119 @@
+import os
+import zipfile
+import cgi
+import re
+import common
+
+class FormTools():
+
+    @classmethod
+    def verif_mail(cls, mail):
+        """ check the validity of email address """
+
+        res = mail.strip()
+        if res.find(' ') > 0:
+            return False
+        a = res.find('@')
+        if a <= 0:
+            return False
+        if res.find('@', a + 1) > 0:
+            return False
+        return True
+
+    @classmethod
+    def extract_files_from_fileitems(cls, fileitems, final_dir, extensions_list=None, unzip=False):
+        """
+        Cette fonction permet l'extraction de fichiers et leur copie vers le répertoire de résultats de l'analyse (dossier 'result/{run_id}').
+        Ces fichiers ont été fournie par l'utilisateur, le paramètre 'fileitems' dont correspondre à un objet fileitems (type spécifique associé au téléchargement de fichiers). Ils serviront dans l'analyse de PAMPA.
+        Cette fonction permet de faire un filtre entre les fichiers dont l'extension est valide (extensions passés en arguments).
+        Les fichiers peuvent être contenus dans un fichier ZIP.
+        """
+        files_moved = []
+        files_not_moved = []
+
+        final_dir = final_dir.rstrip('/')
+        if not os.path.exists(final_dir):
+            os.mkdir(final_dir)
+
+        if extensions_list:
+            extensions = "|".join(extensions_list)
+
+        for f in fileitems:
+            fn = os.path.basename(f.filename)
+            re_extension = re.compile(os.path.splitext(fn)[1], re.IGNORECASE)  # for extension check
+            if unzip and re_extension.search(".zip"):
+                with zipfile.ZipFile(f.file, "r") as myzip:
+                    for element in myzip.filelist:
+                        fname = element.filename
+                        re_extension = re.compile(os.path.splitext(fname)[1], re.IGNORECASE)  # for extension check
+                        if (extensions_list is None or re_extension.search(extensions)) and not os.path.exists(
+                                final_dir + '/' + fname):
+                            open(final_dir + '/' + fname, 'wb').write(myzip.open(fname, "r").read())  # cp file
+                            files_moved.append(fname)
+                        elif fname[-1] == "/":
+                            pass  # if a directory is in the zipfile.filelist: pass (no recursivity)
+                        else:
+                            files_not_moved.append(fname)
+            elif (extensions_list is None or re_extension.search(extensions)) and not os.path.exists(
+                    final_dir + '/' + fn):
+                open(final_dir + '/' + fn, 'wb').write(f.file.read())  # cp file
+                files_moved.append(fn)
+            else:
+                files_not_moved.append(fn)
+
+        return files_moved, files_not_moved
+
+    @classmethod
+    def extract_files_from_list(cls, filename_list, final_dir):
+        """
+        Déplacement de fichiers internes.
+        l'unzip est systématique
+        """
+        final_dir = final_dir.rstrip('/')
+        if not os.path.exists(final_dir):
+            os.mkdir(final_dir)
+
+        for fn in filename_list:
+            if os.path.exists(fn):
+                re_extension = re.compile(os.path.splitext(fn)[1], re.IGNORECASE)  # to detect ZIP archive
+                if re_extension.search(".zip"):
+                    with zipfile.ZipFile(fn, "r") as myzip:
+                        for element in myzip.filelist:
+                            fname = element.filename
+                            if fname[-1] != "/":  # not a directory
+                                open(final_dir + '/' + fname, 'wb').write(myzip.open(fname, "r").read())  # cp file
+                else:
+                    open(final_dir + '/' + os.path.basename(fn), 'w').write(open(fn, "r").read())  # cp file
+        return None
+
+
+class ResultTools():
+
+    @classmethod
+    def href_dl(cls, link, name):
+        """
+        Construit une balise <a> ouvrant la page sur un nouvel onglet.
+        """
+        result = f'<a href="{link}" download="{name}" target="_blank">{name}</a>'
+        return result
+
+    @classmethod
+    def zip_results(cls, file_names, run_id, job_name=None):
+        """
+        Produit un fichier ZIP contenant tous les fichiers de résultat :
+        - out.tsv
+        - detail_out.tsv
+        - report_out.txt
+        Ce fichier ZIP pourra être téléchargé par l'utilisateur.
+        """
+        with zipfile.ZipFile(f"{common.RESULT_DIR}{run_id}/data_result_{run_id}.zip", "a",
+                             compression=zipfile.ZIP_DEFLATED) as dwl_zip:
+            for fn in file_names:
+                abs_path = common.RESULT_DIR + run_id + '/' + fn
+                if os.path.exists(abs_path):
+                    if job_name is not None:
+                        parts = os.path.splitext(fn)
+                        dwl_name = parts[0] + "_" + job_name + parts[1]
+                    else:
+                        dwl_name = fn
+                    dwl_zip.write(abs_path, arcname=dwl_name)
\ No newline at end of file
-- 
GitLab


From 6821de271e288ebb7b3c8b10f339c67fc0e340ee Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Tue, 11 Mar 2025 15:43:39 +0100
Subject: [PATCH 03/59] Taking in count #formulaire-craft in pampa.css and
 script.js

---
 www/html/pampa/css/pampa.css |  2 +-
 www/html/pampa/js/script.js  | 28 ++++++++++++++++++----------
 2 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/www/html/pampa/css/pampa.css b/www/html/pampa/css/pampa.css
index 8ceca56..a7352f3 100644
--- a/www/html/pampa/css/pampa.css
+++ b/www/html/pampa/css/pampa.css
@@ -11,7 +11,7 @@ blockquote {
   padding-left: 8px;
 }
 
-.formulaire {
+.formulaire, .formulaire-craft {
     border: 1px solid #CCCCCC;
     color: #000000;
     margin: auto auto 10pt;
diff --git a/www/html/pampa/js/script.js b/www/html/pampa/js/script.js
index 74b14b8..1d3b16e 100644
--- a/www/html/pampa/js/script.js
+++ b/www/html/pampa/js/script.js
@@ -6,17 +6,25 @@ $(document).on("click", "#ex1", function(){
     return false;
 });
 
-$(document).on("click", "#reset", function(){
-    $('#main').load(soft+'form.php #center', function(){});
+function resetForm(formSelector, baseForm) {
+    $(document).on("click", formSelector, function(){
+        $('#main').load(soft+ baseForm + ' #center', function(){});
+        return false;
+    });
+}
+resetForm("#reset", "form.php")
+resetForm("#reset-craft", "craft-form.php")
+
+function formSubmit(formSelector, paramIndex) {
+  $(document).on("submit", formSelector, function() {
+    const param = loadParaSoft(soft);
+    const formData = new FormData(this);
+    loadFormulaire(formData, param[1], param[paramIndex]);
     return false;
-});
-
-$(document).on("submit", "#formulaire", function(){
-    var param = loadParaSoft(soft);
-    var formData=new FormData(this);
-    loadFormulaire(formData, param[1], param[3]);
-    return false;
-});
+  });
+}
+formSubmit("#formulaire", 3);
+formSubmit("#formulaire-craft", 5);
 
 $(document).on("submit", "#form_id", function(){
     var id = document.getElementById("run_id").value;
-- 
GitLab


From ffa0baab29699d3a6056d202b8547257bd4827b6 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Tue, 11 Mar 2025 15:45:44 +0100
Subject: [PATCH 04/59] Update menu_central.txt and para_software.txt for
 pampa-craft.py

---
 www/html/pampa/menu_central.txt  | 5 ++++-
 www/html/pampa/para_software.txt | 2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/www/html/pampa/menu_central.txt b/www/html/pampa/menu_central.txt
index 52aeb8f..de19f0b 100644
--- a/www/html/pampa/menu_central.txt
+++ b/www/html/pampa/menu_central.txt
@@ -3,7 +3,10 @@
     <a href="/pampa/" class="mc">pampa</a>
   </li>
   <li class="onglet">
-    <a href="/pampa/form.php" class="mc" >web server</a>
+    <a href="/pampa/form.php" class="mc" >Pampa web server</a>
+  </li>
+    <li class="onglet">
+    <a href="/pampa/craft-form.php" class="mc" >Pampa-Craft web server</a>
   </li>
   <li class="onglet">
     <a href="/pampa/help.php" class="mc" >help</a>
diff --git a/www/html/pampa/para_software.txt b/www/html/pampa/para_software.txt
index cd6c039..f5f50e1 100644
--- a/www/html/pampa/para_software.txt
+++ b/www/html/pampa/para_software.txt
@@ -1 +1 @@
-rep_software:pampa:name_wrapper:pampa.py
+rep_software:pampa:name_wrapper:pampa.py:name_wrapper2:pampa-craft.py
-- 
GitLab


From 61624a8646ad93799ad9b045c36fe9a9d21c5423 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Tue, 11 Mar 2025 15:49:33 +0100
Subject: [PATCH 05/59] Add AGgrid in header.php (js package allowing table
 modification)

---
 www/html/pampa/header.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/www/html/pampa/header.php b/www/html/pampa/header.php
index dfb6eba..c155b69 100644
--- a/www/html/pampa/header.php
+++ b/www/html/pampa/header.php
@@ -15,6 +15,7 @@
     <script type="text/javascript" src="/pampa/js/script.js"></script>   
     <script type="text/javascript" src="/pampa/js/pampa.js"></script>  
     <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.1.1.min.js"></script>
+    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js"></script>
     <script type="text/javascript">Bokeh.set_log_level("info");</script>
    <title>Bonsai  :: Bioinformatics Software Server</title>
     <script type="text/javascript">
-- 
GitLab


From e1eca5e724a835133863226b6f17ff4a7994dafa Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Tue, 11 Mar 2025 16:03:50 +0100
Subject: [PATCH 06/59] Update for python 3.12

---
 Dockerfile | 27 ++++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index 88b3ddb..517bcb5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,26 +3,34 @@ FROM ubuntu:20.04
 ARG DEBIAN_FRONTEND=noninteractive
 
 ARG SERVER_NAME=localhost
-
 ARG HTML_ROOT=/var/www/html
 
 RUN apt-get update && apt-get install -y \
-	nginx \
+    nginx \
     php-fpm \
     fcgiwrap \
     spawn-fcgi \
     dialog \
     apt-utils \
-    python3-pip \
     postgresql \
     git \
-	curl \
-	wget \
-	nano \
-    unzip
+    curl \
+    wget \
+    nano \
+    unzip \
+    software-properties-common
+
+# Installer Python 3.12
+RUN add-apt-repository ppa:deadsnakes/ppa -y && \
+    apt-get update && \
+    apt-get install -y python3.12 python3.12-venv python3.12-dev \
+    && python3.12 -m ensurepip
+
+# Créer un lien symbolique pour python
+RUN ln -sf /usr/bin/python3.12 /usr/bin/python3 && ln -sf /usr/bin/python3 /usr/bin/python
 
-# Créer lien sympolique python
-RUN ln -s /usr/bin/python3 /usr/bin/python
+# Installer pip pour Python 3.12
+RUN python3.12 -m pip install --upgrade pip
 
 # Python required modules
 RUN pip install numpy
@@ -34,6 +42,7 @@ RUN pip install scipy
 RUN pip install pandas
 RUN pip install bokeh
 RUN pip install beautifulsoup4
+RUN pip install --force-reinstall pandas
 
 # Chrome driver, selenium, etc.
 RUN pip install imgkit
-- 
GitLab


From f60b3c7371d6bc732e9595c3528d5c3600ec5d65 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Tue, 11 Mar 2025 16:05:25 +0100
Subject: [PATCH 07/59] less steps Dockerfile

---
 Dockerfile | 69 +++++++++++++++++++-----------------------------------
 1 file changed, 24 insertions(+), 45 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index 517bcb5..78877d7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -32,39 +32,24 @@ RUN ln -sf /usr/bin/python3.12 /usr/bin/python3 && ln -sf /usr/bin/python3 /usr/
 # Installer pip pour Python 3.12
 RUN python3.12 -m pip install --upgrade pip
 
-# Python required modules
-RUN pip install numpy
-RUN pip install biopython
-RUN pip install pyteomics
-RUN pip install lxml
-RUN pip install psycopg2-binary
-RUN pip install scipy
-RUN pip install pandas
-RUN pip install bokeh
-RUN pip install beautifulsoup4
+# Installer les modules Python requis
+RUN pip install numpy biopython pyteomics lxml psycopg2-binary scipy pandas bokeh beautifulsoup4 imgkit html2image selenium Pillow
 RUN pip install --force-reinstall pandas
 
-# Chrome driver, selenium, etc.
-RUN pip install imgkit
-RUN pip install html2image
-RUN pip install selenium
-RUN pip install Pillow
-
-RUN wget -P /root/ https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
-RUN apt -y install /root/google-chrome-stable_current_amd64.deb
-
-RUN wget -P /root/ https://storage.googleapis.com/chrome-for-testing-public/123.0.6312.122/linux64/chrome-linux64.zip
-RUN unzip -d /root/ /root/chrome-linux64.zip
-RUN rm /usr/bin/google-chrome
-RUN ln -s /root/chrome-linux64/chrome /usr/bin/google-chrome
-
-RUN wget -P /root/ https://storage.googleapis.com/chrome-for-testing-public/123.0.6312.122/linux64/chromedriver-linux64.zip
-RUN unzip -d /root/ /root/chromedriver-linux64.zip
-RUN ln -s /root/chromedriver-linux64/chromedriver /usr/bin/
-RUN chmod -R 755 /root
-RUN chmod -R 766 /root/.cache
-
-# Configuration nginx, FastCGI et serveurs virtuels
+# Installer Google Chrome et ChromeDriver
+RUN wget -P /root/ https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \
+    apt -y install /root/google-chrome-stable_current_amd64.deb && \
+    wget -P /root/ https://storage.googleapis.com/chrome-for-testing-public/123.0.6312.122/linux64/chrome-linux64.zip && \
+    unzip -d /root/ /root/chrome-linux64.zip && \
+    rm /usr/bin/google-chrome && \
+    ln -s /root/chrome-linux64/chrome /usr/bin/google-chrome && \
+    wget -P /root/ https://storage.googleapis.com/chrome-for-testing-public/123.0.6312.122/linux64/chromedriver-linux64.zip && \
+    unzip -d /root/ /root/chromedriver-linux64.zip && \
+    ln -s /root/chromedriver-linux64/chromedriver /usr/bin/ && \
+    chmod -R 755 /root && \
+    chmod -R 766 /root/.cache
+
+# Configuration de nginx, FastCGI et serveurs virtuels
 COPY ./nginx/fastcgi.conf /etc/nginx/
 COPY ./nginx/fastcgi_params /etc/nginx/
 COPY ./nginx/fcgiwrap.conf /etc/nginx/
@@ -73,28 +58,23 @@ COPY ./nginx/sites-available/pampa /etc/nginx/sites-available/pampa
 COPY ./nginx/spawn-fcgi /
 
 RUN sed -i "s/localhost;/localhost $SERVER_NAME;/g" /etc/nginx/sites-available/pampa
-
 RUN ln -s /etc/nginx/sites-available/pampa /etc/nginx/sites-enabled/pampa
 
-# Configuration postgresql
+# Configuration PostgreSQL
 COPY ./postgresql/pg_hba.conf /etc/postgresql/12/main/
 RUN chown postgres /etc/postgresql/12/main/pg_hba.conf
 
-
-# Copie des répertoires html et cgi-bin
+# Copier les répertoires html et cgi-bin
 COPY ./www/html/ /var/www/html/
 RUN mkdir /var/www/cgi-bin
 COPY ./www/cgi-bin/ /var/www/cgi-bin/
 RUN chown -R www-data:www-data /var/www
 
-# on récupère le logiciel pampa depuis GitHub
-# désactivation temporaire pour ne pas mettre à jour la version de pampa
-# RUN git clone https://github.com/touzet/pampa.git /var/www/cgi-bin/pampa/pampa/
-# ajustement pour version web : pas de print sur sdtout mais envoie d'une réponse CGI de type content-type/html
+# Copier le logiciel PAMPA
 COPY ./pampa /var/www/cgi-bin/pampa/pampa
 COPY ./main_taxonomy_filtering.py /var/www/cgi-bin/pampa/pampa/
 
-# Ajustage des droits
+# Ajustement des permissions
 RUN chmod +x /var/www/cgi-bin/pampa/*
 RUN mkdir /var/www/cgi-bin/pampa/tmp
 RUN mkdir /var/www/html/pampa/result
@@ -102,14 +82,13 @@ RUN chmod -R 755 /var/www/cgi-bin/pampa/tmp
 RUN chown -R www-data:www-data /var/www/cgi-bin/pampa
 RUN chown -R www-data:www-data /var/www/html/pampa
 
-# Copie de la bdd
+# Copier la base de données
 COPY ./postgresql /var/www/cgi-bin/pampa/postgresql
 RUN chmod +x /var/www/cgi-bin/pampa/postgresql/*
 RUN chown postgres:postgres /var/www/cgi-bin/pampa/postgresql
 
-# Expose le port 80
+# Exposer le port 80
 EXPOSE 80
 
-# Lancer les différents services au démmarrage (nginx, php-fpm, fastcgiwrap)
-CMD service php7.4-fpm start && /spawn-fcgi && /etc/init.d/postgresql start && /var/www/cgi-bin/pampa/postgresql/create_pampa_db && /usr/bin/python /var/www/cgi-bin/pampa/postgresql/fill_taxo.py /var/www/html/pampa/data_pampa/taxonomy_reduced.tsv reduced && /usr/bin/python /var/www/cgi-bin/pampa/postgresql/json_taxo.py /var/www/html/pampa/data_pampa reduced && chown www-data:www-data -R /var/run/fcgiwrap.socket &&  chmod 777 /var/run/fcgiwrap.socket &&  nginx -g "daemon off;"
-
+# Lancer les services au démarrage
+CMD service php7.4-fpm start && /spawn-fcgi && /etc/init.d/postgresql start && /var/www/cgi-bin/pampa/postgresql/create_pampa_db && python3 /var/www/cgi-bin/pampa/postgresql/fill_taxo.py /var/www/html/pampa/data_pampa/taxonomy_reduced.tsv reduced && python3 /var/www/cgi-bin/pampa/postgresql/json_taxo.py /var/www/html/pampa/data_pampa reduced && chown www-data:www-data -R /var/run/fcgiwrap.socket && chmod 777 /var/run/fcgiwrap.socket && nginx -g "daemon off;"
\ No newline at end of file
-- 
GitLab


From add7221c25ef302652bac8e1c2ac03d6b9e1b167 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Tue, 11 Mar 2025 16:05:44 +0100
Subject: [PATCH 08/59] Add docker-compose.yml for fast restart

---
 docker-compose.yml | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 docker-compose.yml

diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..22bc743
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,19 @@
+version: '3.8'
+
+services:
+  pampa:
+    build:
+      context: .
+      args:
+        - DEBIAN_FRONTEND=noninteractive
+        - SERVER_NAME=localhost
+    ports:
+      - "80:80"
+    volumes:
+      - postgres_data:/var/lib/postgresql/12/main
+    environment:
+      - SERVER_NAME=localhost
+    restart: unless-stopped
+
+volumes:
+  postgres_data:
\ No newline at end of file
-- 
GitLab


From adf0ce0e240ab15f52892916979aac49993cb798 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Tue, 11 Mar 2025 16:29:52 +0100
Subject: [PATCH 09/59] "Remove redundant parentheses"

---
 www/cgi-bin/pampa/tools.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/www/cgi-bin/pampa/tools.py b/www/cgi-bin/pampa/tools.py
index dc7d2cf..fdc0f3b 100644
--- a/www/cgi-bin/pampa/tools.py
+++ b/www/cgi-bin/pampa/tools.py
@@ -4,7 +4,7 @@ import cgi
 import re
 import common
 
-class FormTools():
+class FormTools:
 
     @classmethod
     def verif_mail(cls, mail):
@@ -87,7 +87,7 @@ class FormTools():
         return None
 
 
-class ResultTools():
+class ResultTools:
 
     @classmethod
     def href_dl(cls, link, name):
-- 
GitLab


From 7c113d2deb69e3bbbbee808663595295c213e9f2 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 12 Mar 2025 16:47:03 +0100
Subject: [PATCH 10/59] add homology example

---
 www/cgi-bin/pampa/pampa-craft.py              | 51 +++++++------------
 www/html/pampa/craft-form.php                 | 20 +++++++-
 www/html/pampa/data_pampa/craft_peptides.tsv  | 12 +++++
 .../pampa/data_pampa/craft_sequences.fasta    |  4 ++
 4 files changed, 53 insertions(+), 34 deletions(-)
 create mode 100644 www/html/pampa/data_pampa/craft_peptides.tsv
 create mode 100644 www/html/pampa/data_pampa/craft_sequences.fasta

diff --git a/www/cgi-bin/pampa/pampa-craft.py b/www/cgi-bin/pampa/pampa-craft.py
index fe2a6d0..e26af6b 100644
--- a/www/cgi-bin/pampa/pampa-craft.py
+++ b/www/cgi-bin/pampa/pampa-craft.py
@@ -29,66 +29,51 @@ def extract_request(form):
     log.write(f'Starting log file\n')  # log
 
     # Sauvegarde :
-    #       - fichiers CSV spectra dans le dossier result/run_id/spectra/
     #       - fichiers FASTA sequences dans le dossier result/run_id/sequences/
     #       - fichier TSV peptides_table dans result/run_id/
-    #       - fichier TSV taxonomy (optionnel ?) dans result/run_id/
-
+    #       - fichier txt limit dans result/run_id
     # Nom du fichier output fixé a partir du run_id
 
-    # Resolution par defaut 0.01 ?
-
     # dossier de résultats utilisateur
     resdir = common.RESULT_DIR + form['run_id'].value
     log.write(f"Result directory : {resdir}\n")
 
-    # table des peptides par défaut
-    # attention: certains de ces chemins (path) renvoient vers des fichiers qui n'existent peut-être pas encore par manque de données (ex: birds, reptiles, fish)
-    peptides_path_dict = {"mammals": common.PEPTIDES_MAMMALS_FILE, "birds": common.PEPTIDES_BIRDS_FILE,
-                          "fishes": common.PEPTIDES_FISHES_FILE, "reptiles": common.PEPTIDES_REPTILES_FILE}
-
-    req['files_uploaded'] = []  # for the render_result
-
-    # req['pampa_version'] = form['pampa_version']
-
-    # Job name parsing (optionnal)
+    req['files_uploaded'] = []
     if "job_name" in form:
         job_name = form["job_name"].value.strip().replace(' ', '_')
         if job_name == "":
-            log.write("Job name: None\n")  # log
+            log.write("Job name: None\n")
         elif len(job_name) <= 30:
             req['job_name'] = job_name
-            log.write(f"Job name: {job_name}\n")  # log
+            log.write(f"Job name: {job_name}\n")
         else:
             error_messages.append(f"The job name cannot exceed 30 characters. Current size: {len(job_name)}.")
     else:
         log.write("Job name: None\n")
 
-    if 'peptides_file' in form and form['peptides_file'].filename != "":
+    if 'craft-example' in form:
+        FormTools.extract_files_from_list([common.CRAFT_PEPTIDE], resdir)
+        req["peptides_path"] = resdir + '/' + "craft_peptides.tsv"
+    elif 'peptides_file' in form and form['peptides_file'].filename != "":
         f = form['peptides_file'].filename
         fn = os.path.basename(f)
         uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['peptides_file']], resdir, extensions_list=[".tsv"],
                                                               unzip=False)
-        req["peptides_path"] = resdir + '/' + fn  # for the command
-        req['files_uploaded'].extend(uploaded)  # for the render_result
+        req["peptides_path"] = resdir + '/' + fn
+        req['files_uploaded'].extend(uploaded)
         for fn in uploaded:
             log.write(f"file {fn}\n")
         for fn in not_uploaded:
-            error_messages.append(f"{fn} : The taxonomy file must be in TSV format.")
-    else:
-        error_messages.append("If you choose to upload your own taxonomy, a taxonomy file is required.")
+            error_messages.append(f"{fn} : The peptide table file must be in TSV format.")
 
-    # Spectra files parsing
     rel_sequences_dir = "sequences"
-    abs_sequences_dir = os.path.join(resdir, rel_sequences_dir)  # destination directory
-    req['sequences_dir'] = abs_sequences_dir  # for the command
-    log.write(f"sequences_dir: {req['sequences_dir']}\n")  # log
+    abs_sequences_dir = os.path.join(resdir, rel_sequences_dir)
+    req['sequences_dir'] = abs_sequences_dir
+    log.write(f"sequences_dir: {req['sequences_dir']}\n")
 
-    if 'spectra_example_1' in form:
-        # Cas où les spectres à analyser sont les spectres de l'exemple 1 (copie de fichiers interne plutôt qu'upload des spectres utilisateur)
-        FormTools.extract_files_from_list([common.SPECTRA_EXAMPLE_1])
+    if 'craft-example' in form:
+        FormTools.extract_files_from_list([common.CRAFT_SEQUENCES], abs_sequences_dir)
     elif 'sequences_files' in form:
-        # spectres utilisateurs
         fileitems = form['sequences_files']
         if not isinstance(fileitems, list):
             fileitems = [fileitems]
@@ -115,8 +100,8 @@ def extract_request(form):
         fn = os.path.basename(f)
         uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['limit_file']], resdir, extensions_list=[".txt"],
                                                               unzip=False)
-        req["limit_path"] = resdir + '/' + fn  # for the command
-        req['files_uploaded'].extend(uploaded)  # for the render_result
+        req["limit_path"] = resdir + '/' + fn
+        req['files_uploaded'].extend(uploaded)
         for fn in uploaded:
             log.write(f"file {fn}\n")
         for fn in not_uploaded:
diff --git a/www/html/pampa/craft-form.php b/www/html/pampa/craft-form.php
index 7e0f061..20c180e 100644
--- a/www/html/pampa/craft-form.php
+++ b/www/html/pampa/craft-form.php
@@ -80,6 +80,13 @@
             <input type="file" name="peptides_file" value="file" accept=".tsv" multiple required/>
           </td>
         </tr>
+        <tr id="peptide_example" style="display: none;">
+          <td>
+            <input class="file_input" id="back_to_normal" type="button" value="Parcourir..."/>
+            craft-example.tsv
+            <input type="checkbox" style="display: none;" name="craft-example"/>
+          </td>
+        </tr>
       </table>
       <br>
     </div>
@@ -97,6 +104,13 @@
               <input type="file" name="sequences_files" value="file" accept=".fasta,.zip" multiple required/>
             </td>
           </tr>
+          <tr id="sequences_example" style="display: none;">
+            <td>
+              <input class="file_input" id="back_to_normal" type="button" value="Parcourir..."/>
+              craft-example.zip
+              <input type="checkbox" style="display: none;" name="craft-example"/>
+            </td>
+          </tr>
         </table>
         <br>
 
@@ -113,7 +127,11 @@
             </td>
           </tr>
         </table>
-     </div>
+    </div>
+
+    <div class="center">
+      <input type="button" id="example-craft" name="example-craft" value="Example">
+    </div>
 
     <div class="center">
       <input type="submit" id="reset-craft" name="reset" value="Reset" />
diff --git a/www/html/pampa/data_pampa/craft_peptides.tsv b/www/html/pampa/data_pampa/craft_peptides.tsv
new file mode 100644
index 0000000..e52e9d0
--- /dev/null
+++ b/www/html/pampa/data_pampa/craft_peptides.tsv
@@ -0,0 +1,12 @@
+Taxon name	Sequence	PTM	Marker	Gene
+Meles meles	GLPGEFGLPGPAGPR	2H	COL1A2-484/B	COL1A2
+Meles meles	GLPGVSGSVGEPGPLGIAGPPGAR	3H	COL1A2-793/D	COL1A2
+Meles meles	GLTGPIGPPGPAGAPGDKGEAGPSGPAGPTGAR	2H	COL1A1-586/F	COL1A1
+Meles meles	GLTGPIGPPGPAGAPGDKGEAGPSGPAGPTGAR	3H	COL1A1-586/F	COL1A1
+Meles meles	GPNGEAGSTGPSGPPGLR	2H	COL1A2-292/P2	COL1A2
+Meles meles	GPPGESGAAGPSGPIGSR	1H	COL1A2-502/C	COL1A2
+Meles meles	GPSGEAGTAGPPGTPGPQGLLGAPGILGLPGSR	4H	COL1A2-757/G	COL1A2
+Meles meles	GPSGEAGTAGPPGTPGPQGLLGAPGILGLPGSR	5H	COL1A2-757/G	COL1A2
+Meles meles	GVQGPPGPAGPR	1H	COL1A1-508/P1	COL1A1
+Meles meles	TGHPGTVGPAGIR	0H	COL1A2-978/A	COL1A2
+Meles meles	TGHPGTVGPAGIR	1H	COL1A2-978/A	COL1A2
\ No newline at end of file
diff --git a/www/html/pampa/data_pampa/craft_sequences.fasta b/www/html/pampa/data_pampa/craft_sequences.fasta
new file mode 100644
index 0000000..9062c90
--- /dev/null
+++ b/www/html/pampa/data_pampa/craft_sequences.fasta
@@ -0,0 +1,4 @@
+>XP_047563680.1 OS=Lutra lutra OX=9657 GN=COL1A1
+MFSFVDLRLLLLLAATALLTHGQEEGQEEDIPPVTCVQNGLRYYDRDVWKPEACRICVCDNGNVLCDDVICDETKSCPGAQVPPGECCPVCPDGEASPTDQETAGVEGPKGDTGPRGPRGPAGPPGRDGIPGQPGLPGPPGPPGPPGPPGLGGNFAPQMSYGYDEKSTGGISVPGPMGPSGPRGLPGPPGAPGPQGFQGPPGEPGEPGASGPMGPRGPPGPPGKNGDDGEAGKPGRPGERGPPGPQGARGLPGTAGLPGMKGHRGFSGLDGAKGDAGPAGPKGEPGSPGENGAPGQMGPRGLPGERGRPGAPGPAGARGNDGATGAAGPPGPTGPAGPPGFPGAVGAKGEAGPQGARGSEGPQGVRGEPGPPGPAGAAGPAGNPGADGQPGAKGANGAPGIAGAPGFPGARGPSGPQGPSGPPGPKGNSGEPGAPGNKGDTGAKGEPGPTGIQGPPGPAGEEGKRGARGEPGPTGLPGPPGERGGPGSRGFPGADGVAGPKGPAGERGSPGPAGPKGSPGEAGRPGEAGLPGAKGLTGSPGSPGPDGKTGPPGPAGQDGRPGPPGPPGARGQAGVMGFPGPKGAAGEPGKAGERGVPGPPGAVGPAGKDGEAGAQGAPGPAGPAGERGEQGPAGSPGFQGLPGPAGPPGEAGKPGEQGVPGDLGAPGPSGARGERGFPGERGVQGPPGPAGPRGANGAPGNDGAKGDAGAPGAPGSQGAPGLQGMPGERGAAGLPGPKGDRGDAGPKGADGSPGKDGVRGLTGPIGPPGPAGAPGDKGEAGPSGPAGPTGARGAPGDRGEPGPPGPAGFAGPPGADGQPGAKGEPGDAGAKGDAGPPGPAGPTGPPGPIGNVGAPGPKGARGSAGPPGATGFPGAAGRVGPPGPSGNAGPPGPPGPAGKEGGKGPRGETGPAGRPGEVGPPGPPGPAGEKGSPGADGPAGAPGTPGPQGIAGQRGVVGLPGQRGERGFPGLPGPSGEPGKQGPSGASGERGPPGPMGPPGLAGPPGESGREGSPGAEGSPGRDGSPGPKGDRGETGPAGPPGAPGAPGAPGPVGPAGKNGDRGETGPAGPAGPIGPVGARGPTGPQGPRGDKGETGEQGDRGIKGHRGFSGLQGPPGPPGSPGEQGPSGASGPAGPRGPPGSAGSPGKDGLNGLPGPIGPPGPRGRTGDAGPVGPPGPPGPPGPPGPPSGGFDFSFLPQPPQEKAHDGGRYYRADDANVVRDRDLEVDTTLKSLSQQIENIRSPEGSRKNPARTCRDLKMCHSDWKSGEYWIDPNQGCNLDAIKVFCNMETGETCVYPTQPQVAQKNWYISKNPKEKRHVWYGESMTDGFQFEYGGQGSDPADVAIQLTFLRLMSTEASQNITYHCKNSVAYMDQQTGNLKKALLLQGSNEIEIRAEGNSRFTYSVTYDGCTSHTGAWGKTVIEYKTTKTSRLPIIDVAPLDVGAPDQEFGMDIGPVCFL
+>XP_047551750.1 OS=Lutra lutra OX=9657 GN=COL1A2
+MLSFVDTRTLLLLAVTSCLATCQSLQEETARKGPAGDRGPRGERGPPGPPGRDGDDGIPGPPGPPGPPGPPGLGGNFAAQYDPGKGVGLGPGPMGLMGPRGPPGASGAPGPQGFQGPAGEPGEPGQTGPAGARGPPGPPGKAGEDGHPGKPGRPGERGVVGPQGARGFPGTPGLPGFKGIRGHNGLDGLKGQPGAPGVKGEPGAPGENGTPGQTGARGLPGERGRVGAPGPAGARGSDGSVGPVGPAGPIGSAGPPGFPGAPGPKGELGPVGNPGPAGPAGPRGEVGLPGVSGPVGPPGNPGANGLTGAKGAAGLPGVAGAPGLPGPRGIPGPVGAAGATGARGLVGEPGPAGSKGESGNKGEPGSAGPQGPPGPSGEEGKRGPNGEAGSAGPSGPPGLRGSPGSRGLPGADGRAGVMGPPGPRGATGPAGVRGPNGDSGRPGEPGLMGPRGFPGAPGNTGPAGKEGPMGLPGIDGRPGPIGPAGARGEPGNIGFPGPKGPTGDPGKPGEKGHAGLAGARGAPGPDGNNGAQGPPGPQGVQGGKGEQGPAGPPGFQGLPGPAGTAGEVGKPGERGLPGEFGLPGPAGPRGERGPPGESGAAGPSGPIGSRGPSGPPGPDGNKGEPGVLGAPGTAGPSGPGGLPGERGAAGVPGGKGEKGETGLRGEVGNPGRDGARGAPGAVGAPGPAGATGDRGEAGPAGPAGPAGPRGSPGERGEVGPAGPNGFAGPAGAAGQPGAKGERGTKGPKGENGPVGPTGPVGSAGPSGPNGPPGPAGSRGDGGPPGATGFPGAAGRTGPPGPSGITGPPGPPGAAGKEGLRGPRGDQGPVGRTGETGAHGPPGFAGEKGPSGEPGTAGPPGTSGPQGLLGAPGILGLPGSRGERGLPGVSGSVGEPGPLGIAGPPGARGPPGAVGAPGVNGAPGEAGRDGNPGNDGPPGRDGQPGHKGERGYPGNIGPVGAVGAPGPHGPVGPTGKHGNRGEPGPAGSVGPVGAAGPRGPSGPQGVRGDKGEPGDKGPRGLPGLKGHNGLQGLPGLAGQHGDQGAPGSVGPAGPRGPAGPSGPAGKDGRTGHPGTVGPAGIRGSQGSQGPAGPPGPPGPPGPPGPSGGGYDFGYEGDFYRADQPRSPPSLRPKDYEVDATLKSLNNQIETLLTPEGSRKNPARTCRDLRLSHPEWSSGYYWIDPNQGCTMDAIKVYCDFSTGETCIRAQPENIPVKNWYRNSKVKKHTWLGETINGGTQFEYNTEGVTTKEMATQLAFMRLLANHASQNITYHCKNSIAYMDEETGNLKKAVILQGSNDVELVAEGNSRFTYTVLADGCSKKTNEWRKTIIEYKTNKPSRLPILDIAPLDIGGADQEFRVDVGPVCFK
\ No newline at end of file
-- 
GitLab


From 185e6bc6d9640e4df8a3d0556d3eba24841f40a7 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 12 Mar 2025 16:47:26 +0100
Subject: [PATCH 11/59] add homology example

---
 www/html/pampa/js/pampa.js | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/www/html/pampa/js/pampa.js b/www/html/pampa/js/pampa.js
index 5297dc2..ab654a7 100644
--- a/www/html/pampa/js/pampa.js
+++ b/www/html/pampa/js/pampa.js
@@ -94,7 +94,22 @@ $(document).on("click", "#example_1", function() {
         $('input[name="taxonomic_group_selection"][value="fishes"]').attr("checked", false);
         $('input[name="taxonomic_group_selection"][value="reptiles"]').attr("checked", false);
     });
-}); 
+});
+
+$(document).on("click", "#example-craft", function() {
+    $('#main').load(soft+'craft-form.php #center', function(){
+        $("input[name='job_name']").val("example_craft");
+        $("#peptide_input").hide();
+        $("#peptide_input input").attr("required", false);
+        $("#peptide_example").show();
+        $("input[name='craft-example']").attr("checked", true);
+        $("#sequences_input").hide();
+        $("#sequences_input input").attr("required", false);
+        $("#sequences_example").show();
+        $("input[name='craft-example']").attr("checked", true);
+    });
+});
+
 
 $(document).on("click", "#back_to_normal", function() {
     if ($("input[name='job_name']").val() == "example_1") {
-- 
GitLab


From 26f96dfcb24abb10e9de9016e9ef2fdec0b1bfce Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 12 Mar 2025 16:47:35 +0100
Subject: [PATCH 12/59] add homology example

---
 www/cgi-bin/pampa/common.py | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/www/cgi-bin/pampa/common.py b/www/cgi-bin/pampa/common.py
index 1727399..3ca18e7 100644
--- a/www/cgi-bin/pampa/common.py
+++ b/www/cgi-bin/pampa/common.py
@@ -24,6 +24,11 @@ TAXONOMY_REDUCED_FILE = DATA_PAMPA_DIR + "taxonomy_reduced.tsv"
 
 SPECTRA_EXAMPLE_1 = DATA_PAMPA_DIR + "example_1.zip"
 
+# ---- CRAFT ----
+CRAFT_PEPTIDE = DATA_PAMPA_DIR + "craft_peptides.tsv"
+CRAFT_SEQUENCES = DATA_PAMPA_DIR + "craft_sequences.fasta"
+
+
 
 #Renvoie le pwd pour le dossier tmp/
 def tmp_dir(run_id):    
-- 
GitLab


From f7e0e56ffa4ff77bbaa456a9dbd2e1cbf6fda8ea Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 13 Mar 2025 12:09:38 +0100
Subject: [PATCH 13/59] working result page without reload (use iframe)

---
 www/cgi-bin/pampa/craft_render_result.py | 62 +++---------------------
 www/cgi-bin/pampa/table_maker.py         | 58 ++++++++++++++++++++++
 2 files changed, 66 insertions(+), 54 deletions(-)
 create mode 100644 www/cgi-bin/pampa/table_maker.py

diff --git a/www/cgi-bin/pampa/craft_render_result.py b/www/cgi-bin/pampa/craft_render_result.py
index 51bbab4..4c054a3 100644
--- a/www/cgi-bin/pampa/craft_render_result.py
+++ b/www/cgi-bin/pampa/craft_render_result.py
@@ -7,6 +7,7 @@ from html_utils import *
 import common
 import zipfile
 import operator
+import table_maker
 
 """
 NOTES
@@ -111,62 +112,15 @@ def write_main_page(run_id, taxo_used, job_name=None):
                         html += f'<tr><td>{line}</td></tr>'
                 html += '''</table></div><br>'''
 
-    # Load the TSV file and convert to JSON
-    import json
-    taxo_data = []
-    with open(taxo_used, "r") as file:
-        headers = file.readline().strip().split("\t")
-        for line in file:
-            values = line.strip().split("\t")
-            taxo_data.append(dict(zip(headers, values)))
-    html += '''
+    table_dir = common.RESULT_DIR + run_id + "/table.php"
+
+    table_maker.table_maker(taxo_used, table_dir)
+
+    html+= '''        
     <h3>Peptide Table</h3>
-    <p>editable preview of the results file. <b>Use the bouton bellow to download edited result.</b></p>
-    <div id="myGrid" style="height: 500px; margin-bottom: 20px;" class="ag-theme-alpine"></div>
-    <div style="margin-top: 10px;">
-        <button onclick="gridApi.exportDataAsCsv()">Download Results (.TSV)</button>
-    </div>
-
-    <script src="https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js"></script>
-    <script type="text/javascript">
-    function initGrid() {
-            let gridApi;
-            const originalData = ''' + json.dumps(taxo_data) + ''';
-    
-            const gridOptions = {
-                rowData: originalData,
-                columnDefs: ''' + json.dumps([{"field": h, "editable": True} for h in headers]) + ''',
-                pagination: true,
-                paginationPageSize: 20,
-                defaultColDef: {
-                    sortable: true,
-                    filter: true,
-                    resizable: true,
-                    editable: true
-                },
-                onCellValueChanged: function(event) {
-                    console.log('Cell modified:', event);
-                },
-                enableCellChangeFlash: true
-            };
-    
-            function exportData() {
-                gridApi.exportDataAsCsv({
-                    fileName: 'taxonomy_export.tsv'
-                });
-            }
-    
-            gridApi = agGrid.createGrid(document.querySelector("#myGrid"), gridOptions);}
-        if(document.readyState === 'loading') {
-    document.addEventListener('DOMContentLoaded', initGrid);
-        } else {
-    initGrid();
-    }
-    
-    </script>
-    <div id="status" style="margin-top: 10px; color: #666;"></div>
-    '''
+    <p>editable preview of the results file. <b>Use the bouton bellow to download edited result.</b></p>'''
 
+    html += '<iframe src="table.php" style="border: none; width: 100%; height: 575px;"></iframe>'
     # Download section
     html += '''
     <h3>Download</h3>''' + results_output(run_id, job_name=job_name) + '<br>'
diff --git a/www/cgi-bin/pampa/table_maker.py b/www/cgi-bin/pampa/table_maker.py
new file mode 100644
index 0000000..1a2273a
--- /dev/null
+++ b/www/cgi-bin/pampa/table_maker.py
@@ -0,0 +1,58 @@
+import json
+
+def table_maker(taxo_used, output_file):
+    taxo_data = []
+    with open(taxo_used, "r") as file:
+        headers = file.readline().strip().split("\t")
+        for line in file:
+            values = line.strip().split("\t")
+            taxo_data.append(dict(zip(headers, values)))
+    html = '''
+        <div id="myGrid" style="height: 500px; margin-bottom: 20px;" class="ag-theme-alpine"></div>
+        <div style="margin-top: 10px;">
+            <button onclick="gridApi.exportData()">Download Results (.TSV)</button>
+        </div>
+    
+        <script src="https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js"></script>
+        <script type="text/javascript">
+        function initGrid() {
+                let gridApi;
+                const originalData = ''' + json.dumps(taxo_data) + ''';
+    
+                const gridOptions = {
+                    rowData: originalData,
+                    columnDefs: ''' + json.dumps([{"field": h, "editable": True} for h in headers]) + ''',
+                    pagination: true,
+                    paginationPageSize: 20,
+                    defaultColDef: {
+                        sortable: true,
+                        filter: true,
+                        resizable: true,
+                        editable: true
+                    },
+                    onCellValueChanged: function(event) {
+                        console.log('Cell modified:', event);
+                    },
+                    enableCellChangeFlash: true
+                };
+    
+                function exportData() {
+                    gridApi.exportDataAsCsv({
+                        fileName: 'taxonomy_export.tsv'
+                    });
+                }
+    
+                gridApi = agGrid.createGrid(document.querySelector("#myGrid"), gridOptions);}
+            if(document.readyState === 'loading') {
+        document.addEventListener('DOMContentLoaded', initGrid);
+            } else {
+        initGrid();
+        }
+    
+        </script>
+        <div id="status" style="margin-top: 10px; color: #666;"></div>
+        '''
+
+    with open(output_file, 'w') as f:
+        f.write(html)
+
-- 
GitLab


From 9e3ca95cfb5405de734d9b0e0115579ba5a0234c Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 13 Mar 2025 15:44:52 +0100
Subject: [PATCH 14/59] update example autofill, method choice working

---
 www/cgi-bin/pampa/pampa-craft.py |  9 ++++++++-
 www/html/pampa/craft-form.php    | 11 ++++++-----
 www/html/pampa/js/pampa.js       |  1 +
 3 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/www/cgi-bin/pampa/pampa-craft.py b/www/cgi-bin/pampa/pampa-craft.py
index e26af6b..d1ba88e 100644
--- a/www/cgi-bin/pampa/pampa-craft.py
+++ b/www/cgi-bin/pampa/pampa-craft.py
@@ -51,6 +51,11 @@ def extract_request(form):
     else:
         log.write("Job name: None\n")
 
+    if 'method' in form:
+        req["method"] = form['method'].value
+    else:
+        error_messages.append(f"Method is required")
+
     if 'craft-example' in form:
         FormTools.extract_files_from_list([common.CRAFT_PEPTIDE], resdir)
         req["peptides_path"] = resdir + '/' + "craft_peptides.tsv"
@@ -130,7 +135,9 @@ def launch_software(run_id, req):
 
     sequences_arg = req["sequences_dir"]
 
-    command = "python pampa_craft.py --homology -p " + peptides_arg + " -d " + sequences_arg + " -o " + common.RESULT_DIR + run_id + "/" + output_arg
+    method = req['method']
+
+    command = "python pampa_craft.py --" + method + " -p " + peptides_arg + " -d " + sequences_arg + " -o " + common.RESULT_DIR + run_id + "/" + output_arg
     if 'limit_path' in req:
         limit_arg = req["limit_path"]
         command += " -l " + limit_arg
diff --git a/www/html/pampa/craft-form.php b/www/html/pampa/craft-form.php
index 20c180e..be3d75c 100644
--- a/www/html/pampa/craft-form.php
+++ b/www/html/pampa/craft-form.php
@@ -31,36 +31,37 @@
       </table>
     </div>
 
+    <h2>Method</h2>
     <table class="vide table-center pampa-choice">
 
     <tr>
       <td>
         <div>
-          <input type="radio" id="allpeptides" name="drone" value="allpeptides" checked/>
+          <input type="radio" id="allpeptides" name="method" value="allpeptides"/>
           <label for="allpeptides">Allpeptides</label>
         </div>
       </td>
       <td>
         <div>
-          <input type="radio" id="deamidation" name="drone" value="deamidation"/>
+          <input type="radio" id="deamidation" name="method" value="deamidation"/>
           <label for="deamidation">Deamidation</label>
         </div>
       </td>
       <td>
         <div>
-          <input type="radio" id="fillin" name="drone" value="fillin"/>
+          <input type="radio" id="fillin" name="method" value="fillin"/>
           <label for="fillin">Fillin</label>
         </div>
       </td>
       <td>
         <div>
-          <input type="radio" id="homology" name="drone" value="homology"/>
+          <input type="radio" id="homology" name="method" value="homology" checked/>
           <label for="homology">Homology</label>
         </div>
       </td>
       <td>
         <div>
-          <input type="radio" id="selection" name="drone" value="selection"/>
+          <input type="radio" id="selection" name="method" value="selection"/>
           <label for="selection">Selection</label>
         </div>
       </td>
diff --git a/www/html/pampa/js/pampa.js b/www/html/pampa/js/pampa.js
index ab654a7..b73fe38 100644
--- a/www/html/pampa/js/pampa.js
+++ b/www/html/pampa/js/pampa.js
@@ -107,6 +107,7 @@ $(document).on("click", "#example-craft", function() {
         $("#sequences_input input").attr("required", false);
         $("#sequences_example").show();
         $("input[name='craft-example']").attr("checked", true);
+        $("#homology").prop("checked", true);
     });
 });
 
-- 
GitLab


From 30ed76994fd1afcdcdf358d932941faeb43185bf Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 19 Mar 2025 10:04:42 +0100
Subject: [PATCH 15/59] Revert back to the previous version.

---
 Dockerfile | 102 ++++++++++++++++++++++++++++++-----------------------
 1 file changed, 57 insertions(+), 45 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index 78877d7..88b3ddb 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,53 +3,59 @@ FROM ubuntu:20.04
 ARG DEBIAN_FRONTEND=noninteractive
 
 ARG SERVER_NAME=localhost
+
 ARG HTML_ROOT=/var/www/html
 
 RUN apt-get update && apt-get install -y \
-    nginx \
+	nginx \
     php-fpm \
     fcgiwrap \
     spawn-fcgi \
     dialog \
     apt-utils \
+    python3-pip \
     postgresql \
     git \
-    curl \
-    wget \
-    nano \
-    unzip \
-    software-properties-common
-
-# Installer Python 3.12
-RUN add-apt-repository ppa:deadsnakes/ppa -y && \
-    apt-get update && \
-    apt-get install -y python3.12 python3.12-venv python3.12-dev \
-    && python3.12 -m ensurepip
-
-# Créer un lien symbolique pour python
-RUN ln -sf /usr/bin/python3.12 /usr/bin/python3 && ln -sf /usr/bin/python3 /usr/bin/python
-
-# Installer pip pour Python 3.12
-RUN python3.12 -m pip install --upgrade pip
-
-# Installer les modules Python requis
-RUN pip install numpy biopython pyteomics lxml psycopg2-binary scipy pandas bokeh beautifulsoup4 imgkit html2image selenium Pillow
-RUN pip install --force-reinstall pandas
-
-# Installer Google Chrome et ChromeDriver
-RUN wget -P /root/ https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \
-    apt -y install /root/google-chrome-stable_current_amd64.deb && \
-    wget -P /root/ https://storage.googleapis.com/chrome-for-testing-public/123.0.6312.122/linux64/chrome-linux64.zip && \
-    unzip -d /root/ /root/chrome-linux64.zip && \
-    rm /usr/bin/google-chrome && \
-    ln -s /root/chrome-linux64/chrome /usr/bin/google-chrome && \
-    wget -P /root/ https://storage.googleapis.com/chrome-for-testing-public/123.0.6312.122/linux64/chromedriver-linux64.zip && \
-    unzip -d /root/ /root/chromedriver-linux64.zip && \
-    ln -s /root/chromedriver-linux64/chromedriver /usr/bin/ && \
-    chmod -R 755 /root && \
-    chmod -R 766 /root/.cache
-
-# Configuration de nginx, FastCGI et serveurs virtuels
+	curl \
+	wget \
+	nano \
+    unzip
+
+# Créer lien sympolique python
+RUN ln -s /usr/bin/python3 /usr/bin/python
+
+# Python required modules
+RUN pip install numpy
+RUN pip install biopython
+RUN pip install pyteomics
+RUN pip install lxml
+RUN pip install psycopg2-binary
+RUN pip install scipy
+RUN pip install pandas
+RUN pip install bokeh
+RUN pip install beautifulsoup4
+
+# Chrome driver, selenium, etc.
+RUN pip install imgkit
+RUN pip install html2image
+RUN pip install selenium
+RUN pip install Pillow
+
+RUN wget -P /root/ https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
+RUN apt -y install /root/google-chrome-stable_current_amd64.deb
+
+RUN wget -P /root/ https://storage.googleapis.com/chrome-for-testing-public/123.0.6312.122/linux64/chrome-linux64.zip
+RUN unzip -d /root/ /root/chrome-linux64.zip
+RUN rm /usr/bin/google-chrome
+RUN ln -s /root/chrome-linux64/chrome /usr/bin/google-chrome
+
+RUN wget -P /root/ https://storage.googleapis.com/chrome-for-testing-public/123.0.6312.122/linux64/chromedriver-linux64.zip
+RUN unzip -d /root/ /root/chromedriver-linux64.zip
+RUN ln -s /root/chromedriver-linux64/chromedriver /usr/bin/
+RUN chmod -R 755 /root
+RUN chmod -R 766 /root/.cache
+
+# Configuration nginx, FastCGI et serveurs virtuels
 COPY ./nginx/fastcgi.conf /etc/nginx/
 COPY ./nginx/fastcgi_params /etc/nginx/
 COPY ./nginx/fcgiwrap.conf /etc/nginx/
@@ -58,23 +64,28 @@ COPY ./nginx/sites-available/pampa /etc/nginx/sites-available/pampa
 COPY ./nginx/spawn-fcgi /
 
 RUN sed -i "s/localhost;/localhost $SERVER_NAME;/g" /etc/nginx/sites-available/pampa
+
 RUN ln -s /etc/nginx/sites-available/pampa /etc/nginx/sites-enabled/pampa
 
-# Configuration PostgreSQL
+# Configuration postgresql
 COPY ./postgresql/pg_hba.conf /etc/postgresql/12/main/
 RUN chown postgres /etc/postgresql/12/main/pg_hba.conf
 
-# Copier les répertoires html et cgi-bin
+
+# Copie des répertoires html et cgi-bin
 COPY ./www/html/ /var/www/html/
 RUN mkdir /var/www/cgi-bin
 COPY ./www/cgi-bin/ /var/www/cgi-bin/
 RUN chown -R www-data:www-data /var/www
 
-# Copier le logiciel PAMPA
+# on récupère le logiciel pampa depuis GitHub
+# désactivation temporaire pour ne pas mettre à jour la version de pampa
+# RUN git clone https://github.com/touzet/pampa.git /var/www/cgi-bin/pampa/pampa/
+# ajustement pour version web : pas de print sur sdtout mais envoie d'une réponse CGI de type content-type/html
 COPY ./pampa /var/www/cgi-bin/pampa/pampa
 COPY ./main_taxonomy_filtering.py /var/www/cgi-bin/pampa/pampa/
 
-# Ajustement des permissions
+# Ajustage des droits
 RUN chmod +x /var/www/cgi-bin/pampa/*
 RUN mkdir /var/www/cgi-bin/pampa/tmp
 RUN mkdir /var/www/html/pampa/result
@@ -82,13 +93,14 @@ RUN chmod -R 755 /var/www/cgi-bin/pampa/tmp
 RUN chown -R www-data:www-data /var/www/cgi-bin/pampa
 RUN chown -R www-data:www-data /var/www/html/pampa
 
-# Copier la base de données
+# Copie de la bdd
 COPY ./postgresql /var/www/cgi-bin/pampa/postgresql
 RUN chmod +x /var/www/cgi-bin/pampa/postgresql/*
 RUN chown postgres:postgres /var/www/cgi-bin/pampa/postgresql
 
-# Exposer le port 80
+# Expose le port 80
 EXPOSE 80
 
-# Lancer les services au démarrage
-CMD service php7.4-fpm start && /spawn-fcgi && /etc/init.d/postgresql start && /var/www/cgi-bin/pampa/postgresql/create_pampa_db && python3 /var/www/cgi-bin/pampa/postgresql/fill_taxo.py /var/www/html/pampa/data_pampa/taxonomy_reduced.tsv reduced && python3 /var/www/cgi-bin/pampa/postgresql/json_taxo.py /var/www/html/pampa/data_pampa reduced && chown www-data:www-data -R /var/run/fcgiwrap.socket && chmod 777 /var/run/fcgiwrap.socket && nginx -g "daemon off;"
\ No newline at end of file
+# Lancer les différents services au démmarrage (nginx, php-fpm, fastcgiwrap)
+CMD service php7.4-fpm start && /spawn-fcgi && /etc/init.d/postgresql start && /var/www/cgi-bin/pampa/postgresql/create_pampa_db && /usr/bin/python /var/www/cgi-bin/pampa/postgresql/fill_taxo.py /var/www/html/pampa/data_pampa/taxonomy_reduced.tsv reduced && /usr/bin/python /var/www/cgi-bin/pampa/postgresql/json_taxo.py /var/www/html/pampa/data_pampa reduced && chown www-data:www-data -R /var/run/fcgiwrap.socket &&  chmod 777 /var/run/fcgiwrap.socket &&  nginx -g "daemon off;"
+
-- 
GitLab


From b6680a83f2217a6916960d119dfe4b2073a8d18b Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 19 Mar 2025 16:22:24 +0100
Subject: [PATCH 16/59] adaptation of main_taxonomy_filtering.py for the last
 version of pampa

---
 main_taxonomy_filtering.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/main_taxonomy_filtering.py b/main_taxonomy_filtering.py
index bea65e6..e0d28a3 100644
--- a/main_taxonomy_filtering.py
+++ b/main_taxonomy_filtering.py
@@ -43,11 +43,11 @@ def main():
     # parsing models for peptide tables, sequences or limits
     set_of_taxid=set()
     if args.peptide_table :
-        set_of_markers = pt.parse_peptide_tables(args.peptide_table, args.limit, primary_taxonomy)
-        set_of_taxid.update({m.taxid for m in set_of_markers})
+        set_of_markers, _ = pt.parse_peptide_tables(args.peptide_table, args.limit, primary_taxonomy)
+        set_of_taxid.update({m.taxid() for m in set_of_markers})
     if args.fasta or args.directory:
         set_of_sequences = fa.build_set_of_sequences(args.fasta, args.directory, None, primary_taxonomy)
-        set_of_taxid.update({s.taxid for s in set_of_sequences})
+        set_of_taxid.update({s.taxid() for s in set_of_sequences})
     if args.limit:
         list_of_constraints=limit.parse_limits(args.limit)
         set_of_taxid.update({t for dict in list_of_constraints for t in dict["OX"]}) 
-- 
GitLab


From 464c855bced7800347478fff2ff6b9e57e920c89 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 19 Mar 2025 16:25:55 +0100
Subject: [PATCH 17/59] Allow .csv and .tsv as peptides table

---
 www/cgi-bin/pampa/pampa-craft.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/www/cgi-bin/pampa/pampa-craft.py b/www/cgi-bin/pampa/pampa-craft.py
index d1ba88e..66a5358 100644
--- a/www/cgi-bin/pampa/pampa-craft.py
+++ b/www/cgi-bin/pampa/pampa-craft.py
@@ -62,7 +62,7 @@ def extract_request(form):
     elif 'peptides_file' in form and form['peptides_file'].filename != "":
         f = form['peptides_file'].filename
         fn = os.path.basename(f)
-        uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['peptides_file']], resdir, extensions_list=[".tsv"],
+        uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['peptides_file']], resdir, extensions_list=[".tsv", ".csv"],
                                                               unzip=False)
         req["peptides_path"] = resdir + '/' + fn
         req['files_uploaded'].extend(uploaded)
-- 
GitLab


From 7307ea913ac810ce61d190d18293c03f7f649bf1 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 19 Mar 2025 16:31:17 +0100
Subject: [PATCH 18/59] better methods descriptions in tools.py

---
 www/cgi-bin/pampa/tools.py | 50 ++++++++++++++++++++++++++++----------
 1 file changed, 37 insertions(+), 13 deletions(-)

diff --git a/www/cgi-bin/pampa/tools.py b/www/cgi-bin/pampa/tools.py
index fdc0f3b..6d76f83 100644
--- a/www/cgi-bin/pampa/tools.py
+++ b/www/cgi-bin/pampa/tools.py
@@ -8,8 +8,15 @@ class FormTools:
 
     @classmethod
     def verif_mail(cls, mail):
-        """ check the validity of email address """
+        """
+        Vérification d'une adresse email.
+
+        Args:
+            mail (str): Email à valider
 
+        Returns:
+            bool: True/False
+        """
         res = mail.strip()
         if res.find(' ') > 0:
             return False
@@ -23,10 +30,16 @@ class FormTools:
     @classmethod
     def extract_files_from_fileitems(cls, fileitems, final_dir, extensions_list=None, unzip=False):
         """
-        Cette fonction permet l'extraction de fichiers et leur copie vers le répertoire de résultats de l'analyse (dossier 'result/{run_id}').
-        Ces fichiers ont été fournie par l'utilisateur, le paramètre 'fileitems' dont correspondre à un objet fileitems (type spécifique associé au téléchargement de fichiers). Ils serviront dans l'analyse de PAMPA.
-        Cette fonction permet de faire un filtre entre les fichiers dont l'extension est valide (extensions passés en arguments).
-        Les fichiers peuvent être contenus dans un fichier ZIP.
+        Gère les fichiers uploadés (dont ZIP) et les copie dans le dossier cible.
+
+        Args:
+            fileitems (list): Liste de fichiers type Werkzeug FileStorage
+            final_dir (str): Dossier de destination
+            extensions_list (list): Extensions autorisées (ex: ['.txt', '.csv'])
+            unzip (bool): Auto-extraction des ZIP si True
+
+        Returns:
+            tuple: (fichiers transférés, fichiers rejetés)
         """
         files_moved = []
         files_not_moved = []
@@ -66,8 +79,11 @@ class FormTools:
     @classmethod
     def extract_files_from_list(cls, filename_list, final_dir):
         """
-        Déplacement de fichiers internes.
-        l'unzip est systématique
+        Copie des fichiers locaux vers un dossier cible avec extraction automatique des ZIP.
+
+        Args:
+            filename_list (list): Liste de chemins de fichiers locaux
+            final_dir (str): Dossier de destination
         """
         final_dir = final_dir.rstrip('/')
         if not os.path.exists(final_dir):
@@ -92,7 +108,14 @@ class ResultTools:
     @classmethod
     def href_dl(cls, link, name):
         """
-        Construit une balise <a> ouvrant la page sur un nouvel onglet.
+        Génère un lien HTML de téléchargement.
+
+        Args:
+            link (str): URL du fichier
+            name (str): Nom affiché du fichier
+
+        Returns:
+            str: Balise HTML <a> formatée
         """
         result = f'<a href="{link}" download="{name}" target="_blank">{name}</a>'
         return result
@@ -100,11 +123,12 @@ class ResultTools:
     @classmethod
     def zip_results(cls, file_names, run_id, job_name=None):
         """
-        Produit un fichier ZIP contenant tous les fichiers de résultat :
-        - out.tsv
-        - detail_out.tsv
-        - report_out.txt
-        Ce fichier ZIP pourra être téléchargé par l'utilisateur.
+        Crée une archive ZIP des résultats d'analyse.
+
+        Args:
+            file_names (list): Noms des fichiers à archiver
+            run_id (str): Identifiant unique de l'analyse
+            job_name (str): Optionnel - suffixe pour les noms de fichiers
         """
         with zipfile.ZipFile(f"{common.RESULT_DIR}{run_id}/data_result_{run_id}.zip", "a",
                              compression=zipfile.ZIP_DEFLATED) as dwl_zip:
-- 
GitLab


From f473e4ff73d2e8f5c93fdcff1c5f4976a7613717 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 20 Mar 2025 11:25:05 +0100
Subject: [PATCH 19/59] Change cell colors based on index comparison between
 original and result tabs (filling method only)

---
 www/cgi-bin/pampa/table_maker.py | 121 ++++++++++++++++++++++++-------
 1 file changed, 94 insertions(+), 27 deletions(-)

diff --git a/www/cgi-bin/pampa/table_maker.py b/www/cgi-bin/pampa/table_maker.py
index 1a2273a..5135ff8 100644
--- a/www/cgi-bin/pampa/table_maker.py
+++ b/www/cgi-bin/pampa/table_maker.py
@@ -1,27 +1,78 @@
 import json
+import csv
 
-def table_maker(taxo_used, output_file):
-    taxo_data = []
-    with open(taxo_used, "r") as file:
-        headers = file.readline().strip().split("\t")
-        for line in file:
-            values = line.strip().split("\t")
-            taxo_data.append(dict(zip(headers, values)))
+
+def read_tsv(file_path):
+    data = []
+    with open(file_path, "r", newline="") as file:
+        reader = csv.DictReader(file, delimiter="\t")
+        for row in reader:
+            data.append(row)
+    return data
+
+
+def table_maker(peptide_out, peptide_or, output_file, method):
+    out_data = read_tsv(peptide_out)
+    or_data = read_tsv(peptide_or)
+
+    if out_data:
+        header = list(out_data[0].keys())
+    else:
+        header = []
     html = '''
         <div id="myGrid" style="height: 500px; margin-bottom: 20px;" class="ag-theme-alpine"></div>
         <div style="margin-top: 10px;">
-            <button onclick="gridApi.exportData()">Download Results (.TSV)</button>
+            <button onclick="exportData()">Download Results (.TSV)</button>
         </div>
-    
+
         <script src="https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js"></script>
         <script type="text/javascript">
-        function initGrid() {
+function initGrid() {
                 let gridApi;
-                const originalData = ''' + json.dumps(taxo_data) + ''';
-    
+                const outData = ''' + json.dumps(out_data) + ''';
+                const orData = ''' + json.dumps(or_data) + ''';
+                const method = "''' + method + '''";
+
+                if (method == "fillin") {
+                    outData.forEach((procRow, index) => {
+                        const origRow = orData[index];
+                        procRow.__modifiedCells = {};
+
+                        Object.keys(procRow).forEach(key => {
+                            if (!origRow) {
+                                procRow.__modifiedCells[key] = 'new';
+                            } else {
+                                const origValue = origRow[key];
+                                const newValue = procRow[key];
+
+                                if (origValue == null || origValue === "" || origValue === undefined) {
+                                    procRow.__modifiedCells[key] = 'added';
+                                } else if (origValue !== newValue) {
+                                    procRow.__modifiedCells[key] = 'modified';
+                                } else {
+                                    procRow.__modifiedCells[key] = 'original';
+                                }
+                            }
+                        });
+                    })
+                }    
+
+                const columnDefs = ''' + json.dumps([{"field": h, "editable": True} for h in header]) + ''';
+
                 const gridOptions = {
-                    rowData: originalData,
-                    columnDefs: ''' + json.dumps([{"field": h, "editable": True} for h in headers]) + ''',
+                    theme: agGrid.themeBalham,
+                    rowData: outData,
+                    columnDefs: columnDefs.map(col => ({
+                        ...col,
+                        cellStyle: params => {
+                            const status = params.data.__modifiedCells?.[col.field];
+                            if (status === 'modified') return { backgroundColor: '#ffff00'};
+                            if (status === 'added') return { backgroundColor: '#00ff00'};
+                            if (status === 'new') return { backgroundColor: '#0000ff'};
+
+                            return null;
+                        }
+                    })),
                     pagination: true,
                     paginationPageSize: 20,
                     defaultColDef: {
@@ -30,29 +81,45 @@ def table_maker(taxo_used, output_file):
                         resizable: true,
                         editable: true
                     },
+                    autoSizeStrategy: {
+                        type: "fitCellContents",
+                    },
                     onCellValueChanged: function(event) {
                         console.log('Cell modified:', event);
                     },
-                    enableCellChangeFlash: true
                 };
-    
-                function exportData() {
-                    gridApi.exportDataAsCsv({
-                        fileName: 'taxonomy_export.tsv'
+
+                function autoSizeAll(skipHeader) {
+                    const allColumnIds = [];
+                    gridApi.getColumns().forEach((column) => {
+                    allColumnIds.push(column.getId());
                     });
+
+                    gridApi.autoSizeColumns(allColumnIds, skipHeader);
+
+                    gridApi.sizeColumnsToFit();
                 }
-    
-                gridApi = agGrid.createGrid(document.querySelector("#myGrid"), gridOptions);}
+
+                gridApi = agGrid.createGrid(document.querySelector("#myGrid"), gridOptions);
+                window.gridApi = gridApi;
+
+                autoSizeAll(true);
+
+                window.exportData = function() {
+                    gridApi.exportDataAsCsv({
+                        fileName: "result.tsv",
+                        columnSeparator: '\\t'
+                    });
+                };
+            }
             if(document.readyState === 'loading') {
-        document.addEventListener('DOMContentLoaded', initGrid);
+                document.addEventListener('DOMContentLoaded', initGrid);
             } else {
-        initGrid();
-        }
-    
+                initGrid();
+            }
         </script>
         <div id="status" style="margin-top: 10px; color: #666;"></div>
         '''
 
     with open(output_file, 'w') as f:
-        f.write(html)
-
+        f.write(html)
\ No newline at end of file
-- 
GitLab


From b5cc7a278bc07fa85016e373054f189f9df16575 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 20 Mar 2025 11:26:32 +0100
Subject: [PATCH 20/59] Adapt craft_render_result.py to the new version of
 table_maker

---
 www/cgi-bin/pampa/craft_render_result.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/www/cgi-bin/pampa/craft_render_result.py b/www/cgi-bin/pampa/craft_render_result.py
index 4c054a3..013aaed 100644
--- a/www/cgi-bin/pampa/craft_render_result.py
+++ b/www/cgi-bin/pampa/craft_render_result.py
@@ -73,7 +73,7 @@ def results_output(run_id, job_name=None):
 
     return  html
 
-def write_main_page(run_id, taxo_used, job_name=None):
+def write_main_page(run_id, taxo_used, or_data, method, job_name=None):
     """Write a HTML page. The result page shown first when the PAMPA analysis is done."""
     html = ""
 
@@ -114,7 +114,7 @@ def write_main_page(run_id, taxo_used, job_name=None):
 
     table_dir = common.RESULT_DIR + run_id + "/table.php"
 
-    table_maker.table_maker(taxo_used, table_dir)
+    table_maker.table_maker(taxo_used, or_data, table_dir, method)
 
     html+= '''        
     <h3>Peptide Table</h3>
@@ -172,10 +172,11 @@ def main():
         job_name = None
     table_files = params["peptides_path"]
     fasta_file = params["sequences_files"]
+    method = params["method"]
 
     zip_results(["out_"+run_id+".tsv", "report_out_"+run_id+".txt"], run_id, job_name=job_name)
 
-    write_main_page(run_id, common.RESULT_DIR+ run_id + "/out_"+run_id+".tsv", job_name=job_name)
+    write_main_page(run_id, common.RESULT_DIR+ run_id + "/out_"+run_id+".tsv", table_files, method, job_name=job_name)
 
 
 main()
-- 
GitLab


From c07cffdade7823a8a9a605e0f88fe2538b07536a Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 20 Mar 2025 11:27:09 +0100
Subject: [PATCH 21/59] Adapt pampa-craft.py to the new version of table_maker

---
 www/cgi-bin/pampa/pampa-craft.py | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/www/cgi-bin/pampa/pampa-craft.py b/www/cgi-bin/pampa/pampa-craft.py
index 66a5358..9a79a06 100644
--- a/www/cgi-bin/pampa/pampa-craft.py
+++ b/www/cgi-bin/pampa/pampa-craft.py
@@ -112,6 +112,9 @@ def extract_request(form):
         for fn in not_uploaded:
             error_messages.append(f"{fn} : The taxonomy file must be in TSV format.")
 
+    if 'error' in form and form['error'] != "":
+        req['error'] = form['error'].value
+
 
     log.write("Recap downloads :\n")
     for fn in req['files_uploaded']:
@@ -141,6 +144,9 @@ def launch_software(run_id, req):
     if 'limit_path' in req:
         limit_arg = req["limit_path"]
         command += " -l " + limit_arg
+    if 'error' in req:
+        error_arg = req["error"]
+        command += " -e " + error_arg
     command += " --web"
     open("acommand.txt", "w").write(f"{command}")  # log
 
@@ -166,6 +172,8 @@ def launch_software(run_id, req):
             render_result_dict["peptides_path"] = req['peptides_path']
         if "sequences_dir" in req:
             render_result_dict["sequences_files"] = req['sequences_dir']  # tables de peptides utilisées
+        if "method" in req:
+            render_result_dict["method"] = req['method']
 
         json.dump(render_result_dict, open(json_file, "w"))
         os.system(f"python craft_render_result.py {json_file}")
-- 
GitLab


From e46dd629bcb058744eecff4abc8d2761ec624f00 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 24 Mar 2025 16:06:35 +0100
Subject: [PATCH 22/59] Split form by method

---
 .../{craft-form.php => craft-fillin.php}      |  49 ++-----
 www/html/pampa/craft-homology.php             | 122 ++++++++++++++++++
 2 files changed, 131 insertions(+), 40 deletions(-)
 rename www/html/pampa/{craft-form.php => craft-fillin.php} (73%)
 create mode 100644 www/html/pampa/craft-homology.php

diff --git a/www/html/pampa/craft-form.php b/www/html/pampa/craft-fillin.php
similarity index 73%
rename from www/html/pampa/craft-form.php
rename to www/html/pampa/craft-fillin.php
index be3d75c..662d221 100644
--- a/www/html/pampa/craft-form.php
+++ b/www/html/pampa/craft-fillin.php
@@ -14,9 +14,10 @@
 <div id="main">
   <div id="center">
 
-    <form id="formulaire-craft" method="post" enctype="multipart/form-data">
+    <form id="formulaire-craft-f" method="post" enctype="multipart/form-data">
 
     <input type="hidden" name="pampa_version" value="assign"/>
+    <input type="hidden" name="method" value="fillin"/>
 
     <div class="formulaire">
       <table class="vide pampa_choice">
@@ -31,43 +32,6 @@
       </table>
     </div>
 
-    <h2>Method</h2>
-    <table class="vide table-center pampa-choice">
-
-    <tr>
-      <td>
-        <div>
-          <input type="radio" id="allpeptides" name="method" value="allpeptides"/>
-          <label for="allpeptides">Allpeptides</label>
-        </div>
-      </td>
-      <td>
-        <div>
-          <input type="radio" id="deamidation" name="method" value="deamidation"/>
-          <label for="deamidation">Deamidation</label>
-        </div>
-      </td>
-      <td>
-        <div>
-          <input type="radio" id="fillin" name="method" value="fillin"/>
-          <label for="fillin">Fillin</label>
-        </div>
-      </td>
-      <td>
-        <div>
-          <input type="radio" id="homology" name="method" value="homology" checked/>
-          <label for="homology">Homology</label>
-        </div>
-      </td>
-      <td>
-        <div>
-          <input type="radio" id="selection" name="method" value="selection"/>
-          <label for="selection">Selection</label>
-        </div>
-      </td>
-    </table>
-    <br>
-
     <div class="formulaire">
       <h2>Peptides Table</h2>
       <table class="vide">
@@ -127,15 +91,20 @@
               <input type="file" name="limit_file" value="file" accept=".txt"/>
             </td>
           </tr>
+          <tr id="error_input">
+            <td>
+              <input type="number" name="error" step="0.0001" min="0.0001" max="1" placeholder="between 0.0001 and 1" value="0.1"/>
+            </td>
+          </tr>
         </table>
     </div>
 
     <div class="center">
-      <input type="button" id="example-craft" name="example-craft" value="Example">
+      <input type="button" id="example-fillin" name="example-fillin" value="Example">
     </div>
 
     <div class="center">
-      <input type="submit" id="reset-craft" name="reset" value="Reset" />
+      <input type="submit" id="reset-craft-f" name="reset" value="Reset" />
       <input type="submit" id="run" name="button" value="Run" />
       <input type="hidden" name="command" value="request" />
     </div>
diff --git a/www/html/pampa/craft-homology.php b/www/html/pampa/craft-homology.php
new file mode 100644
index 0000000..639ce64
--- /dev/null
+++ b/www/html/pampa/craft-homology.php
@@ -0,0 +1,122 @@
+<?php include('header.php') ?>
+
+
+<body>
+  <div class="frametitle">
+   <h1 id="title">Pampa-Craft</h1>
+  </div>
+
+  <div id="center_sup">
+     <div class="theme-border" style="display:none"></div>
+     <div id="link_home" style="display:inline-block"><a href="/" class="text_onglet"><img src="/Style/icon/home_w.png" alt="home_general"/></a></div>
+   <div class="tabs" id="menu_central" style="display:inline-block"><?php include("menu_central.txt")?></div>
+  </div>
+<div id="main">
+  <div id="center">
+
+    <form id="formulaire-craft-h" method="post" enctype="multipart/form-data">
+
+    <input type="hidden" name="pampa_version" value="assign"/>
+    <input type="hidden" name="method" value="homology"/>
+
+    <div class="formulaire">
+      <table class="vide pampa_choice">
+        <tr>
+          <td class="label">
+            <h2>Name of the job <span style="font-weight: normal">(optional) :</span></h2>
+          </td>
+          <td>
+            <input type="text" name="job_name" maxlength="30"/>
+          </td>
+        </tr>
+      </table>
+    </div>
+
+    <div class="formulaire">
+      <h2>Peptides Table</h2>
+      <table class="vide">
+        <tr>
+          <td class="label">
+            <B>Upload</B> the peptides table that you want to complete in (.csv or .tsv)
+          </td>
+        </tr>
+        <tr id="peptide_input">
+          <td>
+            <input type="file" name="peptides_file" value="file" accept=".tsv" multiple required/>
+          </td>
+        </tr>
+        <tr id="peptide_example" style="display: none;">
+          <td>
+            <input class="file_input" id="back_to_normal" type="button" value="Parcourir..."/>
+            craft-example.tsv
+            <input type="checkbox" style="display: none;" name="craft-example"/>
+          </td>
+        </tr>
+      </table>
+      <br>
+    </div>
+
+    <div class="formulaire">
+      <h2>Fasta File</h2>
+        <table class="vide">
+          <tr>
+            <td class="label">
+              <B>Upload</B> The fasta file that you will use to complete your table.
+            </td>
+          </tr>
+          <tr id="sequences_input">
+            <td>
+              <input type="file" name="sequences_files" value="file" accept=".fasta,.zip" multiple required/>
+            </td>
+          </tr>
+          <tr id="sequences_example" style="display: none;">
+            <td>
+              <input class="file_input" id="back_to_normal" type="button" value="Parcourir..."/>
+              craft-example.zip
+              <input type="checkbox" style="display: none;" name="craft-example"/>
+            </td>
+          </tr>
+        </table>
+        <br>
+
+      <b>Limit File</b>
+        <table class="vide">
+          <tr>
+            <td class="label">
+              <B>Upload</B> The limit file (optional, doc : https://github.com/touzet/pampa/wiki/Limiting-searches).
+            </td>
+          </tr>
+          <tr id="limit_input">
+            <td>
+              <input type="file" name="limit_file" value="file" accept=".txt"/>
+            </td>
+          </tr>
+        </table>
+    </div>
+
+    <div class="center">
+      <input type="button" id="example-homology" name="example-homology" value="Example">
+    </div>
+
+    <div class="center">
+      <input type="submit" id="reset-craft-h" name="reset" value="Reset" />
+      <input type="submit" id="run" name="button" value="Run" />
+      <input type="hidden" name="command" value="request" />
+    </div>
+
+  </form>
+
+</div><!--bloc -->
+</div><!-- main-->
+
+<!-- chargement de la librairie php lib.inc -->
+   <?php require("../lib.inc")?>
+<!-- appel de la fonction footer qui permet d'afficher au bas de la page (nom du logiciel, un lien vers le mail, la date de modif -->
+<!-- A modifier en fonction de votre logiciel -->
+   <?php footer("Pampa","Pampa", "areski.flissi@univ-lille.fr","2025"); ?>
+
+
+
+</body>
+</html>
+
-- 
GitLab


From d12b5b0707a926f12bab0e7b877b0bb884fcfbfa Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 24 Mar 2025 16:07:32 +0100
Subject: [PATCH 23/59] Add fillin example data

---
 www/html/pampa/data_pampa/fillin_peptides.tsv      |  5 +++++
 www/html/pampa/data_pampa/fillin_sequences.fasta   | 14 ++++++++++++++
 .../{craft_peptides.tsv => homology_peptides.tsv}  |  0
 ...ft_sequences.fasta => homology_sequences.fasta} |  0
 4 files changed, 19 insertions(+)
 create mode 100644 www/html/pampa/data_pampa/fillin_peptides.tsv
 create mode 100644 www/html/pampa/data_pampa/fillin_sequences.fasta
 rename www/html/pampa/data_pampa/{craft_peptides.tsv => homology_peptides.tsv} (100%)
 rename www/html/pampa/data_pampa/{craft_sequences.fasta => homology_sequences.fasta} (100%)

diff --git a/www/html/pampa/data_pampa/fillin_peptides.tsv b/www/html/pampa/data_pampa/fillin_peptides.tsv
new file mode 100644
index 0000000..90903db
--- /dev/null
+++ b/www/html/pampa/data_pampa/fillin_peptides.tsv
@@ -0,0 +1,5 @@
+Taxon	Marker	Sequence	Mass	PTM	Hel	Gene	SeqID	Begin	End
+Mus musculus	A		1178.6			COL1A2
+Mus musculus	A		1194.6			COL1A2
+Mus musculus	B		1453.7			COL1A2
+Mus musculus	C		1592.8			COL1A2
diff --git a/www/html/pampa/data_pampa/fillin_sequences.fasta b/www/html/pampa/data_pampa/fillin_sequences.fasta
new file mode 100644
index 0000000..776be52
--- /dev/null
+++ b/www/html/pampa/data_pampa/fillin_sequences.fasta
@@ -0,0 +1,14 @@
+>NP_031768.2 OS=Mus musculus OX=10090 GN=COL1A1 
+MFSFVDLRLLLLLGATALLTHGQEDIPEVSCIHNGLRVPNGETWKPEVCLICICHNGTAVCDDVQCNEELDCPNPQRREGECCAFCPEEYVSPNSEDVGVEGPKGDPGPQGPRGPVGPPGRDGIPGQPGLPGPPGPPGPPGPPGLGGNFASQMSYGYDEKSAGVSVPGPMGPSGPRGLPGPPGAPGPQGFQGPPGEPGEPGGSGPMGPRGPPGPPGKNGDDGEAGKPGRPGERGPPGPQGARGLPGTAGLPGMKGHRGFSGLDGAKGDAGPAGPKGEPGSPGENGAPGQMGPRGLPGERGRPGPPGTAGARGNDGAVGAAGPPGPTGPTGPPGFPGAVGAKGEAGPQGARGSEGPQGVRGEPGPPGPAGAAGPAGNPGADGQPGAKGANGAPGIAGAPGFPGARGPSGPQGPSGPPGPKGNSGEPGAPGNKGDTGAKGEPGATGVQGPPGPAGEEGKRGARGEPGPSGLPGPPGERGGPGSRGFPGADGVAGPKGPSGERGAPGPAGPKGSPGEAGRPGEAGLPGAKGLTGSPGSPGPDGKTGPPGPAGQDGRPGPAGPPGARGQAGVMGFPGPKGTAGEPGKAGERGLPGPPGAVGPAGKDGEAGAQGAPGPAGPAGERGEQGPAGSPGFQGLPGPAGPPGEAGKPGEQGVPGDLGAPGPSGARGERGFPGERGVQGPPGPAGPRGNNGAPGNDGAKGDTGAPGAPGSQGAPGLQGMPGERGAAGLPGPKGDRGDAGPKGADGSPGKDGARGLTGPIGPPGPAGAPGDKGEAGPSGPPGPTGARGAPGDRGEAGPPGPAGFAGPPGADGQPGAKGEPGDTGVKGDAGPPGPAGPAGPPGPIGNVGAPGPKGPRGAAGPPGATGFPGAAGRVGPPGPSGNAGPPGPPGPVGKEGGKGPRGETGPAGRPGEVGPPGPPGPAGEKGSPGADGPAGSPGTPGPQGIAGQRGVVGLPGQRGERGFPGLPGPSGEPGKQGPSGSSGERGPPGPMGPPGLAGPPGESGREGSPGAEGSPGRDGAPGAKGDRGETGPAGPPGAPGAPGAPGPVGPAGKNGDRGETGPAGPAGPIGPAGARGPAGPQGPRGDKGETGEQGDRGIKGHRGFSGLQGPPGSPGSPGEQGPSGASGPAGPRGPPGSAGSPGKDGLNGLPGPIGPPGPRGRTGDSGPAGPPGPPGPPGPPGPPSGGYDFSFLPQPPQEKSQDGGRYYRADDANVVRDRDLEVDTTLKSLSQQIENIRSPEGSRKNPARTCRDLKMCHSDWKSGEYWIDPNQGCNLDAIKVYCNMETGQTCVFPTQPSVPQKNWYISPNPKEKKHVWFGESMTDGFPFEYGSEGSDPADVAIQLTFLRLMSTEASQNITYHCKNSVAYMDQQTGNLKKALLLQGSNEIELRGEGNSRFTYSTLVDGCTSHTGTWGKTVIEYKTTKTSRLPIIDVAPLDIGAPDQEFGLDIGPACFV
+>NP_031769.2 OS=Mus musculus OX=10090 GN=COL1A2
+MLSFVDTRTLLLLAVTSCLATCQYLQSGSVRKGPTGDRGPRGQRGPAGPRGRDGVDGPMGPPGPPGSPGPPGSPAPPGLTGNFAAQYSDKGVSSGPGPMGLMGPRGPPGAVGAPGPQGFQGPAGEPGEPGQTGPAGPRGPAGSPGKAGEDGHPGKPGRPGERGVVGPQGARGFPGTPGLPGFKGVKGHSGMDGLKGQPGAQGVKGEPGAPGENGTPGQAGARGLPGERGRVGAPGPAGARGSDGSVGPVGPAGPIGSAGPPGFPGAPGPKGELGPVGNPGPAGPAGPRGEVGLPGLSGPVGPPGNPGTNGLTGAKGATGLPGVAGAPGLPGPRGIPGPAGAAGATGARGLVGEPGPAGSKGESGNKGEPGSVGAQGPPGPSGEEGKRGSPGEAGSAGPAGPPGLRGSPGSRGLPGADGRAGVMGPPGNRGSTGPAGIRGPNGDAGRPGEPGLMGPRGLPGSPGNVGPSGKEGPVGLPGIDGRPGPIGPAGPRGEAGNIGFPGPKGPSGDPGKPGERGHPGLAGARGAPGPDGNNGAQGPPGPQGVQGGKGEQGPAGPPGFQGLPGPSGTTGEVGKPGERGLPGEFGLPGPAGPRGERGTPGESGAAGPSGPIGSRGPSGAPGPDGNKGEAGAVGAPGSAGASGPGGLPGERGAAGIPGGKGEKGETGLRGDTGNTGRDGARGIPGAVGAPGPAGASGDRGEAGAAGPSGPAGPRGSPGERGEVGPAGPNGFAGPAGAAGQPGAKGEKGTKGPKGENGIVGPTGSVGAAGPSGPNGPPGPVGSRGDGGPPGMTGFPGAAGRTGPPGPSGIAGPPGPPGAAGKEGIRGPRGDQGPVGRTGETGASGPPGFVGEKGPSGEPGTAGAPGTAGPQGLLGAPGILGLPGSRGERGLPGIAGALGEPGPLGISGPPGARGPPGAVGSPGVNGAPGEAGRDGNPGSDGPPGRDGQPGHKGERGYPGSIGPTGAAGAPGPHGSVGPAGKHGNRGEPGPAGSVGPVGAVGPRGPSGPQGIRGDKGEPGDKGHRGLPGLKGYSGLQGLPGLAGLHGDQGAPGPVGPAGPRGPAGPSGPVGKDGRSGQPGPVGPAGVRGSQGSQGPAGPPGPPGPPGPPGVSGGGYDFGFEGDFYRADQPRSQPSLRPKDYEVDATLKSLNNQIETLLTPEGSRKNPARTCRDLRLSHPEWNSDYYWIDPNQGCTMDAIKVYCDFSTGETCIQAQPVNTPAKNSYSRAQANKHVWLGETINGGSQFEYNVEGVSSKEMATQLAFMRLLANRASQNITYHCKNSIAYLDEETGSLNKAVLLQGSNDVELVAEGNSRFTYSVLVDGCSKKTNEWGKTIIEYKTNKPSRLPFLDIAPLDIGGADQEFRVEVGPVCFK
+>XP_032768589.1 OS=Rattus rattus OX=10117 GN=COL1A1
+MFSFVDLRLLLLLGATALLTHGQEDIPEVSCIHNGLRVPNGETWKPDVCLICICHNGTAVCDGVLCKEDLDCPNPQRREGECCPFCPEEYVSPDAEVIGVEGPKGDPGPQGPRGPVGPPGQDGIPGQPGLPGPPGPPGPPGPPGLGGNFASQMSYGYDEKSAGVSVPGPMGPSGPRGLPGPPGAPGPQGFQGPPGEPGEPGASGPMGPRGPPGPPGKNGDDGEAGKPGRPGERGPPGPQGARGLPGTAGLPGMKGHRGFSGLDGAKGDTGPAGPKGEPGSPGENGAPGQMGPRGLPGERGRPGPPGSAGARGNDGAVGAAGPPGPTGPTGPPGFPGAAGAKGEAGPQGARGSEGPQGVRGEPGPPGPAGAAGPAGNPGADGQPGAKGANGAPGIAGAPGFPGARGPSGPQGPSGAPGPKGNSGEPGAPGNKGDTGAKGEPGPAGVQGPPGPAGEEGKRGARGEPGPSGLPGPPGERGGPGSRGFPGADGVAGPKGPAGERGSPGPAGPKGSPGEAGRPGEAGLPGAKGLTGSPGSPGPDGKTGPPGPAGQDGRPGPAGPPGARGQAGVMGFPGPKGTAGEPGKAGERGVPGPPGAVGPAGKDGEAGAQGAPGPAGPAGERGEQGPAGSPGFQGLPGPAGPPGEAGKPGEQGVPGDLGAPGPSGARGERGFPGERGVQGPPGPAGPRGNNGAPGNDGAKGDTGAPGAPGSQGAPGLQGMPGERGAAGLPGPKGDRGDAGPKGADGSPGKDGVRGLTGPIGPPGPAGAPGDKGETGPSGPAGPTGARGAPGDRGEPGPPGPAGFAGPPGADGQPGAKGEPGDTGVKGDAGPPGPAGPAGPPGPIGNVGAPGPKGTRGAAGPPGATGFPGAAGRVGPPGPSGNAGPPGPPGPVGKEGGKGPRGETGPAGRPGEVGPPGPPGPAGEKGSPGADGPAGSPGTPGPQGIAGQRGVVGLPGQRGERGFPGLPGPSGEPGKQGPSGASGERGPPGPMGPPGLAGPPGESGREGSPGAEGSPGRDGAPGAKGDRGETGPAGPPGAPGAPGAPGPVGPAGKNGDRGETGPAGPAGPIGPAGARGPAGPQGPRGDKGETGEQGDRGIKGHRGFSGLQGPPGSPGSPGEQGPSGASGPAGPRGPPGSAGSPGKDGLNGLPGPIGPPGPRGRTGDSGPAGPPGPPGPPGPPGPPSGGYDFSFLPQPPQEKSQDGGRYYRADDANVVRDRDLEVDTTLKSLSQQIENIRSPEGSRKNPARTCRDLKMCHSDWKSGEYWIDPNQGCNLDAIKVYCNMETGQTCVFPTQPSVPQKNWYISPNPKEKTHIWFGESMSGGFQFEYGSEGSDPADVAIQLTFLRLMSTEASQNITYHCKNSVAYMDQQTGNLKKSLLLQGSNEIELRGEGNSRFTYSTLVDGCTSHTGTWGKTVIEYKTTKTSRLPIIDVAPLDIGAPDQEFGMDVGPACFV
+>XP_032762867.1 OS=Rattus rattus OX=10117 GN=COL1A2
+MLSFVDTRTLLLLAVTSCLATCQSLQMGSVRKGPTGDRGPRGQRGPAGPRGRDGVDGPVGPPGPAGAPGPPGPPGPPGLTGNFAAQYSDKGVSAGPGPMGLMGPRGPPGAVGAPGPQGFQGPAGEPGEPGQTGPAGSRGPAGPPGKAGEDGHPGKPGRPGERGVVGPQGARGFPGTPGLPGFKGIRGHNGLDGLKGQPGAQGVKGEPGAPGENGTPGQAGARGLPGERGRVGAPGPAGARGSDGSVGPVGPAGPIGSAGPPGFPGAPGPKGELGPVGNPGPAGPAGPRGEAGLPGLSGPVGPPGNPGANGLTGAKGATGLPGVAGAPGLPGPRGIPGPVGAAGATGPRGLVGEPGPAGSKGETGNKGEPGSAGAQGPPGPSGEEGKRGSPGEPGSAGPAGPPGLRGSPGSRGLPGADGRAGVMGPPGNRGSTGPAGVRGPNGDAGRPGEPGLMGPRGLPGSPGNVGPAGKEGPVGLPGIDGRPGPIGPAGPRGEAGNIGFPGPKGPSGDPGKPGEKGHPGLAGARGAPGPDGNNGAQGPPGPQGVQGGKGEQGPAGPPGFQGLPGPSGTAGEVGKPGERGLPGEFGLPGPAGPRGERGPPGESGAAGPSGPIGSRGPSGAAGPDGNKGEAGAVGAPGSAGASGPGGLPGERGAAGIPGGKGEKGETGLRGEIGNPGRDGARGAPGAIGAPGPAGASGDRGEAGAAGPSGPAGPRGSPGERGEVGPAGPNGFAGPAGSAGQPGAKGEKGTKGPKGENGIVGPTGPVGAAGPSGPNGPPGPAGTRGDGGPPGMTGFPGAAGRTGPPGPSGITGPPGPPGAAGKEGIRGPRGDQGPVGRTGEIGASGPPGFAGEKGPSGEPGTAGPPGTAGPQGLLGAPGILGLPGSRGERGLPGIAGALGEPGPLGIAGPPGARGPPGAVGSPGVNGAPGEAGRDGNPGSDGPPGRDGQPGHKGERGYPGNIGPTGAAGAPGPHGSVGPAGKHGNRGEPGPAGSVGPVGAVGPRGPSGPQGIRGDKGEPGDKGARGLPGLKGHNGLQGLPGLAGLHGDQGAPGPVGPAGPRGPAGPSGPVGKDGRSGHPGPVGPAGVRGSQGSQGPAGPPGPPGPPGPPGVSGGGYDFGFEGDFYRADQPRSQPSLRPKDYEVDATLKSLNNQIETLLTPEGSKKNPARTCRDLRLSHPEWKSDYYWIDPNQGCTMDAIKVYCDFSTGETCIQAQPVNTPAKNAYSRAQANKHVWLGETINGGSQFEYNAEGVSSKEMATQLAFMRLLANRASQNITYHCKNSIAYLDEETGRLNKAVILQGSNDVELVAEGNSRFTYTVLVDGCSKKTNEWDKTIIEYKTNKPSRLPFLDIAPLDIGGANQEFRVEVGPVCFK
+>NP_445756.1 OS=Rattus norvegicus OX=10116 GN=COL1A1 
+MFSFVDLRLLLLLGATALLTHGQEDIPEVSCIHNGLRVPNGETWKPDVCLICICHNGTAVCDGVLCKEDLDCPNPQKREGECCPFCPEEYVSPDAEVIGVEGPKGDPGPQGPRGPVGPPGQDGIPGQPGLPGPPGPPGPPGPPGLGGNFASQMSYGYDEKSAGVSVPGPMGPSGPRGLPGPPGAPGPQGFQGPPGEPGEPGASGPMGPRGPPGPPGKNGDDGEAGKPGRPGERGPPGPQGARGLPGTAGLPGMKGHRGFSGLDGAKGDTGPAGPKGEPGSPGENGAPGQMGPRGLPGERGRPGPPGSAGARGNDGAVGAAGPPGPTGPTGPPGFPGAAGAKGEAGPQGARGSEGPQGVRGEPGPPGPAGAAGPAGNPGADGQPGAKGANGAPGIAGAPGFPGARGPSGPQGPSGAPGPKGNSGEPGAPGNKGDTGAKGEPGPAGVQGPPGPAGEEGKRGARGEPGPSGLPGPPGERGGPGSRGFPGADGVAGPKGPAGERGSPGPAGPKGSPGEAGRPGEAGLPGAKGLTGSPGSPGPDGKTGPPGPAGQDGRPGPAGPPGARGQAGVMGFPGPKGTAGEPGKAGERGVPGPPGAVGPAGKDGEAGAQGAPGPAGPAGERGEQGPAGSPGFQGLPGPAGPPGEAGKPGEQGVPGDLGAPGPSGARGERGFPGERGVQGPPGPAGPRGNNGAPGNDGAKGDTGAPGAPGSQGAPGLQGMPGERGAAGLPGPKGDRGDAGPKGADGSPGKDGVRGLTGPIGPPGPAGAPGDKGETGPSGPAGPTGARGAPGDRGEPGPPGPAGFAGPPGADGQPGAKGEPGDTGVKGDAGPPGPAGPAGPPGPIGNVGAPGPKGSRGAAGPPGATGFPGAAGRVGPPGPSGNAGPPGPPGPVGKEGGKGPRGETGPAGRPGEVGPPGPPGPAGEKGSPGADGPAGSPGTPGPQGIAGQRGVVGLPGQRGERGFPGLPGPSGEPGKQGPSGASGERGPPGPMGPPGLAGPPGESGREGSPGAEGSPGRDGAPGAKGDRGETGPAGPPGAPGAPGAPGPVGPAGKNGDRGETGPAGPAGPIGPAGARGPAGPQGPRGDKGETGEQGDRGIKGHRGFSGLQGPPGSPGSPGEQGPSGASGPAGPRGPPGSAGSPGKDGLNGLPGPIGPPGPRGRTGDSGPAGPPGPPGPPGPPGPPSGGYDFSFLPQPPQEKSQDGGRYYRADDANVVRDRDLEVDTTLKSLSQQIENIRSPEGSRKNPARTCRDLKMCHSDWKSGEYWIDPNQGCNLDAIKVYCNMETGQTCVFPTQPSVPQKNWYISPNPKEKKHVWFGESMTDGFQFEYGSEGSDPADVAIQLTFLRLMSTEASQNITYHCKNSVAYMDQQTGNLKKSLLLQGSNEIELRGEGNSRFTYSTLVDGCTSHTGTWGKTVIEYKTTKTSRLPIIDVAPLDIGAPDQEFGMDIGPACFV
+>NP_445808.2 OS=Rattus norvegicus OX=10116 GN=COL1A2  
+MLSFVDTRTLLLLAVTSCLATCQSLQMGSVRKGPTGDRGPRGQRGPAGPRGRDGVDGPVGPPGPPGAPGPPGPPGPPGLTGNFAAQYSDKGVSAGPGPMGLMGPRGPPGAVGAPGPQGFQGPAGEPGEPGQTGPAGSRGPAGPPGKAGEDGHPGKPGRPGERGVVGPQGARGFPGTPGLPGFKGIRGHNGLDGLKGQPGAQGVKGEPGAPGENGTPGQAGARGLPGERGRVGAPGPAGARGSDGSVGPVGPAGPIGSAGPPGFPGAPGPKGELGPVGNPGPAGPAGPRGEAGLPGLSGPVGPPGNPGANGLTGAKGATGLPGVAGAPGLPGPRGIPGPVGAAGATGPRGLVGEPGPAGSKGETGNKGEPGSAGAQGPPGPSGEEGKRGSPGEPGSAGPAGPPGLRGSPGSRGLPGADGRAGVMGPPGNRGSTGPAGVRGPNGDAGRPGEPGLMGPRGLPGSPGNVGPAGKEGPVGLPGIDGRPGPIGPAGPRGEAGNIGFPGPKGPSGDPGKPGEKGHPGLAGARGAPGPDGNNGAQGPPGPQGVQGGKGEQGPAGPPGFQGLPGPSGTAGEVGKPGERGLPGEFGLPGPAGPRGERGPPGESGAAGPSGPIGSRGPSGAPGPDGNKGEAGAVGAPGSAGASGPGGLPGERGAAGIPGGKGEKGETGLRGEIGNPGRDGARGAPGAIGAPGPAGASGDRGEAGAAGPSGPAGPRGSPGERGEVGPAGPNGFAGPAGSAGQPGAKGEKGTKGPKGENGIVGPTGPVGAAGPSGPNGPPGPAGSRGDGGPPGMTGFPGAAGRTGPPGPSGITGPPGPPGAAGKEGIRGPRGDQGPVGRTGEIGASGPPGFAGEKGPSGEPGTTGPPGTAGPQGLLGAPGILGLPGSRGERGLPGIAGALGEPGPLGIAGPPGARGPPGAVGSPGVNGAPGEAGRDGNPGSDGPPGRDGQPGHKGERGYPGNIGPTGAAGAPGPHGSVGPAGKHGNRGEPGPAGSVGPVGAVGPRGPSGPQGIRGDKGEPGDKGARGLPGLKGHNGLQGLPGLAGLHGDQGAPGPVGPAGPRGPAGPSGPIGKDGRSGHPGPVGPAGVRGSQGSQGPAGPPGPPGPPGPPGVSGGGYDFGFEGDFYRADQPRSQPSLRPKDYEVDATLKSLNNQIETLLTPEGSRKNPARTCRDLRLSHPEWKSDYYWIDPNQGCTMDAIKVYCDFSTGETCIQAQPVNTPAKNAYSRAQANKHVWLGETINGGSQFEYNAEGVSSKEMATQLAFMRLLANRASQNITYHCKNSIAYLDEETGRLNKAVILQGSNDVELVAEGNSRFTYTVLVDGCSKKTNEWDKTIIEYKTNKPSRLPFLDIAPLDIGGTNQEFRVEVGPVCFK
+
+
diff --git a/www/html/pampa/data_pampa/craft_peptides.tsv b/www/html/pampa/data_pampa/homology_peptides.tsv
similarity index 100%
rename from www/html/pampa/data_pampa/craft_peptides.tsv
rename to www/html/pampa/data_pampa/homology_peptides.tsv
diff --git a/www/html/pampa/data_pampa/craft_sequences.fasta b/www/html/pampa/data_pampa/homology_sequences.fasta
similarity index 100%
rename from www/html/pampa/data_pampa/craft_sequences.fasta
rename to www/html/pampa/data_pampa/homology_sequences.fasta
-- 
GitLab


From d442f4dc41025145039220d3cace54d04e60d641 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 24 Mar 2025 16:08:37 +0100
Subject: [PATCH 24/59] Support fillin example

---
 www/cgi-bin/pampa/common.py      |  7 +++++--
 www/cgi-bin/pampa/pampa-craft.py | 24 ++++++++++++++++--------
 www/html/pampa/js/pampa.js       | 21 +++++++++++++++++----
 www/html/pampa/js/script.js      |  6 ++++--
 4 files changed, 42 insertions(+), 16 deletions(-)

diff --git a/www/cgi-bin/pampa/common.py b/www/cgi-bin/pampa/common.py
index 3ca18e7..f9f9c5a 100644
--- a/www/cgi-bin/pampa/common.py
+++ b/www/cgi-bin/pampa/common.py
@@ -25,8 +25,11 @@ TAXONOMY_REDUCED_FILE = DATA_PAMPA_DIR + "taxonomy_reduced.tsv"
 SPECTRA_EXAMPLE_1 = DATA_PAMPA_DIR + "example_1.zip"
 
 # ---- CRAFT ----
-CRAFT_PEPTIDE = DATA_PAMPA_DIR + "craft_peptides.tsv"
-CRAFT_SEQUENCES = DATA_PAMPA_DIR + "craft_sequences.fasta"
+HOMOLOGY_PEPTIDE = DATA_PAMPA_DIR + "homology_peptides.tsv"
+HOMOLOGY_SEQUENCES = DATA_PAMPA_DIR + "homology_sequences.fasta"
+
+FILLIN_PEPTIDE = DATA_PAMPA_DIR + "fillin_peptides.tsv"
+FILLIN_SEQUENCES = DATA_PAMPA_DIR + "fillin_sequences.fasta"
 
 
 
diff --git a/www/cgi-bin/pampa/pampa-craft.py b/www/cgi-bin/pampa/pampa-craft.py
index 9a79a06..3127bdc 100644
--- a/www/cgi-bin/pampa/pampa-craft.py
+++ b/www/cgi-bin/pampa/pampa-craft.py
@@ -1,8 +1,6 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
-import cgitb;
-
-cgitb.enable()
+import cgitb;cgitb.enable()
 import os
 import cgi
 import json
@@ -53,12 +51,17 @@ def extract_request(form):
 
     if 'method' in form:
         req["method"] = form['method'].value
+        method = form['method'].value
     else:
         error_messages.append(f"Method is required")
 
     if 'craft-example' in form:
-        FormTools.extract_files_from_list([common.CRAFT_PEPTIDE], resdir)
-        req["peptides_path"] = resdir + '/' + "craft_peptides.tsv"
+        if method == "homology":
+            FormTools.extract_files_from_list([common.HOMOLOGY_PEPTIDE], resdir)
+            req["peptides_path"] = resdir + '/' + "homology_peptides.tsv"
+        elif method == "fillin":
+            FormTools.extract_files_from_list([common.FILLIN_PEPTIDE], resdir)
+            req["peptides_path"] = resdir + '/' + "fillin_peptides.tsv"
     elif 'peptides_file' in form and form['peptides_file'].filename != "":
         f = form['peptides_file'].filename
         fn = os.path.basename(f)
@@ -77,7 +80,10 @@ def extract_request(form):
     log.write(f"sequences_dir: {req['sequences_dir']}\n")
 
     if 'craft-example' in form:
-        FormTools.extract_files_from_list([common.CRAFT_SEQUENCES], abs_sequences_dir)
+        if method == "homology":
+            FormTools.extract_files_from_list([common.HOMOLOGY_SEQUENCES], abs_sequences_dir)
+        elif method == "fillin":
+            FormTools.extract_files_from_list([common.FILLIN_SEQUENCES], abs_sequences_dir)
     elif 'sequences_files' in form:
         fileitems = form['sequences_files']
         if not isinstance(fileitems, list):
@@ -112,8 +118,10 @@ def extract_request(form):
         for fn in not_uploaded:
             error_messages.append(f"{fn} : The taxonomy file must be in TSV format.")
 
-    if 'error' in form and form['error'] != "":
-        req['error'] = form['error'].value
+    if 'error' in form:
+        error_value = form['error'].value.strip()
+        if error_value:
+            req['error'] = error_value
 
 
     log.write("Recap downloads :\n")
diff --git a/www/html/pampa/js/pampa.js b/www/html/pampa/js/pampa.js
index b73fe38..81f58fe 100644
--- a/www/html/pampa/js/pampa.js
+++ b/www/html/pampa/js/pampa.js
@@ -96,9 +96,23 @@ $(document).on("click", "#example_1", function() {
     });
 });
 
-$(document).on("click", "#example-craft", function() {
-    $('#main').load(soft+'craft-form.php #center', function(){
-        $("input[name='job_name']").val("example_craft");
+$(document).on("click", "#example-homology", function() {
+    $('#main').load(soft+'craft-homology.php #center', function(){
+        $("input[name='job_name']").val("example_homology");
+        $("#peptide_input").hide();
+        $("#peptide_input input").attr("required", false);
+        $("#peptide_example").show();
+        $("input[name='craft-example']").attr("checked", true);
+        $("#sequences_input").hide();
+        $("#sequences_input input").attr("required", false);
+        $("#sequences_example").show();
+        $("input[name='craft-example']").attr("checked", true);
+    });
+});
+
+$(document).on("click", "#example-fillin", function() {
+    $('#main').load(soft+'craft-fillin.php #center', function(){
+        $("input[name='job_name']").val("example_homology");
         $("#peptide_input").hide();
         $("#peptide_input input").attr("required", false);
         $("#peptide_example").show();
@@ -107,7 +121,6 @@ $(document).on("click", "#example-craft", function() {
         $("#sequences_input input").attr("required", false);
         $("#sequences_example").show();
         $("input[name='craft-example']").attr("checked", true);
-        $("#homology").prop("checked", true);
     });
 });
 
diff --git a/www/html/pampa/js/script.js b/www/html/pampa/js/script.js
index 1d3b16e..9647380 100644
--- a/www/html/pampa/js/script.js
+++ b/www/html/pampa/js/script.js
@@ -13,7 +13,8 @@ function resetForm(formSelector, baseForm) {
     });
 }
 resetForm("#reset", "form.php")
-resetForm("#reset-craft", "craft-form.php")
+resetForm("#reset-craft-h", "craft-homology.php")
+resetForm("reset-craft-f", "craft-fillin.php")
 
 function formSubmit(formSelector, paramIndex) {
   $(document).on("submit", formSelector, function() {
@@ -24,7 +25,8 @@ function formSubmit(formSelector, paramIndex) {
   });
 }
 formSubmit("#formulaire", 3);
-formSubmit("#formulaire-craft", 5);
+formSubmit("#formulaire-craft-h", 5);
+formSubmit("#formulaire-craft-f", 5)
 
 $(document).on("submit", "#form_id", function(){
     var id = document.getElementById("run_id").value;
-- 
GitLab


From 4a101009f1033781ae908a07b1166bedaf8fb536 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 24 Mar 2025 16:10:30 +0100
Subject: [PATCH 25/59] "home" page for pampa craft (TODO)

---
 www/html/pampa/menu_central.txt |  2 +-
 www/html/pampa/pampa-craft.php  | 34 +++++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)
 create mode 100644 www/html/pampa/pampa-craft.php

diff --git a/www/html/pampa/menu_central.txt b/www/html/pampa/menu_central.txt
index de19f0b..c960a2c 100644
--- a/www/html/pampa/menu_central.txt
+++ b/www/html/pampa/menu_central.txt
@@ -6,7 +6,7 @@
     <a href="/pampa/form.php" class="mc" >Pampa web server</a>
   </li>
     <li class="onglet">
-    <a href="/pampa/craft-form.php" class="mc" >Pampa-Craft web server</a>
+    <a href="/pampa/pampa-craft.php" class="mc" >Pampa-Craft web server</a>
   </li>
   <li class="onglet">
     <a href="/pampa/help.php" class="mc" >help</a>
diff --git a/www/html/pampa/pampa-craft.php b/www/html/pampa/pampa-craft.php
new file mode 100644
index 0000000..ead8f7a
--- /dev/null
+++ b/www/html/pampa/pampa-craft.php
@@ -0,0 +1,34 @@
+<?php include('header.php') ?>
+
+
+<body>
+  <div class="frametitle">
+   <h1 id="title">Pampa-Craft</h1>
+  </div>
+
+  <div id="center_sup">
+     <div class="theme-border" style="display:none"></div>
+     <div id="link_home" style="display:inline-block"><a href="/" class="text_onglet"><img src="/Style/icon/home_w.png" alt="home_general"/></a></div>
+   <div class="tabs" id="menu_central" style="display:inline-block"><?php include("menu_central.txt")?></div>
+  </div>
+<div id="main">
+  <div id="center">
+
+    <a href="/pampa/craft-homology.php" class="mc" >Pampa-Craft homology</a>
+
+    <a href="/pampa/craft-fillin.php" class="mc" >Pampa-Craft fillin</a>
+
+</div><!--bloc -->
+</div><!-- main-->
+
+<!-- chargement de la librairie php lib.inc -->
+   <?php require("../lib.inc")?>
+<!-- appel de la fonction footer qui permet d'afficher au bas de la page (nom du logiciel, un lien vers le mail, la date de modif -->
+<!-- A modifier en fonction de votre logiciel -->
+   <?php footer("Pampa","Pampa", "areski.flissi@univ-lille.fr","2025"); ?>
+
+
+
+</body>
+</html>
+
-- 
GitLab


From f78465673156dcb3aa8305504f2026e84e7f9344 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 24 Mar 2025 16:11:09 +0100
Subject: [PATCH 26/59] Handle fillin form in css

---
 www/html/pampa/css/pampa.css | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/www/html/pampa/css/pampa.css b/www/html/pampa/css/pampa.css
index a7352f3..76a4432 100644
--- a/www/html/pampa/css/pampa.css
+++ b/www/html/pampa/css/pampa.css
@@ -11,7 +11,7 @@ blockquote {
   padding-left: 8px;
 }
 
-.formulaire, .formulaire-craft {
+.formulaire, .formulaire-craft-h, .formulaire-craft-f {
     border: 1px solid #CCCCCC;
     color: #000000;
     margin: auto auto 10pt;
-- 
GitLab


From 29a3d2b099d7da584874ab352fc7169b68ea6647 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 24 Mar 2025 16:12:42 +0100
Subject: [PATCH 27/59] No more code duplication in pampa.py (Tools.py)

---
 www/cgi-bin/pampa/pampa.py | 98 ++++----------------------------------
 1 file changed, 9 insertions(+), 89 deletions(-)

diff --git a/www/cgi-bin/pampa/pampa.py b/www/cgi-bin/pampa/pampa.py
index 8303085..9c0f320 100644
--- a/www/cgi-bin/pampa/pampa.py
+++ b/www/cgi-bin/pampa/pampa.py
@@ -7,87 +7,7 @@ import json
 import re
 import sys
 import common
-import shutil
-import zipfile
-
-
-#Vérification du mail
-def verif_mail(mail):
-    """ check the validity of email address """
-
-    res = mail.strip()
-    if res.find(' ') > 0:
-        return False
-    a = res.find('@')
-    if a <= 0:
-        return False
-    if res.find('@', a+1) > 0:
-        return False
-    return True
-
-def extract_files_from_fileitems(fileitems, final_dir, extensions_list=None, unzip=False):
-    """
-    Cette fonction permet l'extraction de fichiers et leur copie vers le répertoire de résultats de l'analyse (dossier 'result/{run_id}').
-    Ces fichiers ont été fournie par l'utilisateur, le paramètre 'fileitems' dont correspondre à un objet fileitems (type spécifique associé au téléchargement de fichiers). Ils serviront dans l'analyse de PAMPA.
-    Cette fonction permet de faire un filtre entre les fichiers dont l'extension est valide (extensions passés en arguments).
-    Les fichiers peuvent être contenus dans un fichier ZIP.
-    """
-    files_moved = []
-    files_not_moved = []
-
-    final_dir = final_dir.rstrip('/')
-    if not os.path.exists(final_dir):
-        os.mkdir(final_dir)
-
-    if extensions_list:
-        extensions = "|".join(extensions_list)
-
-    for f in fileitems:
-        fn = os.path.basename(f.filename)
-        re_extension = re.compile(os.path.splitext(fn)[1], re.IGNORECASE)  # for extension check
-        if unzip and re_extension.search(".zip"):
-            with zipfile.ZipFile(f.file, "r") as myzip:
-                for element in myzip.filelist:
-                    fname = element.filename 
-                    re_extension = re.compile(os.path.splitext(fname)[1], re.IGNORECASE)  # for extension check
-                    if (extensions_list is None or re_extension.search(extensions)) and not os.path.exists(final_dir + '/' + fname):
-                        open(final_dir + '/' + fname, 'wb').write(myzip.open(fname, "r").read())  # cp file
-                        files_moved.append(fname)
-                    elif fname[-1] == "/":
-                        pass  # if a directory is in the zipfile.filelist: pass (no recursivity)
-                    else:
-                        files_not_moved.append(fname)
-        elif (extensions_list is None or re_extension.search(extensions)) and not os.path.exists(final_dir + '/' + fn):
-            open(final_dir + '/' + fn, 'wb').write(f.file.read())  # cp file
-            files_moved.append(fn)
-        else:
-            files_not_moved.append(fn)
-    
-    return files_moved, files_not_moved
-
-def extract_files_from_list(filename_list, final_dir):
-    """
-    Déplacement de fichiers internes.
-    l'unzip est systématique
-    """
-    final_dir = final_dir.rstrip('/')
-    if not os.path.exists(final_dir):
-        os.mkdir(final_dir)
-
-    for fn in filename_list:
-        if os.path.exists(fn):
-            re_extension = re.compile(os.path.splitext(fn)[1], re.IGNORECASE)  # to detect ZIP archive
-            if re_extension.search(".zip"):
-                with zipfile.ZipFile(fn, "r") as myzip:
-                    for element in myzip.filelist:
-                        fname = element.filename 
-                        if fname[-1] != "/":  # not a directory
-                            open(final_dir + '/' + fname, 'wb').write(myzip.open(fname, "r").read())  # cp file
-            else:
-                open(final_dir + '/' + os.path.basename(fn), 'w').write(open(fn, "r").read())  # cp file
-    return None
-
-
+from tools import *
 
 #Fonction permettant de vérifier les données soumises dans le formulaire, retour dans un dictionnaire (req)
 #Fonction qui sera modifiée pour le traitement de vos données
@@ -147,7 +67,7 @@ def extract_request(form):
 
     if 'spectra_example_1' in form:
         # Cas où les spectres à analyser sont les spectres de l'exemple 1 (copie de fichiers interne plutôt qu'upload des spectres utilisateur)
-        extract_files_from_list([common.SPECTRA_EXAMPLE_1], abs_spectra_dir)
+        FormTools.extract_files_from_list([common.SPECTRA_EXAMPLE_1], abs_spectra_dir)
     elif 'spectra_files' in form:
         # spectres utilisateurs 
         fileitems = form['spectra_files']
@@ -156,7 +76,7 @@ def extract_request(form):
         if fileitems[0].filename == "":
             error_messages.append("At least one spectrum is required.")
         else:
-            uploaded, not_uploaded = extract_files_from_fileitems(fileitems, abs_spectra_dir, extensions_list=[".csv", ".mgd", ".mzml"], unzip=True)
+            uploaded, not_uploaded = FormTools.extract_files_from_fileitems(fileitems, abs_spectra_dir, extensions_list=[".csv", ".mgd", ".mzml"], unzip=True)
             req['files_uploaded'].extend(uploaded)
             for fn in uploaded:
                 log.write(f"file {fn}\n")
@@ -244,7 +164,7 @@ def extract_request(form):
         # Récupération des jeux de données internes pour l'analyse
         req["peptides_files"] = []  # for the command
         f_list = [peptides_path_dict[group] for group in tax_groups]
-        extract_files_from_list(f_list, resdir)
+        FormTools.extract_files_from_list(f_list, resdir)
         for f in f_list:
             fn = os.path.basename(f)
             req["peptides_files"].append(resdir + '/' + fn)  # for the command
@@ -254,7 +174,7 @@ def extract_request(form):
         # Taxonomy selection : fichier interne
         f = common.TAXONOMY_REDUCED_FILE
         fn = os.path.basename(f)
-        extract_files_from_list([f], resdir)
+        FormTools.extract_files_from_list([f], resdir)
         req["taxo_file"] = resdir + '/' + fn  # for the command
         log.write(f"file {fn}\n")  # log
         req["taxo_source"] = "default_reduced"
@@ -282,7 +202,7 @@ def extract_request(form):
             elif peptides_fileitems[0].filename != "":  # Peptide table(s) provided
                 # Table de peptides
                 req['peptides_files'] = []  # for the command
-                uploaded, not_uploaded = extract_files_from_fileitems(peptides_fileitems, resdir, extensions_list=[".tsv"], unzip=True)
+                uploaded, not_uploaded = FormTools.extract_files_from_fileitems(peptides_fileitems, resdir, extensions_list=[".tsv"], unzip=True)
                 req['files_uploaded'].extend(uploaded)  # for the render_result
                 req["reference"] = "peptides"  # for the command
                 log.write(f"Reference type: {req['reference']}\n")  # log
@@ -299,7 +219,7 @@ def extract_request(form):
                 rel_sequences_dir = "sequences"
                 abs_sequences_dir = os.path.join(resdir,rel_sequences_dir)  # destination directory
                 req['sequences_dir'] = abs_sequences_dir  # for the command
-                uploaded, not_uploaded = extract_files_from_fileitems(sequences_fileitems, abs_sequences_dir, extensions_list=[".fasta", ".fna", ".fa"], unzip=True)
+                uploaded, not_uploaded = FormTools.extract_files_from_fileitems(sequences_fileitems, abs_sequences_dir, extensions_list=[".fasta", ".fna", ".fa"], unzip=True)
                 req['files_uploaded'].extend(uploaded)  # for the render_result 
                 req["reference"] = "sequences"  # for the command
                 log.write(f"Reference type: {req['reference']}\n")  # log
@@ -320,7 +240,7 @@ def extract_request(form):
                 # sur les données (table/sequences) utilisateurs, on utilise la taxonomy_all.tsv qui est plus générale.
                 f = common.TAXONOMY_ALL_FILE
                 fn = os.path.basename(f)
-                extract_files_from_list([f], resdir)
+                FormTools.extract_files_from_list([f], resdir)
                 req["taxo_file"] = resdir + '/' + fn  # for the command
                 req["taxo_source"] = "default_all"
                 log.write(f"file {fn}\n")  # log
@@ -332,7 +252,7 @@ def extract_request(form):
                 if 'taxo_file' in form and not isinstance(form['taxo_file'], list) and form['taxo_file'].filename != "":
                     f = form['taxo_file'].filename
                     fn = os.path.basename(f)
-                    uploaded, not_uploaded = extract_files_from_fileitems([form['taxo_file']], resdir, extensions_list=[".tsv"], unzip=False)
+                    uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['taxo_file']], resdir, extensions_list=[".tsv"], unzip=False)
                     req["taxo_file"] = resdir + '/' + fn  # for the command
                     req["taxo_source"] = "user"
                     req['files_uploaded'].extend(uploaded)  # for the render_result
-- 
GitLab


From 1937c8f229cbd53dfa53258f26788a2257caf7d7 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 24 Mar 2025 16:21:37 +0100
Subject: [PATCH 28/59] use Tools to avoid code duplication

---
 www/cgi-bin/pampa/craft_render_result.py | 37 +++---------------------
 1 file changed, 4 insertions(+), 33 deletions(-)

diff --git a/www/cgi-bin/pampa/craft_render_result.py b/www/cgi-bin/pampa/craft_render_result.py
index 013aaed..97fb49e 100644
--- a/www/cgi-bin/pampa/craft_render_result.py
+++ b/www/cgi-bin/pampa/craft_render_result.py
@@ -1,6 +1,4 @@
-import cgitb;
-
-cgitb.enable()
+import cgitb; cgitb.enable()
 import json
 import os, sys
 from html_utils import *
@@ -8,6 +6,7 @@ import common
 import zipfile
 import operator
 import table_maker
+from tools import *
 
 """
 NOTES
@@ -16,34 +15,6 @@ NOTES
 
 """
 
-
-
-def href_dl(link, name):
-    """
-    Construit une balise <a> ouvrant la page sur un nouvel onglet.
-    """
-    result = f'<a href="{link}" download="{name}" target="_blank">{name}</a>'
-    return result
-
-def zip_results(file_names, run_id, job_name=None):
-    """
-    Produit un fichier ZIP contenant tous les fichiers de résultat : 
-    - out.tsv
-    - detail_out.tsv
-    - report_out.txt
-    Ce fichier ZIP pourra être téléchargé par l'utilisateur.
-    """
-    with zipfile.ZipFile(f"{common.RESULT_DIR}{run_id}/data_result_{run_id}.zip", "a", compression=zipfile.ZIP_DEFLATED) as dwl_zip:
-        for fn in file_names:
-            abs_path = common.RESULT_DIR + run_id + '/' + fn
-            if os.path.exists(abs_path):
-                if job_name is not None:
-                    parts = os.path.splitext(fn)
-                    dwl_name = parts[0] + "_" + job_name + parts[1]
-                else: 
-                    dwl_name = fn
-                dwl_zip.write(abs_path, arcname=dwl_name)
-
 def results_output(run_id, job_name=None):
     """
     Produit la liste des fichiers téléchargeables par l'utilisateur.
@@ -68,7 +39,7 @@ def results_output(run_id, job_name=None):
                 dwl_name = parts[0] + "_" + job_name + parts[1]
             else: 
                 dwl_name = fn
-            html += li(output_files_title[fn] + href_dl(fn, dwl_name))
+            html += li(output_files_title[fn] + ResultTools.href_dl(fn, dwl_name))
     html += "</ul>"
 
     return  html
@@ -174,7 +145,7 @@ def main():
     fasta_file = params["sequences_files"]
     method = params["method"]
 
-    zip_results(["out_"+run_id+".tsv", "report_out_"+run_id+".txt"], run_id, job_name=job_name)
+    ResultTools.zip_results(["out_"+run_id+".tsv", "report_out_"+run_id+".txt", ], run_id, job_name=job_name)
 
     write_main_page(run_id, common.RESULT_DIR+ run_id + "/out_"+run_id+".tsv", table_files, method, job_name=job_name)
 
-- 
GitLab


From 59f739918d7dc6c22a756b3872e3cc8ae5394b0b Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 24 Mar 2025 16:27:43 +0100
Subject: [PATCH 29/59] use Tools to avoid code duplication

---
 www/cgi-bin/pampa/render_result.py | 33 +++---------------------------
 1 file changed, 3 insertions(+), 30 deletions(-)

diff --git a/www/cgi-bin/pampa/render_result.py b/www/cgi-bin/pampa/render_result.py
index 695ac0d..fbb1722 100644
--- a/www/cgi-bin/pampa/render_result.py
+++ b/www/cgi-bin/pampa/render_result.py
@@ -6,6 +6,7 @@ import common
 import zipfile
 import operator
 from render_sub_assignment import *
+from tools import *
 
 """
 NOTES
@@ -14,15 +15,6 @@ NOTES
 
 """
 
-
-
-def href_dl(link, name):
-    """
-    Construit une balise <a> ouvrant la page sur un nouvel onglet.
-    """
-    result = f'<a href="{link}" download="{name}" target="_blank">{name}</a>'
-    return result
-
 def command_builder_taxoreducer(taxonomy, output, reroot=False, peptides_tables=None, sequence_dir=None, limit_file=None):
     """
     Construit la ligne de commande adaptée à l'utilisation du script 'main_taxonomy_reduced'. Ce script produit une taxonomie réduite sur la base de l'ensemble des espèces sur lesquelles des données sont renseignées (sous la forme de tables de peptides ou d'un dossier de fichiers fasta de séquences de collagène) ou d'un limit_file (au format employé par PAMPA).
@@ -41,25 +33,6 @@ def command_builder_taxoreducer(taxonomy, output, reroot=False, peptides_tables=
         command += " -r"
     return command
 
-def zip_results(file_names, run_id, job_name=None):
-    """
-    Produit un fichier ZIP contenant tous les fichiers de résultat : 
-    - out.tsv
-    - detail_out.tsv
-    - report_out.txt
-    Ce fichier ZIP pourra être téléchargé par l'utilisateur.
-    """
-    with zipfile.ZipFile(f"{common.RESULT_DIR}{run_id}/data_result_{run_id}.zip", "a", compression=zipfile.ZIP_DEFLATED) as dwl_zip:
-        for fn in file_names:
-            abs_path = common.RESULT_DIR + run_id + '/' + fn
-            if os.path.exists(abs_path):
-                if job_name is not None:
-                    parts = os.path.splitext(fn)
-                    dwl_name = parts[0] + "_" + job_name + parts[1]
-                else: 
-                    dwl_name = fn
-                dwl_zip.write(abs_path, arcname=dwl_name)
-
 def results_output(run_id, job_name=None):
     """
     Produit la liste des fichiers téléchargeables par l'utilisateur.
@@ -84,7 +57,7 @@ def results_output(run_id, job_name=None):
                 dwl_name = parts[0] + "_" + job_name + parts[1]
             else: 
                 dwl_name = fn
-            html += li(output_files_title[fn] + href_dl(fn, dwl_name))
+            html += li(output_files_title[fn] + ResultTools.href_dl(fn, dwl_name))
     html += "</ul>"
 
     return  html
@@ -257,7 +230,7 @@ def main():
         data_dir = params['data_dir']
 
     # production du fichier ZIP contenant les résultats
-    zip_results(["out_"+run_id+".tsv", "detail_out_"+run_id+".tsv", "report_out_"+run_id+".txt", "table_out_"+run_id+".tsv"], run_id, job_name=job_name)
+    ResultTools.zip_results(["out_"+run_id+".tsv", "detail_out_"+run_id+".tsv", "report_out_"+run_id+".txt", "table_out_"+run_id+".tsv"], run_id, job_name=job_name)
 
     # test de l'usage d'une taxonomie par PAMPA
     if taxo_source == "none":
-- 
GitLab


From 5294e30c902d0399865a39e9e7c0d933b082b0aa Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 26 Mar 2025 11:43:42 +0100
Subject: [PATCH 30/59] param error on fillin example

---
 www/html/pampa/js/pampa.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/www/html/pampa/js/pampa.js b/www/html/pampa/js/pampa.js
index 81f58fe..196ee7b 100644
--- a/www/html/pampa/js/pampa.js
+++ b/www/html/pampa/js/pampa.js
@@ -121,6 +121,7 @@ $(document).on("click", "#example-fillin", function() {
         $("#sequences_input input").attr("required", false);
         $("#sequences_example").show();
         $("input[name='craft-example']").attr("checked", true);
+        $("input[name='error']").val("0.1");
     });
 });
 
-- 
GitLab


From 7b931a46c34a37f21d3458e5f4ed88b61437d909 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 26 Mar 2025 11:44:22 +0100
Subject: [PATCH 31/59] Update style on form

---
 www/html/pampa/craft-fillin.php   | 48 ++++++++++++++++++++++++++-----
 www/html/pampa/craft-homology.php |  9 +++---
 2 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/www/html/pampa/craft-fillin.php b/www/html/pampa/craft-fillin.php
index 662d221..8237ff8 100644
--- a/www/html/pampa/craft-fillin.php
+++ b/www/html/pampa/craft-fillin.php
@@ -33,11 +33,11 @@
     </div>
 
     <div class="formulaire">
-      <h2>Peptides Table</h2>
+      <h2>Peptide table</h2>
       <table class="vide">
         <tr>
           <td class="label">
-            <B>Upload</B> the peptides table that you want to complete in (.csv or .tsv)
+            <B>Upload</B> the peptide table that you want to complete in (.csv or .tsv)
           </td>
         </tr>
         <tr id="peptide_input">
@@ -57,16 +57,16 @@
     </div>
 
     <div class="formulaire">
-      <h2>Fasta File</h2>
+      <h2>Target sequences</h2>
         <table class="vide">
           <tr>
             <td class="label">
-              <B>Upload</B> The fasta file that you will use to complete your table.
+              <B>Upload</B> The fasta files that you will use to complete your table.
             </td>
           </tr>
           <tr id="sequences_input">
             <td>
-              <input type="file" name="sequences_files" value="file" accept=".fasta,.zip" multiple required/>
+              <input type="file" name="sequences_files" value="file" accept=".fasta,.zip" multiple/>
             </td>
           </tr>
           <tr id="sequences_example" style="display: none;">
@@ -79,7 +79,6 @@
         </table>
         <br>
 
-      <b>Limit File</b>
         <table class="vide">
           <tr>
             <td class="label">
@@ -91,14 +90,49 @@
               <input type="file" name="limit_file" value="file" accept=".txt"/>
             </td>
           </tr>
+        </table>
+        <br>
+        <table>
+          <tr>
+            <td class="label">
+              <B>Enter</B> The margin error
+            </td>
+          </tr>
           <tr id="error_input">
             <td>
-              <input type="number" name="error" step="0.0001" min="0.0001" max="1" placeholder="between 0.0001 and 1" value="0.1"/>
+              <input type="number" name="error" step="0.0001" min="0.0001" max="1" placeholder="between 0.0001 and 1"/>
             </td>
           </tr>
         </table>
     </div>
 
+    <div class="formulaire">
+        <h2>Taxonomy</h2>
+        <table class="vide">
+          <tr>
+            <td colspan="2">
+              <input type="radio" name="taxonomy_selection" value="default"/>
+              Use NCBI taxonomy
+            </td>
+          </tr>
+          <tr>
+            <td colspan="2">
+              <input type="radio" name="taxonomy_selection" value="no" checked/>
+              Provide no taxonomy
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <input type="radio" name="taxonomy_selection" value="custom"/>
+              Upload your own taxonomy
+            </td>
+            <td>
+              <input type="file" name="taxo_file" value="file" accept=".tsv"/>
+            </td>
+          </tr>
+        </table>
+      </div>
+
     <div class="center">
       <input type="button" id="example-fillin" name="example-fillin" value="Example">
     </div>
diff --git a/www/html/pampa/craft-homology.php b/www/html/pampa/craft-homology.php
index 639ce64..26d825f 100644
--- a/www/html/pampa/craft-homology.php
+++ b/www/html/pampa/craft-homology.php
@@ -33,11 +33,11 @@
     </div>
 
     <div class="formulaire">
-      <h2>Peptides Table</h2>
+      <h2>Peptide Table</h2>
       <table class="vide">
         <tr>
           <td class="label">
-            <B>Upload</B> the peptides table that you want to complete in (.csv or .tsv)
+            <B>Upload</B> the peptide table that you want to complete in (.csv or .tsv)
           </td>
         </tr>
         <tr id="peptide_input">
@@ -57,11 +57,11 @@
     </div>
 
     <div class="formulaire">
-      <h2>Fasta File</h2>
+      <h2>Target sequences</h2>
         <table class="vide">
           <tr>
             <td class="label">
-              <B>Upload</B> The fasta file that you will use to complete your table.
+              <B>Upload</B> The fasta files that you will use to complete your table.
             </td>
           </tr>
           <tr id="sequences_input">
@@ -79,7 +79,6 @@
         </table>
         <br>
 
-      <b>Limit File</b>
         <table class="vide">
           <tr>
             <td class="label">
-- 
GitLab


From d6ce930b30743c96bef0f9e403369081d6687dad Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 26 Mar 2025 11:44:53 +0100
Subject: [PATCH 32/59] Add deamidation form

---
 www/html/pampa/craft-deamidation.php | 100 +++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)
 create mode 100644 www/html/pampa/craft-deamidation.php

diff --git a/www/html/pampa/craft-deamidation.php b/www/html/pampa/craft-deamidation.php
new file mode 100644
index 0000000..14a855f
--- /dev/null
+++ b/www/html/pampa/craft-deamidation.php
@@ -0,0 +1,100 @@
+<?php include('header.php') ?>
+
+
+<body>
+  <div class="frametitle">
+   <h1 id="title">Pampa-Craft</h1>
+  </div>
+
+  <div id="center_sup">
+     <div class="theme-border" style="display:none"></div>
+     <div id="link_home" style="display:inline-block"><a href="/" class="text_onglet"><img src="/Style/icon/home_w.png" alt="home_general"/></a></div>
+   <div class="tabs" id="menu_central" style="display:inline-block"><?php include("menu_central.txt")?></div>
+  </div>
+<div id="main">
+  <div id="center">
+
+    <form id="formulaire-craft-f" method="post" enctype="multipart/form-data">
+
+    <input type="hidden" name="pampa_version" value="assign"/>
+    <input type="hidden" name="method" value="deamidation"/>
+
+    <div class="formulaire">
+      <table class="vide pampa_choice">
+        <tr>
+          <td class="label">
+            <h2>Name of the job <span style="font-weight: normal">(optional) :</span></h2>
+          </td>
+          <td>
+            <input type="text" name="job_name" maxlength="30"/>
+          </td>
+        </tr>
+      </table>
+    </div>
+
+    <div class="formulaire">
+      <h2>Peptides Table</h2>
+      <table class="vide">
+        <tr>
+          <td class="label">
+            <B>Upload</B> the peptides table that you want to complete in (.csv or .tsv)
+          </td>
+        </tr>
+        <tr id="peptide_input">
+          <td>
+            <input type="file" name="peptides_file" value="file" accept=".tsv" multiple required/>
+          </td>
+        </tr>
+        <tr id="peptide_example" style="display: none;">
+          <td>
+            <input class="file_input" id="back_to_normal" type="button" value="Parcourir..."/>
+            craft-example.tsv
+            <input type="checkbox" style="display: none;" name="craft-example"/>
+          </td>
+        </tr>
+      </table>
+      <br>
+    </div>
+
+    <div class="formulaire">
+      <h2>Limit File</h2>
+        <table class="vide">
+          <tr>
+            <td class="label">
+              <B>Upload</B> The limit file (optional, doc : https://github.com/touzet/pampa/wiki/Limiting-searches).
+            </td>
+          </tr>
+          <tr id="limit_input">
+            <td>
+              <input type="file" name="limit_file" value="file" accept=".txt"/>
+            </td>
+          </tr>
+        </table>
+    </div>
+
+    <div class="center">
+      <input type="button" id="example-fillin" name="example-fillin" value="Example">
+    </div>
+
+    <div class="center">
+      <input type="submit" id="reset-craft-f" name="reset" value="Reset" />
+      <input type="submit" id="run" name="button" value="Run" />
+      <input type="hidden" name="command" value="request" />
+    </div>
+
+  </form>
+
+</div><!--bloc -->
+</div><!-- main-->
+
+<!-- chargement de la librairie php lib.inc -->
+   <?php require("../lib.inc")?>
+<!-- appel de la fonction footer qui permet d'afficher au bas de la page (nom du logiciel, un lien vers le mail, la date de modif -->
+<!-- A modifier en fonction de votre logiciel -->
+   <?php footer("Pampa","Pampa", "areski.flissi@univ-lille.fr","2025"); ?>
+
+
+
+</body>
+</html>
+
-- 
GitLab


From 460fbd3fe714b3c2c289dd35ce24ef94b8ae3b0c Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 26 Mar 2025 11:46:06 +0100
Subject: [PATCH 33/59] Access to deamidation-form (TODO)

---
 www/html/pampa/pampa-craft.php | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/www/html/pampa/pampa-craft.php b/www/html/pampa/pampa-craft.php
index ead8f7a..d27f3a1 100644
--- a/www/html/pampa/pampa-craft.php
+++ b/www/html/pampa/pampa-craft.php
@@ -18,6 +18,8 @@
 
     <a href="/pampa/craft-fillin.php" class="mc" >Pampa-Craft fillin</a>
 
+    <a href="/pampa/craft-deamidation.php" class="mc" >Pampa-Craft deamidation</a>
+
 </div><!--bloc -->
 </div><!-- main-->
 
-- 
GitLab


From 606870ac72c6e6e5ca6e44a9a110e85cf7d9939d Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 26 Mar 2025 11:47:21 +0100
Subject: [PATCH 34/59] Protect header and remove quotes

---
 www/cgi-bin/pampa/table_maker.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/www/cgi-bin/pampa/table_maker.py b/www/cgi-bin/pampa/table_maker.py
index 5135ff8..9a0152f 100644
--- a/www/cgi-bin/pampa/table_maker.py
+++ b/www/cgi-bin/pampa/table_maker.py
@@ -57,7 +57,7 @@ function initGrid() {
                     })
                 }    
 
-                const columnDefs = ''' + json.dumps([{"field": h, "editable": True} for h in header]) + ''';
+                const columnDefs = ''' + json.dumps([{"field": h, "headerName": h, "editable": True} for h in header]) + ''';
 
                 const gridOptions = {
                     theme: agGrid.themeBalham,
@@ -108,7 +108,8 @@ function initGrid() {
                 window.exportData = function() {
                     gridApi.exportDataAsCsv({
                         fileName: "result.tsv",
-                        columnSeparator: '\\t'
+                        columnSeparator: '\\t',
+                        suppressQuotes: true,
                     });
                 };
             }
-- 
GitLab


From 262e02ad7ccee92c2c2d4964e3d3aa29cb3a0cf5 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 26 Mar 2025 11:48:27 +0100
Subject: [PATCH 35/59] support for deamidation add support for taxonomy

---
 www/cgi-bin/pampa/pampa-craft.py | 60 +++++++++++++++++++++++++++-----
 1 file changed, 52 insertions(+), 8 deletions(-)

diff --git a/www/cgi-bin/pampa/pampa-craft.py b/www/cgi-bin/pampa/pampa-craft.py
index 3127bdc..d4b898c 100644
--- a/www/cgi-bin/pampa/pampa-craft.py
+++ b/www/cgi-bin/pampa/pampa-craft.py
@@ -89,7 +89,8 @@ def extract_request(form):
         if not isinstance(fileitems, list):
             fileitems = [fileitems]
         if fileitems[0].filename == "":
-            error_messages.append("At least one FASTA file is required.")
+            if method != "fillin" and method != "deamidation":
+                error_messages.append("At least one FASTA file is required.")
         else:
             uploaded, not_uploaded = FormTools.extract_files_from_fileitems(fileitems, abs_sequences_dir,
                                                                   extensions_list=[".txt", ".fasta"], unzip=True)
@@ -104,7 +105,8 @@ def extract_request(form):
                 else:
                     error_messages.append(f"{fn} : Sequences must be in FASTA or in a ZIP archive.")
     else:
-        error_messages.append("At least one spectrum is required.")
+        if method != "fillin" and method != "deamidation":
+            error_messages.append("At least one sequence is required.")
 
     if 'limit_file' in form and form['limit_file'].filename != "":
         f = form['limit_file'].filename
@@ -122,6 +124,44 @@ def extract_request(form):
         error_value = form['error'].value.strip()
         if error_value:
             req['error'] = error_value
+        else:
+            req['error'] = "0"
+
+    if method == "fillin":
+        if "taxonomy_selection" in form:
+            taxonomy_selection = form["taxonomy_selection"].value
+            if taxonomy_selection == "default":
+                # sur les données (table/sequences) utilisateurs, on utilise la taxonomy_all.tsv qui est plus générale.
+                f = common.TAXONOMY_ALL_FILE
+                fn = os.path.basename(f)
+                FormTools.extract_files_from_list([f], resdir)
+                req["taxo_file"] = resdir + '/' + fn  # for the command
+                req["taxo_source"] = "default_all"
+                log.write(f"file {fn}\n")  # log
+            elif taxonomy_selection == "no":
+                # pas d'information taxonomique -> pas de fichier taxo.tsv
+                log.write("No taxonomy\n")
+            elif taxonomy_selection == "custom":
+                # upload de la taxonomie utilisateur si possible, sinon erreur
+                if 'taxo_file' in form and not isinstance(form['taxo_file'], list) and form['taxo_file'].filename != "":
+                    f = form['taxo_file'].filename
+                    fn = os.path.basename(f)
+                    uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['taxo_file']], resdir,
+                                                                                    extensions_list=[".tsv"], unzip=False)
+                    req["taxo_file"] = resdir + '/' + fn  # for the command
+                    req["taxo_source"] = "user"
+                    req['files_uploaded'].extend(uploaded)  # for the render_result
+                    for fn in uploaded:
+                        log.write(f"file {fn}\n")
+                    for fn in not_uploaded:
+                        error_messages.append(f"{fn} : The taxonomy file must be in TSV format.")
+                else:
+                    error_messages.append("If you choose to upload your own taxonomy, a taxonomy file is required.")
+            else:
+                error_messages.append(
+                    f"Internal error. An unexpected value has been submitted in the 'taxonomy_selection' input : {taxonomy_selection}")
+        else:
+            error_messages.append("A choice is required in the 'Taxonomy' section.")
 
 
     log.write("Recap downloads :\n")
@@ -140,21 +180,25 @@ def extract_request(form):
 # Fonction à modifier pour l'adapter à votre programme
 def launch_software(run_id, req):
     # Build the command line for PAMPA
-    peptides_arg = req['peptides_path']
-
     output_arg = "out_" + run_id + ".tsv"
-
-    sequences_arg = req["sequences_dir"]
-
     method = req['method']
 
-    command = "python pampa_craft.py --" + method + " -p " + peptides_arg + " -d " + sequences_arg + " -o " + common.RESULT_DIR + run_id + "/" + output_arg
+    command = "python pampa_craft.py --" + method + " -o " + common.RESULT_DIR + run_id + "/" + output_arg
+    if 'peptides_path' in req:
+        peptides_arg = req['peptides_path']
+        command += " -p " + peptides_arg
+    if 'sequences_dir' in req:
+        sequences_arg = req['sequences_dir']
+        command += " -d " + sequences_arg
     if 'limit_path' in req:
         limit_arg = req["limit_path"]
         command += " -l " + limit_arg
     if 'error' in req:
         error_arg = req["error"]
         command += " -e " + error_arg
+    if 'taxo_file' in req:
+        taxo_file_arg = req["taxo_file"]
+        command += " -t " + taxo_file_arg
     command += " --web"
     open("acommand.txt", "w").write(f"{command}")  # log
 
-- 
GitLab


From eb8091fc671ed29ed3def499b6a4b04251c806dd Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 27 Mar 2025 15:05:06 +0100
Subject: [PATCH 36/59] working extract_files_from_fileitems()

---
 www/cgi-bin/pampa/tools.py | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/www/cgi-bin/pampa/tools.py b/www/cgi-bin/pampa/tools.py
index 6d76f83..2cd6be6 100644
--- a/www/cgi-bin/pampa/tools.py
+++ b/www/cgi-bin/pampa/tools.py
@@ -46,30 +46,34 @@ class FormTools:
 
         final_dir = final_dir.rstrip('/')
         if not os.path.exists(final_dir):
-            os.mkdir(final_dir)
+            os.makedirs(final_dir, exist_ok=True)
 
         if extensions_list:
             extensions = "|".join(extensions_list)
 
         for f in fileitems:
             fn = os.path.basename(f.filename)
-            re_extension = re.compile(os.path.splitext(fn)[1], re.IGNORECASE)  # for extension check
+            re_extension = re.compile(os.path.splitext(fn)[1], re.IGNORECASE)
+
             if unzip and re_extension.search(".zip"):
                 with zipfile.ZipFile(f.file, "r") as myzip:
-                    for element in myzip.filelist:
-                        fname = element.filename
-                        re_extension = re.compile(os.path.splitext(fname)[1], re.IGNORECASE)  # for extension check
-                        if (extensions_list is None or re_extension.search(extensions)) and not os.path.exists(
-                                final_dir + '/' + fname):
-                            open(final_dir + '/' + fname, 'wb').write(myzip.open(fname, "r").read())  # cp file
+                    for element in myzip.infolist():
+                        fname = os.path.basename(element.filename)
+
+                        if element.is_dir():
+                            continue
+
+                        re_extension = re.compile(os.path.splitext(fname)[1], re.IGNORECASE)
+                        if (extensions_list is None or re_extension.search(extensions)) and not os.path.exists(os.path.join(final_dir, fname)):
+                            with myzip.open(element.filename, "r") as src, open(os.path.join(final_dir, fname), 'wb') as dst:
+                                dst.write(src.read())
                             files_moved.append(fname)
-                        elif fname[-1] == "/":
-                            pass  # if a directory is in the zipfile.filelist: pass (no recursivity)
                         else:
                             files_not_moved.append(fname)
             elif (extensions_list is None or re_extension.search(extensions)) and not os.path.exists(
-                    final_dir + '/' + fn):
-                open(final_dir + '/' + fn, 'wb').write(f.file.read())  # cp file
+                    os.path.join(final_dir, fn)):
+                with open(os.path.join(final_dir, fn), 'wb') as dst:
+                    dst.write(f.file.read())
                 files_moved.append(fn)
             else:
                 files_not_moved.append(fn)
-- 
GitLab


From b1dcdb3dd274117451e9575cad27f224d68f1f26 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Fri, 28 Mar 2025 16:39:25 +0100
Subject: [PATCH 37/59] allpeptides form

---
 www/html/pampa/craft-allpeptides.php | 155 +++++++++++++++++++++++++++
 1 file changed, 155 insertions(+)
 create mode 100644 www/html/pampa/craft-allpeptides.php

diff --git a/www/html/pampa/craft-allpeptides.php b/www/html/pampa/craft-allpeptides.php
new file mode 100644
index 0000000..a88ab8e
--- /dev/null
+++ b/www/html/pampa/craft-allpeptides.php
@@ -0,0 +1,155 @@
+<?php include('header.php') ?>
+
+
+<body>
+  <div class="frametitle">
+   <h1 id="title">Pampa-Craft</h1>
+  </div>
+
+  <div id="center_sup">
+     <div class="theme-border" style="display:none"></div>
+     <div id="link_home" style="display:inline-block"><a href="/" class="text_onglet"><img src="/Style/icon/home_w.png" alt="home_general"/></a></div>
+   <div class="tabs" id="menu_central" style="display:inline-block"><?php include("menu_central.txt")?></div>
+  </div>
+<div id="main">
+  <div id="center">
+
+    <form id="formulaire-craft-f" method="post" enctype="multipart/form-data">
+
+    <input type="hidden" name="pampa_version" value="assign"/>
+    <input type="hidden" name="method" value="allpeptides"/>
+
+    <div class="formulaire">
+      <table class="vide pampa_choice">
+        <tr>
+          <td class="label">
+            <h2>Name of the job <span style="font-weight: normal">(optional) :</span></h2>
+          </td>
+          <td>
+            <input type="text" name="job_name" maxlength="30"/>
+          </td>
+        </tr>
+      </table>
+    </div>
+
+    <div class="formulaire">
+      <h2>Target sequences</h2>
+        <table class="vide">
+          <tr>
+            <td class="label">
+              <B>Upload</B> The fasta files that you will use to complete your table.
+            </td>
+          </tr>
+          <tr id="sequences_input">
+            <td>
+              <input type="file" name="sequences_files" value="file" accept=".fasta,.zip" multiple required/>
+            </td>
+          </tr>
+          <tr id="sequences_example" style="display: none;">
+            <td>
+              <input class="file_input" id="back_to_normal" type="button" value="Parcourir..."/>
+              craft-example.zip
+              <input type="checkbox" style="display: none;" name="craft-example"/>
+            </td>
+          </tr>
+        </table>
+        <br>
+
+        <table class="vide">
+          <tr>
+            <td class="label">
+              <B>Upload</B> The limit file (optional, doc : https://github.com/touzet/pampa/wiki/Limiting-searches).
+            </td>
+          </tr>
+          <tr id="limit_input">
+            <td>
+              <input type="file" name="limit_file" value="file" accept=".txt"/>
+            </td>
+          </tr>
+        </table>
+    </div>
+
+        <div class="formulaire">
+      <h2>Mass spectra</h2>
+      <table class="vide">
+        <tr>
+          <td class="label">
+          <B>Upload</B> your MS spectra files in CSV, MGF or mzML format (or in a .ZIP archive)
+          </td>
+        </tr>
+        <tr id="spectra_input">
+          <td>
+            <input type="file" name="spectra_files" value="file" accept=".csv,.mgf,.mzml,.zip" multiple required/>
+          </td>
+        </tr>
+        <tr id="spectra_example" style="display: none;">
+          <td>
+            <input class="file_input" id="back_to_normal" type="button" value="Parcourir..."/>
+            pampa_example_1.zip
+            <input type="checkbox" style="display: none;" name="spectra_example_1"/>
+          </td>
+        </tr>
+      </table>
+      <br>
+
+      <b>Mass error</b>
+      <table class="vide pampa_choice">
+        <tr>
+          <td>
+            <input type="radio" name="error_margin_selection" value="MALDI_TOF" checked/>
+            Optimize for MALDI-TOF spectra
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <input type="radio" name="error_margin_selection" value="MALDI_FTICR"/>
+            Optimize for MALDI-FTICR spectra
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <input type="radio" name="error_margin_selection" value="ppm"/>
+            Custom value in ppm
+          </td>
+          <td>
+            <input type="number" name="error_margin_ppm" step="1" min="1" max="1000" placeholder="between 1 and 1000"/>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <input type="radio" name="error_margin_selection" value="daltons">
+            Custom value in Daltons
+          </td>
+          <td>
+            <input type="number" name="error_margin_daltons" step="0.002" min="0.002" max="0.998" placeholder="between 0.002 and 0.998"/>
+          </td>
+        </tr>
+      </table>
+    </div>
+
+    <div class="center">
+      <input type="button" id="example-fillin" name="example-fillin" value="Example">
+    </div>
+
+    <div class="center">
+      <input type="submit" id="reset-craft-f" name="reset" value="Reset" />
+      <input type="submit" id="run" name="button" value="Run" />
+      <input type="hidden" name="command" value="request" />
+    </div>
+
+  </form>
+
+</div><!--bloc -->
+</div><!-- main-->
+
+<!-- chargement de la librairie php lib.inc -->
+   <?php require("../lib.inc")?>
+<!-- appel de la fonction footer qui permet d'afficher au bas de la page (nom du logiciel, un lien vers le mail, la date de modif -->
+<!-- A modifier en fonction de votre logiciel -->
+   <?php footer("Pampa","Pampa", "areski.flissi@univ-lille.fr","2025"); ?>
+
+
+
+</body>
+</html>
+
-- 
GitLab


From fae3454799bea0d24116fdf361a3f9f3c2cfc01c Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Fri, 28 Mar 2025 16:39:40 +0100
Subject: [PATCH 38/59] selection form

---
 www/html/pampa/craft-selection.php | 142 +++++++++++++++++++++++++++++
 1 file changed, 142 insertions(+)
 create mode 100644 www/html/pampa/craft-selection.php

diff --git a/www/html/pampa/craft-selection.php b/www/html/pampa/craft-selection.php
new file mode 100644
index 0000000..783e9a8
--- /dev/null
+++ b/www/html/pampa/craft-selection.php
@@ -0,0 +1,142 @@
+<?php include('header.php') ?>
+
+
+<body>
+  <div class="frametitle">
+   <h1 id="title">Pampa-Craft</h1>
+  </div>
+
+  <div id="center_sup">
+     <div class="theme-border" style="display:none"></div>
+     <div id="link_home" style="display:inline-block"><a href="/" class="text_onglet"><img src="/Style/icon/home_w.png" alt="home_general"/></a></div>
+   <div class="tabs" id="menu_central" style="display:inline-block"><?php include("menu_central.txt")?></div>
+  </div>
+<div id="main">
+  <div id="center">
+
+    <form id="formulaire-craft-f" method="post" enctype="multipart/form-data">
+
+    <input type="hidden" name="pampa_version" value="assign"/>
+    <input type="hidden" name="method" value="selection"/>
+
+    <div class="formulaire">
+      <table class="vide pampa_choice">
+        <tr>
+          <td class="label">
+            <h2>Name of the job <span style="font-weight: normal">(optional) :</span></h2>
+          </td>
+          <td>
+            <input type="text" name="job_name" maxlength="30"/>
+          </td>
+        </tr>
+      </table>
+    </div>
+
+    <div class="formulaire">
+      <h2>Peptide Table</h2>
+      <table class="vide">
+        <tr>
+          <td class="label">
+            <B>Upload</B> the peptide table that you want to complete in (.csv or .tsv)
+          </td>
+        </tr>
+        <tr id="peptide_input">
+          <td>
+            <input type="file" name="peptides_file" value="file" accept=".tsv" multiple required/>
+          </td>
+        </tr>
+        <tr id="peptide_example" style="display: none;">
+          <td>
+            <input class="file_input" id="back_to_normal" type="button" value="Parcourir..."/>
+            craft-example.tsv
+            <input type="checkbox" style="display: none;" name="craft-example"/>
+          </td>
+        </tr>
+      </table>
+      <br>
+    </div>
+
+        <div class="formulaire">
+      <h2>Mass spectra</h2>
+      <table class="vide">
+        <tr>
+          <td class="label">
+          <B>Upload</B> your MS spectra files in CSV, MGF or mzML format (or in a .ZIP archive)
+          </td>
+        </tr>
+        <tr id="spectra_input">
+          <td>
+            <input type="file" name="spectra_files" value="file" accept=".csv,.mgf,.mzml,.zip" multiple required/>
+          </td>
+        </tr>
+        <tr id="spectra_example" style="display: none;">
+          <td>
+            <input class="file_input" id="back_to_normal" type="button" value="Parcourir..."/>
+            pampa_example_1.zip
+            <input type="checkbox" style="display: none;" name="spectra_example_1"/>
+          </td>
+        </tr>
+      </table>
+      <br>
+
+      <b>Mass error</b>
+      <table class="vide pampa_choice">
+        <tr>
+          <td>
+            <input type="radio" name="error_margin_selection" value="MALDI_TOF" checked/>
+            Optimize for MALDI-TOF spectra
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <input type="radio" name="error_margin_selection" value="MALDI_FTICR"/>
+            Optimize for MALDI-FTICR spectra
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <input type="radio" name="error_margin_selection" value="ppm"/>
+            Custom value in ppm
+          </td>
+          <td>
+            <input type="number" name="error_margin_ppm" step="1" min="1" max="1000" placeholder="between 1 and 1000"/>
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <input type="radio" name="error_margin_selection" value="daltons">
+            Custom value in Daltons
+          </td>
+          <td>
+            <input type="number" name="error_margin_daltons" step="0.002" min="0.002" max="0.998" placeholder="between 0.002 and 0.998"/>
+          </td>
+        </tr>
+      </table>
+    </div>
+
+    <div class="center">
+      <input type="button" id="example-fillin" name="example-fillin" value="Example">
+    </div>
+
+    <div class="center">
+      <input type="submit" id="reset-craft-f" name="reset" value="Reset" />
+      <input type="submit" id="run" name="button" value="Run" />
+      <input type="hidden" name="command" value="request" />
+    </div>
+
+  </form>
+
+</div><!--bloc -->
+</div><!-- main-->
+
+<!-- chargement de la librairie php lib.inc -->
+   <?php require("../lib.inc")?>
+<!-- appel de la fonction footer qui permet d'afficher au bas de la page (nom du logiciel, un lien vers le mail, la date de modif -->
+<!-- A modifier en fonction de votre logiciel -->
+   <?php footer("Pampa","Pampa", "areski.flissi@univ-lille.fr","2025"); ?>
+
+
+
+</body>
+</html>
+
-- 
GitLab


From 51dd8847a8f1c77fa063594762db67b60e582efd Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Fri, 28 Mar 2025 16:40:13 +0100
Subject: [PATCH 39/59] handle allpeptides/selection

---
 www/cgi-bin/pampa/pampa-craft.py | 80 ++++++++++++++++++++++++++++----
 1 file changed, 71 insertions(+), 9 deletions(-)

diff --git a/www/cgi-bin/pampa/pampa-craft.py b/www/cgi-bin/pampa/pampa-craft.py
index d4b898c..e7068fe 100644
--- a/www/cgi-bin/pampa/pampa-craft.py
+++ b/www/cgi-bin/pampa/pampa-craft.py
@@ -75,21 +75,23 @@ def extract_request(form):
             error_messages.append(f"{fn} : The peptide table file must be in TSV format.")
 
     rel_sequences_dir = "sequences"
+    rel_spectra_dir = "spectra"
     abs_sequences_dir = os.path.join(resdir, rel_sequences_dir)
-    req['sequences_dir'] = abs_sequences_dir
-    log.write(f"sequences_dir: {req['sequences_dir']}\n")
+    abs_spectra_dir = os.path.join(resdir, rel_spectra_dir)
 
     if 'craft-example' in form:
+        req['sequences_dir'] = abs_sequences_dir
         if method == "homology":
             FormTools.extract_files_from_list([common.HOMOLOGY_SEQUENCES], abs_sequences_dir)
         elif method == "fillin":
             FormTools.extract_files_from_list([common.FILLIN_SEQUENCES], abs_sequences_dir)
     elif 'sequences_files' in form:
+        req['sequences_dir'] = abs_sequences_dir
         fileitems = form['sequences_files']
         if not isinstance(fileitems, list):
             fileitems = [fileitems]
         if fileitems[0].filename == "":
-            if method != "fillin" and method != "deamidation":
+            if method != "fillin" and method != "deamidation" and method != "selection":
                 error_messages.append("At least one FASTA file is required.")
         else:
             uploaded, not_uploaded = FormTools.extract_files_from_fileitems(fileitems, abs_sequences_dir,
@@ -105,7 +107,7 @@ def extract_request(form):
                 else:
                     error_messages.append(f"{fn} : Sequences must be in FASTA or in a ZIP archive.")
     else:
-        if method != "fillin" and method != "deamidation":
+        if method != "fillin" and method != "deamidation" and method != "selection":
             error_messages.append("At least one sequence is required.")
 
     if 'limit_file' in form and form['limit_file'].filename != "":
@@ -120,12 +122,69 @@ def extract_request(form):
         for fn in not_uploaded:
             error_messages.append(f"{fn} : The taxonomy file must be in TSV format.")
 
-    if 'error' in form:
-        error_value = form['error'].value.strip()
-        if error_value:
-            req['error'] = error_value
+    if method == "fillin":
+        if 'error' in form:
+            error_value = form['error'].value.strip()
+            if error_value:
+                req['error'] = error_value
+            else:
+                req['error'] = "0"
+    if method == "allpeptides" or method == "selection":
+        if "error_margin_selection" in form:
+            error_margin_selection = form['error_margin_selection'].value
+            error_margin = 0
+            if error_margin_selection == "MALDI_TOF":
+                # valeur par défaut
+                error_margin = 50
+            elif error_margin_selection == "MALDI_FTICR":
+                # valeur par défaut
+                error_margin = 5
+            elif error_margin_selection == "ppm":
+                if "error_margin_ppm" in form:
+                    error_margin = int(form['error_margin_ppm'].value)
+                    if error_margin < 1 or error_margin > 1000:
+                        error_messages.append(f"The mass error in ppm must be within the range 1-1000. Input value: {error_margin}.")
+                else:
+                    error_messages.append(f"With the custom value in ppm option, a mass error value is required.")
+            elif error_margin_selection == "daltons":
+                if "error_margin_daltons" in form:
+                    error_margin = float(form['error_margin_daltons'].value)
+                    if not (error_margin > 0 and error_margin < 1):
+                        error_messages.append(f"The mass error in daltons must be within the range 0.002-0.998. Input value: {error_margin}.")
+                else:
+                    error_messages.append(f"With the custom value in Daltons option, a mass error value is required.")
+            else:
+                error_messages.append(f"Internal error. An unknow value have been entered in the 'error_margin_selection' input: {error_margin_selection}")
+            log.write(f"Error margin: {error_margin}\n")
+            req['error'] = str(error_margin)
+        else:
+            error_messages.append("A mass error selection is necessary for the analysis.")
+
+    if method == "selection" or method == "allpeptides":
+        if 'spectra_example_1' in form:
+            req['spectra_dir'] = abs_spectra_dir
+            # Cas où les spectres à analyser sont les spectres de l'exemple 1 (copie de fichiers interne plutôt qu'upload des spectres utilisateur)
+            FormTools.extract_files_from_list([common.SPECTRA_EXAMPLE_1], abs_spectra_dir)
+        elif 'spectra_files' in form:
+            req['spectra_dir'] = abs_spectra_dir
+            # spectres utilisateurs
+            fileitems = form['spectra_files']
+            if not isinstance(fileitems, list):
+                fileitems = [fileitems]
+            if fileitems[0].filename == "":
+                error_messages.append("At least one spectrum is required.")
+            else:
+                uploaded, not_uploaded = FormTools.extract_files_from_fileitems(fileitems, abs_spectra_dir, extensions_list=[".csv", ".mgd", ".mzml"], unzip=True)
+                req['files_uploaded'].extend(uploaded)
+                for fn in uploaded:
+                    log.write(f"file {fn}\n")
+                for fn in not_uploaded:
+                    if fn in uploaded :
+                        error_messages.append(f'The mass spectrum {fn} appears to be present several times in the data supplied.')
+                    else:
+                        error_messages.append(f"{fn} : Spectra must be in CSV, MGD or mzML format or in a ZIP archive.")
         else:
-            req['error'] = "0"
+            error_messages.append("At least one spectrum is required.")
 
     if method == "fillin":
         if "taxonomy_selection" in form:
@@ -190,6 +249,9 @@ def launch_software(run_id, req):
     if 'sequences_dir' in req:
         sequences_arg = req['sequences_dir']
         command += " -d " + sequences_arg
+    if 'spectra_dir' in req:
+        spectra_arg = req['spectra_dir']
+        command += " -s " + spectra_arg
     if 'limit_path' in req:
         limit_arg = req["limit_path"]
         command += " -l " + limit_arg
-- 
GitLab


From 0879ff4d6c6940343676f3f06575310f54497792 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Fri, 28 Mar 2025 16:40:30 +0100
Subject: [PATCH 40/59] TODO

---
 www/html/pampa/pampa-craft.php | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/www/html/pampa/pampa-craft.php b/www/html/pampa/pampa-craft.php
index d27f3a1..feb4cd4 100644
--- a/www/html/pampa/pampa-craft.php
+++ b/www/html/pampa/pampa-craft.php
@@ -20,6 +20,10 @@
 
     <a href="/pampa/craft-deamidation.php" class="mc" >Pampa-Craft deamidation</a>
 
+    <a href="/pampa/craft-allpeptides.php" class="mc" >Pampa-Craft allpeptides</a>
+
+    <a href="/pampa/craft-selection.php" class="mc" >Pampa-Craft selection</a>
+
 </div><!--bloc -->
 </div><!-- main-->
 
-- 
GitLab


From bc00855154de88b1c523eba331a4432add066015 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Fri, 28 Mar 2025 16:40:51 +0100
Subject: [PATCH 41/59] add checks

---
 www/cgi-bin/pampa/craft_render_result.py | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/www/cgi-bin/pampa/craft_render_result.py b/www/cgi-bin/pampa/craft_render_result.py
index 97fb49e..f7a835b 100644
--- a/www/cgi-bin/pampa/craft_render_result.py
+++ b/www/cgi-bin/pampa/craft_render_result.py
@@ -141,8 +141,12 @@ def main():
         job_name = params['job_name']
     else:
         job_name = None
-    table_files = params["peptides_path"]
-    fasta_file = params["sequences_files"]
+    if "peptides_path" in params:
+        table_files = params["peptides_path"]
+    else:
+        table_files = None
+    if "sequences_files" in params:
+        fasta_file = params["sequences_files"]
     method = params["method"]
 
     ResultTools.zip_results(["out_"+run_id+".tsv", "report_out_"+run_id+".txt", ], run_id, job_name=job_name)
-- 
GitLab


From ba1ae530d5ea0b514ebac253ece449af6eff460a Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 10 Apr 2025 10:12:45 +0200
Subject: [PATCH 42/59] no more classmethods in tools.py

---
 www/cgi-bin/pampa/tools.py | 292 +++++++++++++++++++------------------
 1 file changed, 153 insertions(+), 139 deletions(-)

diff --git a/www/cgi-bin/pampa/tools.py b/www/cgi-bin/pampa/tools.py
index 2cd6be6..c3fff08 100644
--- a/www/cgi-bin/pampa/tools.py
+++ b/www/cgi-bin/pampa/tools.py
@@ -4,144 +4,158 @@ import cgi
 import re
 import common
 
-class FormTools:
-
-    @classmethod
-    def verif_mail(cls, mail):
-        """
-        Vérification d'une adresse email.
-
-        Args:
-            mail (str): Email à valider
-
-        Returns:
-            bool: True/False
-        """
-        res = mail.strip()
-        if res.find(' ') > 0:
-            return False
-        a = res.find('@')
-        if a <= 0:
-            return False
-        if res.find('@', a + 1) > 0:
-            return False
-        return True
-
-    @classmethod
-    def extract_files_from_fileitems(cls, fileitems, final_dir, extensions_list=None, unzip=False):
-        """
-        Gère les fichiers uploadés (dont ZIP) et les copie dans le dossier cible.
-
-        Args:
-            fileitems (list): Liste de fichiers type Werkzeug FileStorage
-            final_dir (str): Dossier de destination
-            extensions_list (list): Extensions autorisées (ex: ['.txt', '.csv'])
-            unzip (bool): Auto-extraction des ZIP si True
-
-        Returns:
-            tuple: (fichiers transférés, fichiers rejetés)
-        """
-        files_moved = []
-        files_not_moved = []
-
-        final_dir = final_dir.rstrip('/')
-        if not os.path.exists(final_dir):
-            os.makedirs(final_dir, exist_ok=True)
-
-        if extensions_list:
-            extensions = "|".join(extensions_list)
-
-        for f in fileitems:
-            fn = os.path.basename(f.filename)
-            re_extension = re.compile(os.path.splitext(fn)[1], re.IGNORECASE)
-
-            if unzip and re_extension.search(".zip"):
-                with zipfile.ZipFile(f.file, "r") as myzip:
-                    for element in myzip.infolist():
-                        fname = os.path.basename(element.filename)
-
-                        if element.is_dir():
-                            continue
-
-                        re_extension = re.compile(os.path.splitext(fname)[1], re.IGNORECASE)
-                        if (extensions_list is None or re_extension.search(extensions)) and not os.path.exists(os.path.join(final_dir, fname)):
-                            with myzip.open(element.filename, "r") as src, open(os.path.join(final_dir, fname), 'wb') as dst:
-                                dst.write(src.read())
-                            files_moved.append(fname)
-                        else:
-                            files_not_moved.append(fname)
-            elif (extensions_list is None or re_extension.search(extensions)) and not os.path.exists(
-                    os.path.join(final_dir, fn)):
-                with open(os.path.join(final_dir, fn), 'wb') as dst:
-                    dst.write(f.file.read())
-                files_moved.append(fn)
+# -------------------------------------
+# Form
+
+def verif_mail(mail):
+    """
+    Vérification d'une adresse email.
+
+    Args:
+        mail (str): Email à valider
+
+    Returns:
+        bool: True/False
+    """
+    res = mail.strip()
+    if res.find(' ') > 0:
+        return False
+    a = res.find('@')
+    if a <= 0:
+        return False
+    if res.find('@', a + 1) > 0:
+        return False
+    return True
+
+
+def extract_files_from_fileitems(fileitems, final_dir, extensions_list=None, unzip=False):
+    """
+    Gère les fichiers uploadés (dont ZIP) et les copie dans le dossier cible.
+
+    Args:
+        fileitems (list): Liste de fichiers type Werkzeug FileStorage
+        final_dir (str): Dossier de destination
+        extensions_list (list): Extensions autorisées (ex: ['.txt', '.csv'])
+        unzip (bool): Auto-extraction des ZIP si True
+
+    Returns:
+        tuple: (fichiers transférés, fichiers rejetés)
+    """
+    files_moved = []
+    files_not_moved = []
+
+    final_dir = final_dir.rstrip('/')
+    if not os.path.exists(final_dir):
+        os.makedirs(final_dir, exist_ok=True)
+
+    if extensions_list:
+        extensions = "|".join(extensions_list)
+
+    for f in fileitems:
+        fn = os.path.basename(f.filename)
+        re_extension = re.compile(os.path.splitext(fn)[1], re.IGNORECASE)
+
+        if unzip and re_extension.search(".zip"):
+            with zipfile.ZipFile(f.file, "r") as myzip:
+                for element in myzip.infolist():
+                    fname = os.path.basename(element.filename)
+
+                    if element.is_dir():
+                        continue
+
+                    re_extension = re.compile(os.path.splitext(fname)[1], re.IGNORECASE)
+                    if (extensions_list is None or re_extension.search(extensions)) and not os.path.exists(os.path.join(final_dir, fname)):
+                        with myzip.open(element.filename, "r") as src, open(os.path.join(final_dir, fname), 'wb') as dst:
+                            dst.write(src.read())
+                        files_moved.append(fname)
+                    else:
+                        files_not_moved.append(fname)
+        elif (extensions_list is None or re_extension.search(extensions)) and not os.path.exists(
+                os.path.join(final_dir, fn)):
+            with open(os.path.join(final_dir, fn), 'wb') as dst:
+                dst.write(f.file.read())
+            files_moved.append(fn)
+        else:
+            files_not_moved.append(fn)
+
+    return files_moved, files_not_moved
+
+def extract_files_from_list(filename_list, final_dir):
+    """
+    Copie des fichiers locaux vers un dossier cible avec extraction automatique des ZIP.
+
+    Args:
+        filename_list (list): Liste de chemins de fichiers locaux
+        final_dir (str): Dossier de destination
+    """
+    final_dir = final_dir.rstrip('/')
+    if not os.path.exists(final_dir):
+        os.mkdir(final_dir)
+
+    for fn in filename_list:
+        if os.path.exists(fn):
+            re_extension = re.compile(os.path.splitext(fn)[1], re.IGNORECASE)  # to detect ZIP archive
+            if re_extension.search(".zip"):
+                with zipfile.ZipFile(fn, "r") as myzip:
+                    for element in myzip.filelist:
+                        fname = element.filename
+                        if fname[-1] != "/":  # not a directory
+                            open(final_dir + '/' + fname, 'wb').write(myzip.open(fname, "r").read())  # cp file
             else:
-                files_not_moved.append(fn)
-
-        return files_moved, files_not_moved
-
-    @classmethod
-    def extract_files_from_list(cls, filename_list, final_dir):
-        """
-        Copie des fichiers locaux vers un dossier cible avec extraction automatique des ZIP.
-
-        Args:
-            filename_list (list): Liste de chemins de fichiers locaux
-            final_dir (str): Dossier de destination
-        """
-        final_dir = final_dir.rstrip('/')
-        if not os.path.exists(final_dir):
-            os.mkdir(final_dir)
-
-        for fn in filename_list:
-            if os.path.exists(fn):
-                re_extension = re.compile(os.path.splitext(fn)[1], re.IGNORECASE)  # to detect ZIP archive
-                if re_extension.search(".zip"):
-                    with zipfile.ZipFile(fn, "r") as myzip:
-                        for element in myzip.filelist:
-                            fname = element.filename
-                            if fname[-1] != "/":  # not a directory
-                                open(final_dir + '/' + fname, 'wb').write(myzip.open(fname, "r").read())  # cp file
+                open(final_dir + '/' + os.path.basename(fn), 'w').write(open(fn, "r").read())  # cp file
+    return None
+
+# -------------------------------------
+# Extract form data
+
+def extract_job(form, log, req, error_messages):
+    if "job_name" in form:
+        job_name = form["job_name"].value.strip().replace(' ', '_')
+        if job_name == "":
+            log.write("Job name: None\n")
+        elif len(job_name) <= 30:
+            req['job_name'] = job_name
+            log.write(f"Job name: {job_name}\n")
+        else:
+            error_messages.append(f"The job name cannot exceed 30 characters. Current size: {len(job_name)}.")
+    else:
+        log.write("Job name: None\n")
+
+
+# -------------------------------------
+# Result
+
+def href_dl(link, name):
+    """
+    Génère un lien HTML de téléchargement.
+
+    Args:
+        link (str): URL du fichier
+        name (str): Nom affiché du fichier
+
+    Returns:
+        str: Balise HTML <a> formatée
+    """
+    result = f'<a href="{link}" download="{name}" target="_blank">{name}</a>'
+    return result
+
+def zip_results(file_names, run_id, job_name=None):
+    """
+    Crée une archive ZIP des résultats d'analyse.
+
+    Args:
+        file_names (list): Noms des fichiers à archiver
+        run_id (str): Identifiant unique de l'analyse
+        job_name (str): Optionnel - suffixe pour les noms de fichiers
+    """
+    with zipfile.ZipFile(f"{common.RESULT_DIR}{run_id}/data_result_{run_id}.zip", "a",
+                            compression=zipfile.ZIP_DEFLATED) as dwl_zip:
+        for fn in file_names:
+            abs_path = common.RESULT_DIR + run_id + '/' + fn
+            if os.path.exists(abs_path):
+                if job_name is not None:
+                    parts = os.path.splitext(fn)
+                    dwl_name = parts[0] + "_" + job_name + parts[1]
                 else:
-                    open(final_dir + '/' + os.path.basename(fn), 'w').write(open(fn, "r").read())  # cp file
-        return None
-
-
-class ResultTools:
-
-    @classmethod
-    def href_dl(cls, link, name):
-        """
-        Génère un lien HTML de téléchargement.
-
-        Args:
-            link (str): URL du fichier
-            name (str): Nom affiché du fichier
-
-        Returns:
-            str: Balise HTML <a> formatée
-        """
-        result = f'<a href="{link}" download="{name}" target="_blank">{name}</a>'
-        return result
-
-    @classmethod
-    def zip_results(cls, file_names, run_id, job_name=None):
-        """
-        Crée une archive ZIP des résultats d'analyse.
-
-        Args:
-            file_names (list): Noms des fichiers à archiver
-            run_id (str): Identifiant unique de l'analyse
-            job_name (str): Optionnel - suffixe pour les noms de fichiers
-        """
-        with zipfile.ZipFile(f"{common.RESULT_DIR}{run_id}/data_result_{run_id}.zip", "a",
-                             compression=zipfile.ZIP_DEFLATED) as dwl_zip:
-            for fn in file_names:
-                abs_path = common.RESULT_DIR + run_id + '/' + fn
-                if os.path.exists(abs_path):
-                    if job_name is not None:
-                        parts = os.path.splitext(fn)
-                        dwl_name = parts[0] + "_" + job_name + parts[1]
-                    else:
-                        dwl_name = fn
-                    dwl_zip.write(abs_path, arcname=dwl_name)
\ No newline at end of file
+                    dwl_name = fn
+                dwl_zip.write(abs_path, arcname=dwl_name)
\ No newline at end of file
-- 
GitLab


From 31892d8c56c8574ad06185f30f0d5f3cb7540407 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 10 Apr 2025 10:14:20 +0200
Subject: [PATCH 43/59] don't allow multiple peptides table

---
 www/html/pampa/craft-allpeptides.php | 2 +-
 www/html/pampa/craft-deamidation.php | 2 +-
 www/html/pampa/craft-fillin.php      | 2 +-
 www/html/pampa/craft-homology.php    | 2 +-
 www/html/pampa/craft-selection.php   | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/www/html/pampa/craft-allpeptides.php b/www/html/pampa/craft-allpeptides.php
index a88ab8e..40adadb 100644
--- a/www/html/pampa/craft-allpeptides.php
+++ b/www/html/pampa/craft-allpeptides.php
@@ -79,7 +79,7 @@
         </tr>
         <tr id="spectra_input">
           <td>
-            <input type="file" name="spectra_files" value="file" accept=".csv,.mgf,.mzml,.zip" multiple required/>
+            <input type="file" name="spectra_files" value="file" accept=".csv,.mgf,.mzml,.zip" multiple/>
           </td>
         </tr>
         <tr id="spectra_example" style="display: none;">
diff --git a/www/html/pampa/craft-deamidation.php b/www/html/pampa/craft-deamidation.php
index 14a855f..a430742 100644
--- a/www/html/pampa/craft-deamidation.php
+++ b/www/html/pampa/craft-deamidation.php
@@ -42,7 +42,7 @@
         </tr>
         <tr id="peptide_input">
           <td>
-            <input type="file" name="peptides_file" value="file" accept=".tsv" multiple required/>
+            <input type="file" name="peptides_file" value="file" accept=".tsv,.csv" required/>
           </td>
         </tr>
         <tr id="peptide_example" style="display: none;">
diff --git a/www/html/pampa/craft-fillin.php b/www/html/pampa/craft-fillin.php
index 8237ff8..37816ef 100644
--- a/www/html/pampa/craft-fillin.php
+++ b/www/html/pampa/craft-fillin.php
@@ -42,7 +42,7 @@
         </tr>
         <tr id="peptide_input">
           <td>
-            <input type="file" name="peptides_file" value="file" accept=".tsv" multiple required/>
+            <input type="file" name="peptides_file" value="file" accept=".tsv,.csv" required/>
           </td>
         </tr>
         <tr id="peptide_example" style="display: none;">
diff --git a/www/html/pampa/craft-homology.php b/www/html/pampa/craft-homology.php
index 26d825f..cf395da 100644
--- a/www/html/pampa/craft-homology.php
+++ b/www/html/pampa/craft-homology.php
@@ -42,7 +42,7 @@
         </tr>
         <tr id="peptide_input">
           <td>
-            <input type="file" name="peptides_file" value="file" accept=".tsv" multiple required/>
+            <input type="file" name="peptides_file" value="file" accept=".tsv,.csv" required/>
           </td>
         </tr>
         <tr id="peptide_example" style="display: none;">
diff --git a/www/html/pampa/craft-selection.php b/www/html/pampa/craft-selection.php
index 783e9a8..d5abb61 100644
--- a/www/html/pampa/craft-selection.php
+++ b/www/html/pampa/craft-selection.php
@@ -42,7 +42,7 @@
         </tr>
         <tr id="peptide_input">
           <td>
-            <input type="file" name="peptides_file" value="file" accept=".tsv" multiple required/>
+            <input type="file" name="peptides_file" value="file" accept=".tsv,.csv" required/>
           </td>
         </tr>
         <tr id="peptide_example" style="display: none;">
-- 
GitLab


From 2b60530cf6872bc8aba59cbde633d10559c7d762 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 10 Apr 2025 10:37:50 +0200
Subject: [PATCH 44/59] correct bug in script.js

---
 www/html/pampa/js/script.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/www/html/pampa/js/script.js b/www/html/pampa/js/script.js
index 9647380..6c6bb2b 100644
--- a/www/html/pampa/js/script.js
+++ b/www/html/pampa/js/script.js
@@ -14,7 +14,7 @@ function resetForm(formSelector, baseForm) {
 }
 resetForm("#reset", "form.php")
 resetForm("#reset-craft-h", "craft-homology.php")
-resetForm("reset-craft-f", "craft-fillin.php")
+resetForm("#reset-craft-f", "craft-fillin.php")
 
 function formSubmit(formSelector, paramIndex) {
   $(document).on("submit", formSelector, function() {
-- 
GitLab


From dd3f9fc9c6b093dcc462976c3a88ef2538c97075 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 10 Apr 2025 10:40:25 +0200
Subject: [PATCH 45/59] adaptation for tools.py

---
 www/cgi-bin/pampa/pampa-craft.py   | 53 +++++++++++-------------------
 www/cgi-bin/pampa/pampa.py         | 28 +++++-----------
 www/cgi-bin/pampa/render_result.py |  4 +--
 3 files changed, 31 insertions(+), 54 deletions(-)

diff --git a/www/cgi-bin/pampa/pampa-craft.py b/www/cgi-bin/pampa/pampa-craft.py
index e7068fe..15c0c6f 100644
--- a/www/cgi-bin/pampa/pampa-craft.py
+++ b/www/cgi-bin/pampa/pampa-craft.py
@@ -9,7 +9,7 @@ import sys
 import common
 import shutil
 import zipfile
-from tools import FormTools
+from tools import *
 
 # Fonction permettant de vérifier les données soumises dans le formulaire, retour dans un dictionnaire (req)
 # Fonction qui sera modifiée pour le traitement de vos données
@@ -26,46 +26,31 @@ def extract_request(form):
     log = open('log.txt', 'w')
     log.write(f'Starting log file\n')  # log
 
-    # Sauvegarde :
-    #       - fichiers FASTA sequences dans le dossier result/run_id/sequences/
-    #       - fichier TSV peptides_table dans result/run_id/
-    #       - fichier txt limit dans result/run_id
-    # Nom du fichier output fixé a partir du run_id
-
-    # dossier de résultats utilisateur
     resdir = common.RESULT_DIR + form['run_id'].value
     log.write(f"Result directory : {resdir}\n")
 
     req['files_uploaded'] = []
-    if "job_name" in form:
-        job_name = form["job_name"].value.strip().replace(' ', '_')
-        if job_name == "":
-            log.write("Job name: None\n")
-        elif len(job_name) <= 30:
-            req['job_name'] = job_name
-            log.write(f"Job name: {job_name}\n")
-        else:
-            error_messages.append(f"The job name cannot exceed 30 characters. Current size: {len(job_name)}.")
-    else:
-        log.write("Job name: None\n")
+
+    extract_job(form, log, req, error_messages)
 
     if 'method' in form:
         req["method"] = form['method'].value
         method = form['method'].value
     else:
+        method = None
         error_messages.append(f"Method is required")
 
     if 'craft-example' in form:
         if method == "homology":
-            FormTools.extract_files_from_list([common.HOMOLOGY_PEPTIDE], resdir)
+            extract_files_from_list([common.HOMOLOGY_PEPTIDE], resdir)
             req["peptides_path"] = resdir + '/' + "homology_peptides.tsv"
         elif method == "fillin":
-            FormTools.extract_files_from_list([common.FILLIN_PEPTIDE], resdir)
+            extract_files_from_list([common.FILLIN_PEPTIDE], resdir)
             req["peptides_path"] = resdir + '/' + "fillin_peptides.tsv"
     elif 'peptides_file' in form and form['peptides_file'].filename != "":
         f = form['peptides_file'].filename
         fn = os.path.basename(f)
-        uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['peptides_file']], resdir, extensions_list=[".tsv", ".csv"],
+        uploaded, not_uploaded = extract_files_from_fileitems([form['peptides_file']], resdir, extensions_list=[".tsv", ".csv"],
                                                               unzip=False)
         req["peptides_path"] = resdir + '/' + fn
         req['files_uploaded'].extend(uploaded)
@@ -82,9 +67,9 @@ def extract_request(form):
     if 'craft-example' in form:
         req['sequences_dir'] = abs_sequences_dir
         if method == "homology":
-            FormTools.extract_files_from_list([common.HOMOLOGY_SEQUENCES], abs_sequences_dir)
+            extract_files_from_list([common.HOMOLOGY_SEQUENCES], abs_sequences_dir)
         elif method == "fillin":
-            FormTools.extract_files_from_list([common.FILLIN_SEQUENCES], abs_sequences_dir)
+            extract_files_from_list([common.FILLIN_SEQUENCES], abs_sequences_dir)
     elif 'sequences_files' in form:
         req['sequences_dir'] = abs_sequences_dir
         fileitems = form['sequences_files']
@@ -94,7 +79,7 @@ def extract_request(form):
             if method != "fillin" and method != "deamidation" and method != "selection":
                 error_messages.append("At least one FASTA file is required.")
         else:
-            uploaded, not_uploaded = FormTools.extract_files_from_fileitems(fileitems, abs_sequences_dir,
+            uploaded, not_uploaded = extract_files_from_fileitems(fileitems, abs_sequences_dir,
                                                                   extensions_list=[".txt", ".fasta"], unzip=True)
             req['files_uploaded'].extend(uploaded)
             for fn in uploaded:
@@ -113,7 +98,7 @@ def extract_request(form):
     if 'limit_file' in form and form['limit_file'].filename != "":
         f = form['limit_file'].filename
         fn = os.path.basename(f)
-        uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['limit_file']], resdir, extensions_list=[".txt"],
+        uploaded, not_uploaded = extract_files_from_fileitems([form['limit_file']], resdir, extensions_list=[".txt"],
                                                               unzip=False)
         req["limit_path"] = resdir + '/' + fn
         req['files_uploaded'].extend(uploaded)
@@ -164,17 +149,18 @@ def extract_request(form):
         if 'spectra_example_1' in form:
             req['spectra_dir'] = abs_spectra_dir
             # Cas où les spectres à analyser sont les spectres de l'exemple 1 (copie de fichiers interne plutôt qu'upload des spectres utilisateur)
-            FormTools.extract_files_from_list([common.SPECTRA_EXAMPLE_1], abs_spectra_dir)
+            extract_files_from_list([common.SPECTRA_EXAMPLE_1], abs_spectra_dir)
         elif 'spectra_files' in form:
-            req['spectra_dir'] = abs_spectra_dir
             # spectres utilisateurs
             fileitems = form['spectra_files']
             if not isinstance(fileitems, list):
                 fileitems = [fileitems]
             if fileitems[0].filename == "":
-                error_messages.append("At least one spectrum is required.")
+                if method != "allpeptides":
+                    error_messages.append("At least one spectrum is required.")
             else:
-                uploaded, not_uploaded = FormTools.extract_files_from_fileitems(fileitems, abs_spectra_dir, extensions_list=[".csv", ".mgd", ".mzml"], unzip=True)
+                req['spectra_dir'] = abs_spectra_dir
+                uploaded, not_uploaded = extract_files_from_fileitems(fileitems, abs_spectra_dir, extensions_list=[".csv", ".mgd", ".mzml"], unzip=True)
                 req['files_uploaded'].extend(uploaded)
                 for fn in uploaded:
                     log.write(f"file {fn}\n")
@@ -184,7 +170,8 @@ def extract_request(form):
                     else:
                         error_messages.append(f"{fn} : Spectra must be in CSV, MGD or mzML format or in a ZIP archive.")
         else:
-            error_messages.append("At least one spectrum is required.")
+            if method != "allpeptides":
+                error_messages.append("At least one spectrum is required.")
 
     if method == "fillin":
         if "taxonomy_selection" in form:
@@ -193,7 +180,7 @@ def extract_request(form):
                 # sur les données (table/sequences) utilisateurs, on utilise la taxonomy_all.tsv qui est plus générale.
                 f = common.TAXONOMY_ALL_FILE
                 fn = os.path.basename(f)
-                FormTools.extract_files_from_list([f], resdir)
+                extract_files_from_list([f], resdir)
                 req["taxo_file"] = resdir + '/' + fn  # for the command
                 req["taxo_source"] = "default_all"
                 log.write(f"file {fn}\n")  # log
@@ -205,7 +192,7 @@ def extract_request(form):
                 if 'taxo_file' in form and not isinstance(form['taxo_file'], list) and form['taxo_file'].filename != "":
                     f = form['taxo_file'].filename
                     fn = os.path.basename(f)
-                    uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['taxo_file']], resdir,
+                    uploaded, not_uploaded = extract_files_from_fileitems([form['taxo_file']], resdir,
                                                                                     extensions_list=[".tsv"], unzip=False)
                     req["taxo_file"] = resdir + '/' + fn  # for the command
                     req["taxo_source"] = "user"
diff --git a/www/cgi-bin/pampa/pampa.py b/www/cgi-bin/pampa/pampa.py
index 9c0f320..87b1472 100644
--- a/www/cgi-bin/pampa/pampa.py
+++ b/www/cgi-bin/pampa/pampa.py
@@ -47,17 +47,7 @@ def extract_request(form):
     #req['pampa_version'] = form['pampa_version']
 
     # Job name parsing (optionnal)
-    if "job_name" in form:
-        job_name = form["job_name"].value.strip().replace(' ', '_')
-        if job_name == "":
-            log.write("Job name: None\n")  # log
-        elif len(job_name) <= 30:
-            req['job_name'] = job_name
-            log.write(f"Job name: {job_name}\n")  # log
-        else:
-            error_messages.append(f"The job name cannot exceed 30 characters. Current size: {len(job_name)}.")
-    else:
-        log.write("Job name: None\n")
+    extract_job(form, log, req, error_messages)
     
     # Spectra files parsing
     rel_spectra_dir = "spectra"
@@ -67,7 +57,7 @@ def extract_request(form):
 
     if 'spectra_example_1' in form:
         # Cas où les spectres à analyser sont les spectres de l'exemple 1 (copie de fichiers interne plutôt qu'upload des spectres utilisateur)
-        FormTools.extract_files_from_list([common.SPECTRA_EXAMPLE_1], abs_spectra_dir)
+        extract_files_from_list([common.SPECTRA_EXAMPLE_1], abs_spectra_dir)
     elif 'spectra_files' in form:
         # spectres utilisateurs 
         fileitems = form['spectra_files']
@@ -76,7 +66,7 @@ def extract_request(form):
         if fileitems[0].filename == "":
             error_messages.append("At least one spectrum is required.")
         else:
-            uploaded, not_uploaded = FormTools.extract_files_from_fileitems(fileitems, abs_spectra_dir, extensions_list=[".csv", ".mgd", ".mzml"], unzip=True)
+            uploaded, not_uploaded = extract_files_from_fileitems(fileitems, abs_spectra_dir, extensions_list=[".csv", ".mgd", ".mzml"], unzip=True)
             req['files_uploaded'].extend(uploaded)
             for fn in uploaded:
                 log.write(f"file {fn}\n")
@@ -164,7 +154,7 @@ def extract_request(form):
         # Récupération des jeux de données internes pour l'analyse
         req["peptides_files"] = []  # for the command
         f_list = [peptides_path_dict[group] for group in tax_groups]
-        FormTools.extract_files_from_list(f_list, resdir)
+        extract_files_from_list(f_list, resdir)
         for f in f_list:
             fn = os.path.basename(f)
             req["peptides_files"].append(resdir + '/' + fn)  # for the command
@@ -174,7 +164,7 @@ def extract_request(form):
         # Taxonomy selection : fichier interne
         f = common.TAXONOMY_REDUCED_FILE
         fn = os.path.basename(f)
-        FormTools.extract_files_from_list([f], resdir)
+        extract_files_from_list([f], resdir)
         req["taxo_file"] = resdir + '/' + fn  # for the command
         log.write(f"file {fn}\n")  # log
         req["taxo_source"] = "default_reduced"
@@ -202,7 +192,7 @@ def extract_request(form):
             elif peptides_fileitems[0].filename != "":  # Peptide table(s) provided
                 # Table de peptides
                 req['peptides_files'] = []  # for the command
-                uploaded, not_uploaded = FormTools.extract_files_from_fileitems(peptides_fileitems, resdir, extensions_list=[".tsv"], unzip=True)
+                uploaded, not_uploaded = extract_files_from_fileitems(peptides_fileitems, resdir, extensions_list=[".tsv"], unzip=True)
                 req['files_uploaded'].extend(uploaded)  # for the render_result
                 req["reference"] = "peptides"  # for the command
                 log.write(f"Reference type: {req['reference']}\n")  # log
@@ -219,7 +209,7 @@ def extract_request(form):
                 rel_sequences_dir = "sequences"
                 abs_sequences_dir = os.path.join(resdir,rel_sequences_dir)  # destination directory
                 req['sequences_dir'] = abs_sequences_dir  # for the command
-                uploaded, not_uploaded = FormTools.extract_files_from_fileitems(sequences_fileitems, abs_sequences_dir, extensions_list=[".fasta", ".fna", ".fa"], unzip=True)
+                uploaded, not_uploaded = extract_files_from_fileitems(sequences_fileitems, abs_sequences_dir, extensions_list=[".fasta", ".fna", ".fa"], unzip=True)
                 req['files_uploaded'].extend(uploaded)  # for the render_result 
                 req["reference"] = "sequences"  # for the command
                 log.write(f"Reference type: {req['reference']}\n")  # log
@@ -240,7 +230,7 @@ def extract_request(form):
                 # sur les données (table/sequences) utilisateurs, on utilise la taxonomy_all.tsv qui est plus générale.
                 f = common.TAXONOMY_ALL_FILE
                 fn = os.path.basename(f)
-                FormTools.extract_files_from_list([f], resdir)
+                extract_files_from_list([f], resdir)
                 req["taxo_file"] = resdir + '/' + fn  # for the command
                 req["taxo_source"] = "default_all"
                 log.write(f"file {fn}\n")  # log
@@ -252,7 +242,7 @@ def extract_request(form):
                 if 'taxo_file' in form and not isinstance(form['taxo_file'], list) and form['taxo_file'].filename != "":
                     f = form['taxo_file'].filename
                     fn = os.path.basename(f)
-                    uploaded, not_uploaded = FormTools.extract_files_from_fileitems([form['taxo_file']], resdir, extensions_list=[".tsv"], unzip=False)
+                    uploaded, not_uploaded = extract_files_from_fileitems([form['taxo_file']], resdir, extensions_list=[".tsv"], unzip=False)
                     req["taxo_file"] = resdir + '/' + fn  # for the command
                     req["taxo_source"] = "user"
                     req['files_uploaded'].extend(uploaded)  # for the render_result
diff --git a/www/cgi-bin/pampa/render_result.py b/www/cgi-bin/pampa/render_result.py
index fbb1722..b421b55 100644
--- a/www/cgi-bin/pampa/render_result.py
+++ b/www/cgi-bin/pampa/render_result.py
@@ -57,7 +57,7 @@ def results_output(run_id, job_name=None):
                 dwl_name = parts[0] + "_" + job_name + parts[1]
             else: 
                 dwl_name = fn
-            html += li(output_files_title[fn] + ResultTools.href_dl(fn, dwl_name))
+            html += li(output_files_title[fn] + href_dl(fn, dwl_name))
     html += "</ul>"
 
     return  html
@@ -230,7 +230,7 @@ def main():
         data_dir = params['data_dir']
 
     # production du fichier ZIP contenant les résultats
-    ResultTools.zip_results(["out_"+run_id+".tsv", "detail_out_"+run_id+".tsv", "report_out_"+run_id+".txt", "table_out_"+run_id+".tsv"], run_id, job_name=job_name)
+    zip_results(["out_"+run_id+".tsv", "detail_out_"+run_id+".tsv", "report_out_"+run_id+".txt", "table_out_"+run_id+".tsv"], run_id, job_name=job_name)
 
     # test de l'usage d'une taxonomie par PAMPA
     if taxo_source == "none":
-- 
GitLab


From 3d686708297c0664e237beb7ae9dfe1d0affea09 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 10 Apr 2025 10:40:52 +0200
Subject: [PATCH 46/59] typo

---
 www/html/pampa/js/pampa.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/www/html/pampa/js/pampa.js b/www/html/pampa/js/pampa.js
index 196ee7b..f382d70 100644
--- a/www/html/pampa/js/pampa.js
+++ b/www/html/pampa/js/pampa.js
@@ -112,7 +112,7 @@ $(document).on("click", "#example-homology", function() {
 
 $(document).on("click", "#example-fillin", function() {
     $('#main').load(soft+'craft-fillin.php #center', function(){
-        $("input[name='job_name']").val("example_homology");
+        $("input[name='job_name']").val("example_fillin");
         $("#peptide_input").hide();
         $("#peptide_input input").attr("required", false);
         $("#peptide_example").show();
-- 
GitLab


From fc688bfee1504699b8652cad7d8f0ed15024b490 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 10 Apr 2025 16:11:07 +0200
Subject: [PATCH 47/59] add allpeptides example

---
 www/cgi-bin/pampa/common.py                        |  2 ++
 www/html/pampa/craft-allpeptides.php               |  6 +++---
 .../pampa/data_pampa/allpeptides_sequences.fasta   |  4 ++++
 www/html/pampa/js/pampa.js                         | 14 +++++++++++++-
 www/html/pampa/js/script.js                        |  1 +
 5 files changed, 23 insertions(+), 4 deletions(-)
 create mode 100644 www/html/pampa/data_pampa/allpeptides_sequences.fasta

diff --git a/www/cgi-bin/pampa/common.py b/www/cgi-bin/pampa/common.py
index f9f9c5a..013cf08 100644
--- a/www/cgi-bin/pampa/common.py
+++ b/www/cgi-bin/pampa/common.py
@@ -31,6 +31,8 @@ HOMOLOGY_SEQUENCES = DATA_PAMPA_DIR + "homology_sequences.fasta"
 FILLIN_PEPTIDE = DATA_PAMPA_DIR + "fillin_peptides.tsv"
 FILLIN_SEQUENCES = DATA_PAMPA_DIR + "fillin_sequences.fasta"
 
+ALLPEPTIDES_SEQUENCES = DATA_PAMPA_DIR + "allpeptides_sequences.fasta"
+
 
 
 #Renvoie le pwd pour le dossier tmp/
diff --git a/www/html/pampa/craft-allpeptides.php b/www/html/pampa/craft-allpeptides.php
index 40adadb..0223418 100644
--- a/www/html/pampa/craft-allpeptides.php
+++ b/www/html/pampa/craft-allpeptides.php
@@ -85,7 +85,7 @@
         <tr id="spectra_example" style="display: none;">
           <td>
             <input class="file_input" id="back_to_normal" type="button" value="Parcourir..."/>
-            pampa_example_1.zip
+            craft_example.zip
             <input type="checkbox" style="display: none;" name="spectra_example_1"/>
           </td>
         </tr>
@@ -128,11 +128,11 @@
     </div>
 
     <div class="center">
-      <input type="button" id="example-fillin" name="example-fillin" value="Example">
+      <input type="button" id="example-allpeptides" name="example-allpeptides" value="Example">
     </div>
 
     <div class="center">
-      <input type="submit" id="reset-craft-f" name="reset" value="Reset" />
+      <input type="submit" id="reset-craft-a" name="reset" value="Reset" />
       <input type="submit" id="run" name="button" value="Run" />
       <input type="hidden" name="command" value="request" />
     </div>
diff --git a/www/html/pampa/data_pampa/allpeptides_sequences.fasta b/www/html/pampa/data_pampa/allpeptides_sequences.fasta
new file mode 100644
index 0000000..87de780
--- /dev/null
+++ b/www/html/pampa/data_pampa/allpeptides_sequences.fasta
@@ -0,0 +1,4 @@
+>XP_027830506.1 OS=Ovis aries OX=9940 GN=COL1A1 A 
+MFSFVDLRLLLLLAATALLTHGQEEGQEEGQEEDIPPVTCVQNGLRYHDRDVWKPVPCQICVCDNGNVLCDDVICDELKDCPNAKVPTDECCPVCPEGQESTTDQETTGVEGPKGDTGPRGPRGPAGPPGRDGIPGQPGLPGPPGPPGPPGPPGLGGNFAPQLSYGYDEKSTGISVPGPMGPSGPRGLPGPPGAPGPQGFQGPPGEPGEPGASGPMGPRGPPGPPGKNGDDGEAGKPGRPGERGPPGPQGARGLPGTAGLPGMKGHRGFSGLDGAKGDAGPAGPKGEPGSPGENGAPGQMGPRGLPGERGRPGAPGPAGARGNDGATGAAGPPGPTGPAGPPGFPGAVGAKGEAGPQGPRGSEGPQGVRGEPGPPGPAGAAGPAGNPGADGQPGAKGANGAPGIAGAPGFPGARGPSGPQGPSGPPGPKGNSGEPGAPGSKGDTGAKGEPGPTGIQGPPGPAGEEGKRGARGEPGPAGLPGPPGERGGPGSRGFPGSDGVAGPKGPAGERGAPGPAGPKGSPGEAGRPGEAGLPGAKGLTGSPGSPGPDGKTGPPGPAGQDGRPGPPGPPGARGQAGVMGFPGPKGAAGEPGKAGERGVPGPPGAVGPAGKDGEAGAQGPPGPAGPAGERGEQGPAGSPGFQGLPGPAGPPGEAGKPGEQGVPGDLGAPGPSGARGERGFPGERGVQGPPGPAGPRGANGAPGNDGAKGDAGAPGAPGSQGAPGLQGMPGERGAAGLPGPKGDRGDAGPKGADGAPGKDGVRGLTGPIGPPGPAGAPGDKGETGPSGPAGPTGARGAPGDRGEPGPPGPAGFAGPPGADGQPGAKGEPGDAGAKGDAGPPGPAGPAGPPGPIGNVGAPGPKGARGSAGPPGATGFPGAAGRVGPPGPSGNAGPPGPPGPAGKEGSKGPRGETGPAGRAGEVGPPGPPGPAGEKGAPGADGPAGAPGTPGPQGIAGQRGVVGLPGQRGERGFPGLPGPSGEPGKQGPSGASGERGPPGPMGPPGLAGPPGESGREGAPGAEGSPGRDGAPGAKGDRGETGPAGPPGAPGAPGAPGPVGPAGKSGDRGETGPAGPAGPIGPVGARGPAGPQGPRGDKGETGEQGDRGIKGHRGFSGLQGPPGPPGSPGEQGPSGASGPAGPRGPPGSAGTPGKDGLNGLPGPIGPPGPRGRTGDAGPAGPPGPPGPPGPPGPPSGGYDLSFLPQPPQEKAHDGGRYYRADDANVVRDRDLEVDTTLKSLSQQIENIRSPEGSRKNPARTCRDLKMCHPDWKSGEYWIDPNQGCNLDAIKVFCNMETGETCVYPTQPSVPQKNWYISKNPKDKRHVWYGESMTGGFQFEYGGQGSDPADVAIQLTFLRLMSTEASQNITYHCKNSVAYMDQQTGSLKKALLLQGSNEIEIRAEGNSRFTYSVTYDGCTSHTGAWGKTVIEYKTTKTSRLPIIDVAPLDVGAPDQEFGFDIGSVCFL
+>XP_004007775.1 OS=Ovis aries OX=9940 GN=COL1A2 A 
+MLSFVDTRTLLLLAVTSCLATCQSLQEATARKGPSGDRGPRGERGPPGPPGRDGDDGIPGPPGPPGPPGPPGLGGNFAAQFDGKGGGPGPMGLMGPRGPPGASGAPGPQGFQGPPGEPGEPGQTGPAGARGPPGPPGKAGEDGHPGKPGRPGERGVVGPQGARGFPGTPGLPGFKGIRGHNGLDGLKGQPGAPGVKGEPGAPGENGTPGQTGARGLPGERGRVGAPGPAGARGSDGSVGPVGPAGPIGSAGPPGFPGAPGPKGELGPVGNPGPAGPAGPRGEVGLPGLSGPVGPPGNPGANGLPGAKGAAGLPGVAGAPGLPGPRGIPGPVGAAGATGARGLVGEPGPAGSKGESGNKGEPGAVGQPGPPGPSGEEGKRGSTGEIGPAGPPGPPGLRGNPGSRGLPGADGRAGVMGPAGSRGATGPAGVRGPNGDSGRPGEPGLMGPRGFPGSPGNIGPAGKEGPAGLPGIDGRPGPIGPAGARGEPGNIGFPGPKGPTGDPGKAGEKGHAGLAGPRGAPGPDGNNGAQGPPGLQGVQGGKGEQGPAGPPGFQGLPGPAGTAGEAGKPGERGIPGEFGLPGPAGARGERGPPGESGAAGPTGPIGSRGPSGPPGPDGNKGEPGVVGAPGTAGPSGPSGLPGERGAAGIPGGKGEKGETGLRGDVGSPGRDGARGAPGAVGAPGPAGANGDRGEAGPAGPAGPAGPRGSPGERGEVGPAGPNGFAGPAGAAGQPGAKGERGTKGPKGENGPVGPTGPVGAAGPSGPNGPPGPAGSRGDGGPPGATGFPGAAGRTGPPGPAGISGPPGPPGPAGKEGLRGPRGDQGPVGRTGEPGAAGPPGFVGEKGPSGEPGTAGPPGTPGPQGLLGAPGFLGLPGSRGERGLPGVAGSVGEPGPLGIAGPPGARGPPGNVGNPGVNGAPGEAGRDGNPGNDGPPGRDGQPGHKGERGYPGNAGPVGAAGAPGPQGPVGPTGKHGSRGEPGPVGAVGPAGAVGPRGPSGPQGIRGDKGEPGDKGPRGLPGLKGHNGLQGLPGLAGHHGDQGAPGAVGPAGPRGPAGPTGPAGKDGRTGQPGAVGPAGIRGSQGSQGPAGPPGPPGPPGPPGPSGGGYDFGFDGDFYRADQPRSPASLRPKDYEVDATLKSLNNQIETLLTPEGSRKNPARTCRDLRLSHPEWSSGYYWIDPNQGCTMDAIKVYCDFSTGETCIRAQPEDIPVKNWYRNSKAKKHVWVGETINGGTQFEYNVEGVTTKEMATQLAFMRLLANHASQNITYHCKNSIAYMDEETGNLKKAVILQGSNDVELVAEGNSRFTYTVLVDGCSKKTNEWKKTIIEYKTNKPSRLPILDIAPLDIGGADQEIRLNIGPVCFK
diff --git a/www/html/pampa/js/pampa.js b/www/html/pampa/js/pampa.js
index f382d70..db91f35 100644
--- a/www/html/pampa/js/pampa.js
+++ b/www/html/pampa/js/pampa.js
@@ -120,11 +120,23 @@ $(document).on("click", "#example-fillin", function() {
         $("#sequences_input").hide();
         $("#sequences_input input").attr("required", false);
         $("#sequences_example").show();
-        $("input[name='craft-example']").attr("checked", true);
         $("input[name='error']").val("0.1");
     });
 });
 
+$(document).on("click", "#example-allpeptides", function() {
+    $('#main').load(soft+'craft-allpeptides.php #center', function(){
+        $("input[name='job_name']").val("example_allpeptides");
+        $("#peptide_input").hide();
+        $("#peptide_input input").attr("required", false);
+        $("#peptide_example").show();
+        $("input[name='craft-example']").attr("checked", true);
+        $("#sequences_input").hide();
+        $("#sequences_input input").attr("required", false);
+        $("#sequences_example").show();
+    });
+});
+
 
 $(document).on("click", "#back_to_normal", function() {
     if ($("input[name='job_name']").val() == "example_1") {
diff --git a/www/html/pampa/js/script.js b/www/html/pampa/js/script.js
index 6c6bb2b..3b8916f 100644
--- a/www/html/pampa/js/script.js
+++ b/www/html/pampa/js/script.js
@@ -15,6 +15,7 @@ function resetForm(formSelector, baseForm) {
 resetForm("#reset", "form.php")
 resetForm("#reset-craft-h", "craft-homology.php")
 resetForm("#reset-craft-f", "craft-fillin.php")
+resetForm("#reset-craft-a", "craft-allpeptides.php")
 
 function formSubmit(formSelector, paramIndex) {
   $(document).on("submit", formSelector, function() {
-- 
GitLab


From bb5fb6a325ce415e2a3be156929feb41b9e64f08 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 10 Apr 2025 16:15:36 +0200
Subject: [PATCH 48/59] add allpeptides example

---
 www/cgi-bin/pampa/pampa-craft.py | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/www/cgi-bin/pampa/pampa-craft.py b/www/cgi-bin/pampa/pampa-craft.py
index 15c0c6f..c22a595 100644
--- a/www/cgi-bin/pampa/pampa-craft.py
+++ b/www/cgi-bin/pampa/pampa-craft.py
@@ -70,6 +70,8 @@ def extract_request(form):
             extract_files_from_list([common.HOMOLOGY_SEQUENCES], abs_sequences_dir)
         elif method == "fillin":
             extract_files_from_list([common.FILLIN_SEQUENCES], abs_sequences_dir)
+        elif method == "allpeptides":
+            extract_files_from_list([common.ALLPEPTIDES_SEQUENCES], abs_sequences_dir)
     elif 'sequences_files' in form:
         req['sequences_dir'] = abs_sequences_dir
         fileitems = form['sequences_files']
@@ -146,11 +148,7 @@ def extract_request(form):
             error_messages.append("A mass error selection is necessary for the analysis.")
 
     if method == "selection" or method == "allpeptides":
-        if 'spectra_example_1' in form:
-            req['spectra_dir'] = abs_spectra_dir
-            # Cas où les spectres à analyser sont les spectres de l'exemple 1 (copie de fichiers interne plutôt qu'upload des spectres utilisateur)
-            extract_files_from_list([common.SPECTRA_EXAMPLE_1], abs_spectra_dir)
-        elif 'spectra_files' in form:
+        if 'spectra_files' in form:
             # spectres utilisateurs
             fileitems = form['spectra_files']
             if not isinstance(fileitems, list):
-- 
GitLab


From ab7f599a564e81dac715716347ef578922f566e5 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 10 Apr 2025 16:26:17 +0200
Subject: [PATCH 49/59] update for tools.py and new table_maker.py

---
 www/cgi-bin/pampa/craft_render_result.py | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/www/cgi-bin/pampa/craft_render_result.py b/www/cgi-bin/pampa/craft_render_result.py
index f7a835b..180f9ce 100644
--- a/www/cgi-bin/pampa/craft_render_result.py
+++ b/www/cgi-bin/pampa/craft_render_result.py
@@ -39,7 +39,7 @@ def results_output(run_id, job_name=None):
                 dwl_name = parts[0] + "_" + job_name + parts[1]
             else: 
                 dwl_name = fn
-            html += li(output_files_title[fn] + ResultTools.href_dl(fn, dwl_name))
+            html += li(output_files_title[fn] + href_dl(fn, dwl_name))
     html += "</ul>"
 
     return  html
@@ -85,13 +85,16 @@ def write_main_page(run_id, taxo_used, or_data, method, job_name=None):
 
     table_dir = common.RESULT_DIR + run_id + "/table.php"
 
-    table_maker.table_maker(taxo_used, or_data, table_dir, method)
+    #table_maker.table_maker(taxo_used, or_data, table_dir, method)
+    command = f"/usr/bin/python {common.PAMPA_DIR}table_maker.py -g {or_data} -r {taxo_used} -o {table_dir} -m {method}"
+    os.chdir(format(os.getcwd()) + "/pampa/")
+    os.system(command)
 
     html+= '''        
     <h3>Peptide Table</h3>
     <p>editable preview of the results file. <b>Use the bouton bellow to download edited result.</b></p>'''
 
-    html += '<iframe src="table.php" style="border: none; width: 100%; height: 575px;"></iframe>'
+    html += '<iframe id="results" src="table.php" style="border: none; width: 100%; height: 575px;"></iframe>'
     # Download section
     html += '''
     <h3>Download</h3>''' + results_output(run_id, job_name=job_name) + '<br>'
@@ -149,7 +152,7 @@ def main():
         fasta_file = params["sequences_files"]
     method = params["method"]
 
-    ResultTools.zip_results(["out_"+run_id+".tsv", "report_out_"+run_id+".txt", ], run_id, job_name=job_name)
+    zip_results(["out_"+run_id+".tsv", "report_out_"+run_id+".txt", ], run_id, job_name=job_name)
 
     write_main_page(run_id, common.RESULT_DIR+ run_id + "/out_"+run_id+".tsv", table_files, method, job_name=job_name)
 
-- 
GitLab


From a0630850a01752a9f552f438c0af0cdc6f5cc34a Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 10 Apr 2025 16:27:03 +0200
Subject: [PATCH 50/59] new table_maker.py (works with pampa function)

---
 table_maker.py | 212 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 212 insertions(+)
 create mode 100644 table_maker.py

diff --git a/table_maker.py b/table_maker.py
new file mode 100644
index 0000000..69fdd88
--- /dev/null
+++ b/table_maker.py
@@ -0,0 +1,212 @@
+import argparse
+import json
+import csv
+
+
+from src import markers
+from src import peptide_table as pt
+from src import config
+from src import utils
+from functools import cmp_to_key, partial
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("-g", dest="or_data")
+    parser.add_argument("-r", dest="out_data")
+    parser.add_argument("-o", dest="output")
+    parser.add_argument("-m", dest="method")
+    args = parser.parse_args()
+
+    table_maker(args.out_data, args.or_data, args.output, args.method)
+
+def compare_file(or_markers, out_markers):
+    group = {}
+
+    for or_marker in or_markers:
+        group[or_marker] = []
+        or_data = or_marker.field
+        keys = or_data.keys()
+        for out_marker in out_markers:
+            out_data = out_marker.field
+
+            results = {}
+            for key in keys:
+
+                if or_data[key] == out_data[key]:
+                    results[key] = True
+                elif key == "Mass":
+                    if round(float(or_data[key]),1) == round(float(out_data[key]),1):
+                        results[key] = True
+                    else:
+                        results[key] = False
+                else:
+                    results[key] = False
+            if all(results.values()):
+                group[or_marker].append(out_marker)
+
+    return group
+
+
+def generate_json_result(original_path, output_path, outfile_name):
+    JSON_file = open(outfile_name, "w")
+    or_markers, _ = pt.parse_peptide_tables(original_path, None, None, False)
+    out_markers, list_of_headers = pt.parse_peptide_tables(output_path, None, None, False)
+
+    #pt.build_peptide_table_from_set_of_markers(out_markers, "test.tsv")
+
+    group = compare_file(or_markers, out_markers)
+
+    if not list_of_headers:
+        headers={pt.restitute_field(key) for m in out_markers for key in m.field}
+        list_of_headers=(config.sort_headers(headers))
+    else:
+        list_of_headers=list(map(pt.restitute_field, list_of_headers))
+
+    list_of_headers.append('__modifiedCells')
+
+    reverse_map = {}
+    for orig_marker, out_list in group.items():
+        for out_m in out_list:
+            reverse_map[out_m] = orig_marker
+
+    dicts = []
+    for out_m in out_markers:
+        orig_m = reverse_map.get(out_m, None)
+        status = {}
+        for field, value in out_m.field.items():
+            if orig_m is None:
+                status[field] = 'new'
+            else:
+                if field in orig_m.field:
+                    orig_val = orig_m.field[field]
+                    if field == "Mass":
+                        if orig_val != value:
+                            status[field] = 'modified'
+                        else:
+                            status[field] = 'original'
+                    else:
+                        status[field] = 'modified' if value != orig_val else 'original'
+                else:
+                    status[field] = 'added'
+        out_m.field['__modifiedCells'] = dict(zip(list_of_headers, status.values()))
+        dicts.append(out_m)
+
+    set_of_codes=[m.code() for m in dicts]
+    list_of_codes=config.sort_headers(set_of_codes)
+    dicts.sort(key=cmp_to_key(partial(pt.marker_order, list_of_codes=list_of_codes)))
+
+    result = []
+    for m in dicts:
+        result.append(dict(zip(list_of_headers, m.field.values())))
+    json.dump(result, JSON_file)
+    JSON_file.close()
+
+    return result, list_of_headers
+
+
+
+def read_tsv(file_path):
+    data = []
+    with open(file_path, "r", newline="") as file:
+        reader = csv.DictReader(file, delimiter="\t")
+        headers = reader.fieldnames
+        for row in reader:
+            data.append(row)
+    return data, headers
+
+
+def table_maker(peptide_out, peptide_or, output_file, method):
+    if method == "fillin":
+        dicts, header = generate_json_result([peptide_or], [peptide_out], "test.json")
+        header.pop()
+    else :
+        dicts, header = read_tsv(peptide_out)
+    html = '''
+        <div id="results" style="height: 500px; margin-bottom: 20px;" class="ag-theme-alpine"></div>
+        <div style="margin-top: 10px;">
+            <button onclick="exportData()">Download Results (.TSV)</button>
+            <button onclick="deleteSelectedRows()" style="margin-left: 10px;">Delete Selected Rows</button>
+        </div>
+
+        <script src="https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js"></script>
+        <script type="text/javascript">
+            function initGrid() {
+                let gridApi;
+                const outData = ''' + json.dumps(dicts) + ''';
+                const columnDefs = ''' + json.dumps([{"field": h, "headerName": h, "editable": True} for h in header]) + ''';
+
+                const gridOptions = {
+                    theme: agGrid.themeBalham,
+                    rowData: outData,
+                    columnDefs: columnDefs.map(col => ({
+                        ...col,
+                        cellStyle: params => {
+                            const status = params.data.__modifiedCells?.[col.field];
+                            if (status === 'modified') return { backgroundColor: '#ffff00'};
+                            if (status === 'added') return { backgroundColor: '#00ff00'};
+                            if (status === 'new') return { backgroundColor: '#0000ff'};
+                            return null;
+                        }
+                    })),
+                    pagination: true,
+                    paginationPageSize: 20,
+                    defaultColDef: {
+                        sortable: true,
+                        filter: true,
+                        resizable: true,
+                        editable: true
+                    },
+                    autoSizeStrategy: {
+                        type: "fitCellContents",
+                    },
+                    rowSelection: 'multiple',
+                    onCellValueChanged: function(event) {
+                        console.log('Cell modified:', event);
+                    },
+                };
+
+                function autoSizeAll(skipHeader) {
+                    const allColumnIds = [];
+                    gridApi.getColumns().forEach((column) => {
+                        allColumnIds.push(column.getId());
+                    });
+                    gridApi.autoSizeColumns(allColumnIds, skipHeader);
+                    gridApi.sizeColumnsToFit();
+                }
+
+                gridApi = agGrid.createGrid(document.querySelector("#results"), gridOptions);
+                window.gridApi = gridApi;
+
+                autoSizeAll(true);
+
+                window.exportData = function() {
+                    gridApi.exportDataAsCsv({
+                        fileName: "result.tsv",
+                        columnSeparator: '\\t',
+                        suppressQuotes: true,
+                        skipBOM: true
+                    });
+                };
+
+                window.deleteSelectedRows = function() {
+                    const selectedNodes = gridApi.getSelectedNodes();
+                    const selectedData = selectedNodes.map(node => node.data);
+                    gridApi.applyTransaction({ remove: selectedData });
+                };
+            }
+
+            if(document.readyState === 'loading') {
+                document.addEventListener('DOMContentLoaded', initGrid);
+            } else {
+                initGrid();
+            }
+        </script>
+        <div id="status" style="margin-top: 10px; color: #666;"></div>
+    '''
+
+    with open(output_file, 'w') as f:
+        f.write(html)
+
+
+if __name__ == '__main__':
+    main()
\ No newline at end of file
-- 
GitLab


From 4a8f62d307a42dc6098444359699dd5b451432e3 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Thu, 10 Apr 2025 16:28:22 +0200
Subject: [PATCH 51/59] copy table_maker.py in pampa

---
 Dockerfile | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Dockerfile b/Dockerfile
index 88b3ddb..aa593a5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -84,6 +84,7 @@ RUN chown -R www-data:www-data /var/www
 # ajustement pour version web : pas de print sur sdtout mais envoie d'une réponse CGI de type content-type/html
 COPY ./pampa /var/www/cgi-bin/pampa/pampa
 COPY ./main_taxonomy_filtering.py /var/www/cgi-bin/pampa/pampa/
+COPY ./table_maker.py /var/www/cgi-bin/pampa/pampa
 
 # Ajustage des droits
 RUN chmod +x /var/www/cgi-bin/pampa/*
-- 
GitLab


From 6320ac0ab271ee73710ec9d4447a9fff48674e23 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 14 Apr 2025 15:19:36 +0200
Subject: [PATCH 52/59] Uptade craft_render_result.py for new table_maker.py

---
 www/cgi-bin/pampa/craft_render_result.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/www/cgi-bin/pampa/craft_render_result.py b/www/cgi-bin/pampa/craft_render_result.py
index 180f9ce..9047011 100644
--- a/www/cgi-bin/pampa/craft_render_result.py
+++ b/www/cgi-bin/pampa/craft_render_result.py
@@ -5,7 +5,7 @@ from html_utils import *
 import common
 import zipfile
 import operator
-import table_maker
+#import table_maker
 from tools import *
 
 """
@@ -45,7 +45,7 @@ def results_output(run_id, job_name=None):
     return  html
 
 def write_main_page(run_id, taxo_used, or_data, method, job_name=None):
-    """Write a HTML page. The result page shown first when the PAMPA analysis is done."""
+    """Write a- HTML page. The result page shown first when the PAMPA analysis is done."""
     html = ""
 
     # Insert HTML header and page head
-- 
GitLab


From 2d84edb084970c4fc6d8831f48d09877a30ccf10 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 14 Apr 2025 15:20:04 +0200
Subject: [PATCH 53/59] Delete old table_maker.py

---
 www/cgi-bin/pampa/table_maker.py | 126 -------------------------------
 1 file changed, 126 deletions(-)
 delete mode 100644 www/cgi-bin/pampa/table_maker.py

diff --git a/www/cgi-bin/pampa/table_maker.py b/www/cgi-bin/pampa/table_maker.py
deleted file mode 100644
index 9a0152f..0000000
--- a/www/cgi-bin/pampa/table_maker.py
+++ /dev/null
@@ -1,126 +0,0 @@
-import json
-import csv
-
-
-def read_tsv(file_path):
-    data = []
-    with open(file_path, "r", newline="") as file:
-        reader = csv.DictReader(file, delimiter="\t")
-        for row in reader:
-            data.append(row)
-    return data
-
-
-def table_maker(peptide_out, peptide_or, output_file, method):
-    out_data = read_tsv(peptide_out)
-    or_data = read_tsv(peptide_or)
-
-    if out_data:
-        header = list(out_data[0].keys())
-    else:
-        header = []
-    html = '''
-        <div id="myGrid" style="height: 500px; margin-bottom: 20px;" class="ag-theme-alpine"></div>
-        <div style="margin-top: 10px;">
-            <button onclick="exportData()">Download Results (.TSV)</button>
-        </div>
-
-        <script src="https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js"></script>
-        <script type="text/javascript">
-function initGrid() {
-                let gridApi;
-                const outData = ''' + json.dumps(out_data) + ''';
-                const orData = ''' + json.dumps(or_data) + ''';
-                const method = "''' + method + '''";
-
-                if (method == "fillin") {
-                    outData.forEach((procRow, index) => {
-                        const origRow = orData[index];
-                        procRow.__modifiedCells = {};
-
-                        Object.keys(procRow).forEach(key => {
-                            if (!origRow) {
-                                procRow.__modifiedCells[key] = 'new';
-                            } else {
-                                const origValue = origRow[key];
-                                const newValue = procRow[key];
-
-                                if (origValue == null || origValue === "" || origValue === undefined) {
-                                    procRow.__modifiedCells[key] = 'added';
-                                } else if (origValue !== newValue) {
-                                    procRow.__modifiedCells[key] = 'modified';
-                                } else {
-                                    procRow.__modifiedCells[key] = 'original';
-                                }
-                            }
-                        });
-                    })
-                }    
-
-                const columnDefs = ''' + json.dumps([{"field": h, "headerName": h, "editable": True} for h in header]) + ''';
-
-                const gridOptions = {
-                    theme: agGrid.themeBalham,
-                    rowData: outData,
-                    columnDefs: columnDefs.map(col => ({
-                        ...col,
-                        cellStyle: params => {
-                            const status = params.data.__modifiedCells?.[col.field];
-                            if (status === 'modified') return { backgroundColor: '#ffff00'};
-                            if (status === 'added') return { backgroundColor: '#00ff00'};
-                            if (status === 'new') return { backgroundColor: '#0000ff'};
-
-                            return null;
-                        }
-                    })),
-                    pagination: true,
-                    paginationPageSize: 20,
-                    defaultColDef: {
-                        sortable: true,
-                        filter: true,
-                        resizable: true,
-                        editable: true
-                    },
-                    autoSizeStrategy: {
-                        type: "fitCellContents",
-                    },
-                    onCellValueChanged: function(event) {
-                        console.log('Cell modified:', event);
-                    },
-                };
-
-                function autoSizeAll(skipHeader) {
-                    const allColumnIds = [];
-                    gridApi.getColumns().forEach((column) => {
-                    allColumnIds.push(column.getId());
-                    });
-
-                    gridApi.autoSizeColumns(allColumnIds, skipHeader);
-
-                    gridApi.sizeColumnsToFit();
-                }
-
-                gridApi = agGrid.createGrid(document.querySelector("#myGrid"), gridOptions);
-                window.gridApi = gridApi;
-
-                autoSizeAll(true);
-
-                window.exportData = function() {
-                    gridApi.exportDataAsCsv({
-                        fileName: "result.tsv",
-                        columnSeparator: '\\t',
-                        suppressQuotes: true,
-                    });
-                };
-            }
-            if(document.readyState === 'loading') {
-                document.addEventListener('DOMContentLoaded', initGrid);
-            } else {
-                initGrid();
-            }
-        </script>
-        <div id="status" style="margin-top: 10px; color: #666;"></div>
-        '''
-
-    with open(output_file, 'w') as f:
-        f.write(html)
\ No newline at end of file
-- 
GitLab


From 3069ed6bc532523fc855c08c4a214356c986d316 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 14 Apr 2025 15:20:39 +0200
Subject: [PATCH 54/59] Add more function to avoid code duplication

---
 www/cgi-bin/pampa/tools.py | 69 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/www/cgi-bin/pampa/tools.py b/www/cgi-bin/pampa/tools.py
index c3fff08..e52ea96 100644
--- a/www/cgi-bin/pampa/tools.py
+++ b/www/cgi-bin/pampa/tools.py
@@ -121,6 +121,75 @@ def extract_job(form, log, req, error_messages):
     else:
         log.write("Job name: None\n")
 
+def extract_error_margin(form, log, req, error_messages):
+    if "error_margin_selection" in form:
+        error_margin_selection = form['error_margin_selection'].value
+        error_margin = 0
+        if error_margin_selection == "MALDI_TOF":
+            # valeur par défaut
+            error_margin = 50
+        elif error_margin_selection == "MALDI_FTICR":
+            # valeur par défaut
+            error_margin = 5
+        elif error_margin_selection == "ppm":
+            if "error_margin_ppm" in form:
+                error_margin = int(form['error_margin_ppm'].value)
+                if error_margin < 1 or error_margin > 1000:
+                    error_messages.append(
+                        f"The mass error in ppm must be within the range 1-1000. Input value: {error_margin}.")
+            else:
+                error_messages.append(f"With the custom value in ppm option, a mass error value is required.")
+        elif error_margin_selection == "daltons":
+            if "error_margin_daltons" in form:
+                error_margin = float(form['error_margin_daltons'].value)
+                if not (error_margin > 0 and error_margin < 1):
+                    error_messages.append(
+                        f"The mass error in daltons must be within the range 0.002-0.998. Input value: {error_margin}.")
+            else:
+                error_messages.append(f"With the custom value in Daltons option, a mass error value is required.")
+        else:
+            error_messages.append(
+                f"Internal error. An unknow value have been entered in the 'error_margin_selection' input: {error_margin_selection}")
+        log.write(f"Error margin: {error_margin}\n")
+        req['error_margin'] = error_margin
+    else:
+        error_messages.append("A mass error selection is necessary for the analysis.")
+
+def extract_taxonomy_selection(form, log, req, error_messages, resdir):
+    if "taxonomy_selection" in form:
+        taxonomy_selection = form["taxonomy_selection"].value
+        if taxonomy_selection == "default":
+            # sur les données (table/sequences) utilisateurs, on utilise la taxonomy_all.tsv qui est plus générale.
+            f = common.TAXONOMY_ALL_FILE
+            fn = os.path.basename(f)
+            extract_files_from_list([f], resdir)
+            req["taxo_file"] = resdir + '/' + fn  # for the command
+            req["taxo_source"] = "default_all"
+            log.write(f"file {fn}\n")  # log
+        elif taxonomy_selection == "no":
+            # pas d'information taxonomique -> pas de fichier taxo.tsv
+            log.write("No taxonomy\n")
+        elif taxonomy_selection == "custom":
+            # upload de la taxonomie utilisateur si possible, sinon erreur
+            if 'taxo_file' in form and not isinstance(form['taxo_file'], list) and form['taxo_file'].filename != "":
+                f = form['taxo_file'].filename
+                fn = os.path.basename(f)
+                uploaded, not_uploaded = extract_files_from_fileitems([form['taxo_file']], resdir,
+                                                                      extensions_list=[".tsv"], unzip=False)
+                req["taxo_file"] = resdir + '/' + fn  # for the command
+                req["taxo_source"] = "user"
+                req['files_uploaded'].extend(uploaded)  # for the render_result
+                for fn in uploaded:
+                    log.write(f"file {fn}\n")
+                for fn in not_uploaded:
+                    error_messages.append(f"{fn} : The taxonomy file must be in TSV format.")
+            else:
+                error_messages.append("If you choose to upload your own taxonomy, a taxonomy file is required.")
+        else:
+            error_messages.append(
+                f"Internal error. An unexpected value has been submitted in the 'taxonomy_selection' input : {taxonomy_selection}")
+    else:
+        error_messages.append("A choice is required in the 'Taxonomy' section.")
 
 # -------------------------------------
 # Result
-- 
GitLab


From 5bcc59bbfd3ed84ad48aed6321c64570efa49337 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 14 Apr 2025 15:22:02 +0200
Subject: [PATCH 55/59] Work with new tools.py

---
 www/cgi-bin/pampa/pampa-craft.py | 77 ++++----------------------------
 www/cgi-bin/pampa/pampa.py       | 63 +-------------------------
 2 files changed, 10 insertions(+), 130 deletions(-)

diff --git a/www/cgi-bin/pampa/pampa-craft.py b/www/cgi-bin/pampa/pampa-craft.py
index c22a595..e480358 100644
--- a/www/cgi-bin/pampa/pampa-craft.py
+++ b/www/cgi-bin/pampa/pampa-craft.py
@@ -113,39 +113,11 @@ def extract_request(form):
         if 'error' in form:
             error_value = form['error'].value.strip()
             if error_value:
-                req['error'] = error_value
+                req['error_margin'] = error_value
             else:
-                req['error'] = "0"
+                req['error_margin'] = "0.1"
     if method == "allpeptides" or method == "selection":
-        if "error_margin_selection" in form:
-            error_margin_selection = form['error_margin_selection'].value
-            error_margin = 0
-            if error_margin_selection == "MALDI_TOF":
-                # valeur par défaut
-                error_margin = 50
-            elif error_margin_selection == "MALDI_FTICR":
-                # valeur par défaut
-                error_margin = 5
-            elif error_margin_selection == "ppm":
-                if "error_margin_ppm" in form:
-                    error_margin = int(form['error_margin_ppm'].value)
-                    if error_margin < 1 or error_margin > 1000:
-                        error_messages.append(f"The mass error in ppm must be within the range 1-1000. Input value: {error_margin}.")
-                else:
-                    error_messages.append(f"With the custom value in ppm option, a mass error value is required.")
-            elif error_margin_selection == "daltons":
-                if "error_margin_daltons" in form:
-                    error_margin = float(form['error_margin_daltons'].value)
-                    if not (error_margin > 0 and error_margin < 1):
-                        error_messages.append(f"The mass error in daltons must be within the range 0.002-0.998. Input value: {error_margin}.")
-                else:
-                    error_messages.append(f"With the custom value in Daltons option, a mass error value is required.")
-            else:
-                error_messages.append(f"Internal error. An unknow value have been entered in the 'error_margin_selection' input: {error_margin_selection}")
-            log.write(f"Error margin: {error_margin}\n")
-            req['error'] = str(error_margin)
-        else:
-            error_messages.append("A mass error selection is necessary for the analysis.")
+        extract_error_margin(form, log, req, error_messages)
 
     if method == "selection" or method == "allpeptides":
         if 'spectra_files' in form:
@@ -171,41 +143,8 @@ def extract_request(form):
             if method != "allpeptides":
                 error_messages.append("At least one spectrum is required.")
 
-    if method == "fillin":
-        if "taxonomy_selection" in form:
-            taxonomy_selection = form["taxonomy_selection"].value
-            if taxonomy_selection == "default":
-                # sur les données (table/sequences) utilisateurs, on utilise la taxonomy_all.tsv qui est plus générale.
-                f = common.TAXONOMY_ALL_FILE
-                fn = os.path.basename(f)
-                extract_files_from_list([f], resdir)
-                req["taxo_file"] = resdir + '/' + fn  # for the command
-                req["taxo_source"] = "default_all"
-                log.write(f"file {fn}\n")  # log
-            elif taxonomy_selection == "no":
-                # pas d'information taxonomique -> pas de fichier taxo.tsv
-                log.write("No taxonomy\n")
-            elif taxonomy_selection == "custom":
-                # upload de la taxonomie utilisateur si possible, sinon erreur
-                if 'taxo_file' in form and not isinstance(form['taxo_file'], list) and form['taxo_file'].filename != "":
-                    f = form['taxo_file'].filename
-                    fn = os.path.basename(f)
-                    uploaded, not_uploaded = extract_files_from_fileitems([form['taxo_file']], resdir,
-                                                                                    extensions_list=[".tsv"], unzip=False)
-                    req["taxo_file"] = resdir + '/' + fn  # for the command
-                    req["taxo_source"] = "user"
-                    req['files_uploaded'].extend(uploaded)  # for the render_result
-                    for fn in uploaded:
-                        log.write(f"file {fn}\n")
-                    for fn in not_uploaded:
-                        error_messages.append(f"{fn} : The taxonomy file must be in TSV format.")
-                else:
-                    error_messages.append("If you choose to upload your own taxonomy, a taxonomy file is required.")
-            else:
-                error_messages.append(
-                    f"Internal error. An unexpected value has been submitted in the 'taxonomy_selection' input : {taxonomy_selection}")
-        else:
-            error_messages.append("A choice is required in the 'Taxonomy' section.")
+    if method == "fillin" or method == "homology":
+        extract_taxonomy_selection(form, log, req, error_messages, resdir)
 
 
     log.write("Recap downloads :\n")
@@ -240,9 +179,9 @@ def launch_software(run_id, req):
     if 'limit_path' in req:
         limit_arg = req["limit_path"]
         command += " -l " + limit_arg
-    if 'error' in req:
-        error_arg = req["error"]
-        command += " -e " + error_arg
+    if 'error_margin' in req:
+        error_arg = req["error_margin"]
+        command += " -e " + str(error_arg)
     if 'taxo_file' in req:
         taxo_file_arg = req["taxo_file"]
         command += " -t " + taxo_file_arg
diff --git a/www/cgi-bin/pampa/pampa.py b/www/cgi-bin/pampa/pampa.py
index 87b1472..ab819d7 100644
--- a/www/cgi-bin/pampa/pampa.py
+++ b/www/cgi-bin/pampa/pampa.py
@@ -79,35 +79,7 @@ def extract_request(form):
         error_messages.append("At least one spectrum is required.")
     
     # Mass error parsing
-    if "error_margin_selection" in form:
-        error_margin_selection = form['error_margin_selection'].value
-        error_margin = 0
-        if error_margin_selection == "MALDI_TOF":
-            # valeur par défaut
-            error_margin = 50
-        elif error_margin_selection == "MALDI_FTICR":
-            # valeur par défaut
-            error_margin = 5
-        elif error_margin_selection == "ppm":
-            if "error_margin_ppm" in form:
-                error_margin = int(form['error_margin_ppm'].value)
-                if error_margin < 1 or error_margin > 1000:
-                    error_messages.append(f"The mass error in ppm must be within the range 1-1000. Input value: {error_margin}.")
-            else:
-                error_messages.append(f"With the custom value in ppm option, a mass error value is required.")
-        elif error_margin_selection == "daltons":
-            if "error_margin_daltons" in form:
-                error_margin = float(form['error_margin_daltons'].value)
-                if not (error_margin > 0 and error_margin < 1):
-                    error_messages.append(f"The mass error in daltons must be within the range 0.002-0.998. Input value: {error_margin}.")
-            else:
-                error_messages.append(f"With the custom value in Daltons option, a mass error value is required.")
-        else:
-            error_messages.append(f"Internal error. An unknow value have been entered in the 'error_margin_selection' input: {error_margin_selection}")
-        log.write(f"Error margin: {error_margin}\n")
-        req['error_margin'] = error_margin
-    else:
-        error_messages.append("A mass error selection is necessary for the analysis.")
+    extract_error_margin(form, log, req, error_messages)
     
     # 'Choice of the markers and organisms' parsing (default data or upload custom data)
     if "reference_source" not in form :
@@ -224,38 +196,7 @@ def extract_request(form):
             error_messages.append("Internal error. Issue in the formular structure ('peptides_files' or 'sequences_files' input).")
         
         # Taxonomy parsing
-        if "taxonomy_selection" in form:
-            taxonomy_selection = form["taxonomy_selection"].value
-            if taxonomy_selection == "default":
-                # sur les données (table/sequences) utilisateurs, on utilise la taxonomy_all.tsv qui est plus générale.
-                f = common.TAXONOMY_ALL_FILE
-                fn = os.path.basename(f)
-                extract_files_from_list([f], resdir)
-                req["taxo_file"] = resdir + '/' + fn  # for the command
-                req["taxo_source"] = "default_all"
-                log.write(f"file {fn}\n")  # log
-            elif taxonomy_selection == "no":
-                # pas d'information taxonomique -> pas de fichier taxo.tsv
-                log.write("No taxonomy\n")
-            elif taxonomy_selection == "custom":
-                # upload de la taxonomie utilisateur si possible, sinon erreur
-                if 'taxo_file' in form and not isinstance(form['taxo_file'], list) and form['taxo_file'].filename != "":
-                    f = form['taxo_file'].filename
-                    fn = os.path.basename(f)
-                    uploaded, not_uploaded = extract_files_from_fileitems([form['taxo_file']], resdir, extensions_list=[".tsv"], unzip=False)
-                    req["taxo_file"] = resdir + '/' + fn  # for the command
-                    req["taxo_source"] = "user"
-                    req['files_uploaded'].extend(uploaded)  # for the render_result
-                    for fn in uploaded:
-                        log.write(f"file {fn}\n")
-                    for fn in not_uploaded:
-                        error_messages.append(f"{fn} : The taxonomy file must be in TSV format.")
-                else:
-                    error_messages.append("If you choose to upload your own taxonomy, a taxonomy file is required.")
-            else:
-                error_messages.append(f"Internal error. An unexpected value has been submitted in the 'taxonomy_selection' input : {taxonomy_selection}")
-        else:
-            error_messages.append("A choice is required in the 'Taxonomy' section.")
+        extract_taxonomy_selection(form, log, req, error_messages, resdir)
 
     # option -n nombre et/ou -a (solution suboptimales)
     if 'nearoptimal_selection' in form:
-- 
GitLab


From 2fe5bda4e60f9ec6958606346dd060b5d036f166 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Mon, 14 Apr 2025 15:22:52 +0200
Subject: [PATCH 56/59] Add taxonomy field to follow pampa's homology update

---
 www/html/pampa/craft-homology.php | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/www/html/pampa/craft-homology.php b/www/html/pampa/craft-homology.php
index cf395da..85f6ca2 100644
--- a/www/html/pampa/craft-homology.php
+++ b/www/html/pampa/craft-homology.php
@@ -93,6 +93,33 @@
         </table>
     </div>
 
+    <div class="formulaire">
+      <h2>Taxonomy</h2>
+      <table class="vide">
+        <tr>
+          <td colspan="2">
+            <input type="radio" name="taxonomy_selection" value="default"/>
+            Use NCBI taxonomy
+          </td>
+        </tr>
+        <tr>
+          <td colspan="2">
+            <input type="radio" name="taxonomy_selection" value="no" checked/>
+            Provide no taxonomy
+          </td>
+        </tr>
+        <tr>
+          <td>
+            <input type="radio" name="taxonomy_selection" value="custom"/>
+            Upload your own taxonomy
+          </td>
+          <td>
+            <input type="file" name="taxo_file" value="file" accept=".tsv"/>
+          </td>
+        </tr>
+      </table>
+    </div>
+
     <div class="center">
       <input type="button" id="example-homology" name="example-homology" value="Example">
     </div>
-- 
GitLab


From 310978f14955c3acc6fc1bdd8b274ba3db479660 Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 16 Apr 2025 10:18:29 +0200
Subject: [PATCH 57/59] Remove useless buttons

---
 www/html/pampa/craft-deamidation.php | 5 -----
 www/html/pampa/craft-selection.php   | 5 -----
 2 files changed, 10 deletions(-)

diff --git a/www/html/pampa/craft-deamidation.php b/www/html/pampa/craft-deamidation.php
index a430742..1239505 100644
--- a/www/html/pampa/craft-deamidation.php
+++ b/www/html/pampa/craft-deamidation.php
@@ -73,11 +73,6 @@
     </div>
 
     <div class="center">
-      <input type="button" id="example-fillin" name="example-fillin" value="Example">
-    </div>
-
-    <div class="center">
-      <input type="submit" id="reset-craft-f" name="reset" value="Reset" />
       <input type="submit" id="run" name="button" value="Run" />
       <input type="hidden" name="command" value="request" />
     </div>
diff --git a/www/html/pampa/craft-selection.php b/www/html/pampa/craft-selection.php
index d5abb61..fba5f8e 100644
--- a/www/html/pampa/craft-selection.php
+++ b/www/html/pampa/craft-selection.php
@@ -115,11 +115,6 @@
     </div>
 
     <div class="center">
-      <input type="button" id="example-fillin" name="example-fillin" value="Example">
-    </div>
-
-    <div class="center">
-      <input type="submit" id="reset-craft-f" name="reset" value="Reset" />
       <input type="submit" id="run" name="button" value="Run" />
       <input type="hidden" name="command" value="request" />
     </div>
-- 
GitLab


From b203be8a24d3f433894c3ea6c4c5325832fa738e Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Wed, 16 Apr 2025 10:20:19 +0200
Subject: [PATCH 58/59] Add checks to prevent crashes

---
 www/cgi-bin/pampa/tools.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/www/cgi-bin/pampa/tools.py b/www/cgi-bin/pampa/tools.py
index e52ea96..08e93aa 100644
--- a/www/cgi-bin/pampa/tools.py
+++ b/www/cgi-bin/pampa/tools.py
@@ -132,7 +132,7 @@ def extract_error_margin(form, log, req, error_messages):
             # valeur par défaut
             error_margin = 5
         elif error_margin_selection == "ppm":
-            if "error_margin_ppm" in form:
+            if "error_margin_ppm" in form and form["error_margin_ppm"].value != '':
                 error_margin = int(form['error_margin_ppm'].value)
                 if error_margin < 1 or error_margin > 1000:
                     error_messages.append(
@@ -140,7 +140,7 @@ def extract_error_margin(form, log, req, error_messages):
             else:
                 error_messages.append(f"With the custom value in ppm option, a mass error value is required.")
         elif error_margin_selection == "daltons":
-            if "error_margin_daltons" in form:
+            if "error_margin_daltons" in form and form["error_margin_daltons"].value != '':
                 error_margin = float(form['error_margin_daltons'].value)
                 if not (error_margin > 0 and error_margin < 1):
                     error_messages.append(
-- 
GitLab


From 103fd96e22df0dcebb6210d22ecac69ce786e1ee Mon Sep 17 00:00:00 2001
From: Valentin hottlet <valentin.hottlet.etu@univ-lille.fr>
Date: Fri, 18 Apr 2025 10:38:39 +0200
Subject: [PATCH 59/59] Show error message if result table creation fails (No
 result table)

---
 www/cgi-bin/pampa/craft_render_result.py | 77 +++++++++++++-----------
 1 file changed, 42 insertions(+), 35 deletions(-)

diff --git a/www/cgi-bin/pampa/craft_render_result.py b/www/cgi-bin/pampa/craft_render_result.py
index 9047011..78a2353 100644
--- a/www/cgi-bin/pampa/craft_render_result.py
+++ b/www/cgi-bin/pampa/craft_render_result.py
@@ -63,48 +63,55 @@ def write_main_page(run_id, taxo_used, or_data, method, job_name=None):
     html += open(f"{common.HTML_PATH}/menu_central.txt", "r").read()
     html += '''</div></div><div id="main"><div id="center">'''
 
-    html += f'<h2>Results for job {run_id}{f" ({job_name})" if job_name else ""}</h2>'
-
-    # Show warnings if they exist
-    warning_file = common.RESULT_DIR + run_id + "/warning.log"
-    if os.path.exists(warning_file):
-        with open(warning_file) as fileOut:
-            content = fileOut.read().strip()
-            if content:
-                html += '''
-                <div id="warnings_hidden" style="display: block;">
-                    <p>Warning(s) were raised during the execution. <a id="show_warnings">View report</a></p>
-                </div>
-                <div id="warnings_shown" style="display: none;">
-                    <p>Warning(s) were raised during the execution. <a id="hide_warnings">Hide</a></p>
-                    <table class="vide">'''
-                for line in content.split('\n'):
-                    if line:
-                        html += f'<tr><td>{line}</td></tr>'
-                html += '''</table></div><br>'''
-
     table_dir = common.RESULT_DIR + run_id + "/table.php"
 
-    #table_maker.table_maker(taxo_used, or_data, table_dir, method)
     command = f"/usr/bin/python {common.PAMPA_DIR}table_maker.py -g {or_data} -r {taxo_used} -o {table_dir} -m {method}"
     os.chdir(format(os.getcwd()) + "/pampa/")
     os.system(command)
 
-    html+= '''        
-    <h3>Peptide Table</h3>
-    <p>editable preview of the results file. <b>Use the bouton bellow to download edited result.</b></p>'''
+    if os.path.exists(table_dir):
+        html += f'<h2>Results for job {run_id}{f" ({job_name})" if job_name else ""}</h2>'
+
+        # Show warnings if they exist
+        warning_file = common.RESULT_DIR + run_id + "/warning.log"
+        if os.path.exists(warning_file):
+            with open(warning_file) as fileOut:
+                content = fileOut.read().strip()
+                if content:
+                    html += '''
+                    <div id="warnings_hidden" style="display: block;">
+                        <p>Warning(s) were raised during the execution. <a id="show_warnings">View report</a></p>
+                    </div>
+                    <div id="warnings_shown" style="display: none;">
+                        <p>Warning(s) were raised during the execution. <a id="hide_warnings">Hide</a></p>
+                        <table class="vide">'''
+                    for line in content.split('\n'):
+                        if line:
+                            html += f'<tr><td>{line}</td></tr>'
+                    html += '''</table></div><br>'''
+
+        html+= '''        
+        <h3>Peptide Table</h3>
+        <p>editable preview of the results file. <b>Use the bouton bellow to download edited result.</b></p>'''
+
+        html += '<iframe id="results" src="table.php" style="border: none; width: 100%; height: 575px;"></iframe>'
+        # Download section
+        html += '''
+        <h3>Download</h3>''' + results_output(run_id, job_name=job_name) + '<br>'
+
+        # Retrieve results
+        html += f'''
+        <h3>Retrieve results with an ID</h3>
+        <p>Your result remains available for at least one month.</p>
+        <p>To access it, you can either save the URL or <a id="copy" data-copy="{run_id}">copy the ID</a> {run_id}.</p>
+        <br>'''
 
-    html += '<iframe id="results" src="table.php" style="border: none; width: 100%; height: 575px;"></iframe>'
-    # Download section
-    html += '''
-    <h3>Download</h3>''' + results_output(run_id, job_name=job_name) + '<br>'
-
-    # Retrieve results
-    html += f'''
-    <h3>Retrieve results with an ID</h3>
-    <p>Your result remains available for at least one month.</p>
-    <p>To access it, you can either save the URL or <a id="copy" data-copy="{run_id}">copy the ID</a> {run_id}.</p>
-    <br>'''
+    else:
+        html += f'''<div id="error">
+        <h2>something went wrong in job ({run_id}).</h2>
+        <p>The problem might be related to one of the input files.</p>
+        <p>For more details, Please contact <a href="mailto:areski.flissi@univ-lille.fr?Subject=[Pampa ERROR - JOB: {run_id}]">PAMPA</a>.</p>
+        </div>'''
 
     # Insert page footer
     html += '</div></div>'
-- 
GitLab