<?xml version="1.0"?>
<!DOCTYPE html 
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

  <head>
    <title>TOMUSS Technical Reference</title>
  </head>

  <style>
object.svg {
   overflow: hidden ;
   background-color: #EEE;
}

pre {
   background-color: #EEE;
   border: 1px solid black;
   padding: 0.2em ;
}
table td { vertical-align: top ; }

div.toc * { font-weight: normal ; margin: 0em;}
div.toc h2 { font-size: 80% ; margin-left: 2em ; }
div.toc h3 { font-size: 80% ; margin-left: 4em ; }

table { border-spacing: 0px ; }
table.plugin td { white-space: nowrap ; }
table.plugin svg { width: 16px ; height: 110px}
table.plugin svg text {
   font-size: 12px;
   font-family:sans ;
   font-weight: normal;
   text-anchor: start ;
   fill: black;
}
table.plugin svg:hover text {
   stroke-width: 3px;
   stroke: #0F0;
   stroke-opacity: 0.4;
 }


@media print {

body { margin-left: 5mm ; margin-right: 5mm ; }
}
  </style>

  <body>
    <h1>TOMUSS Technical Reference</h1>

    <p>
Table of content:
</p>
    <div class="toc">
    <h2><a href="#organisation">TOMUSS file organisation</a></h2>
    <h3><a href="#organisation1">The over top file organisation</a></h3>
    <h3><a href="#organisation2">Running server file organisation</a></h3>
    <h2><a href="#plugins">TOMUSS plugins</a></h2>
    <h3><a href="#url_template">URL template</a></h3>
    <h3><a href="#plugin_attributes">Plugin attributes</a></h3>
    <h3><a href="#plugin_list">Plugin documentation</a></h3>
    <h2><a href="#protocols">TOMUSS protocols</a></h2>
    <h3><a href="#protocol_tomuss">TOMUSS client/server protocol</a></h3>
    <h3><a href="#protocol_suivi">TOMUSS suivi client/server protocol</a></h3>
    <h2><a href="#storage">TOMUSS storage</a></h2>
    <h3><a href="#table_storage">Table storage</a></h3>
    <h3><a href="#abj_storage">ABJ + DA storage</a></h3>
    <h3><a href="#tt_storage">TT storage</a></h3>
    <h3><a href="#referent_storage">Referent storage</a></h3>
    <h2><a href="#objects">TOMUSS objects server side</a></h2>
<h3><a href="#Cell">Cell</a></h3>
<h3><a href="#CellEmpty">CellEmpty</a></h3>
<h3><a href="#CellValue">CellValue</a></h3>
<h3><a href="#CellVirtual">CellVirtual</a></h3>
<h3><a href="#Column">Column</a></h3>
<h3><a href="#Columns">Columns</a></h3>
<h3><a href="#Line">Line</a></h3>
<h3><a href="#Lines">Lines</a></h3>
<h3><a href="#UE">UE</a></h3>
    <h3><a href="#column_types">Types of columns</a></h3>
    <h2><a href="#column_attributes">Attributes of table columns</a></h2>
    <h2><a href="#table_attributes">Attributes of tables</a></h2>
    <h2><a href="#serverthreads">TOMUSS server side threads</a></h2>
    <h2><a href="#client">TOMUSS client side</a></h2>
    <h3><a href="#clienthome">The home page</a></h3>
    <h3><a href="#clientabj">The ABJ/DA editing page</a></h3>
    <h3><a href="#clienttable">The table editing page</a></h3>
    <h3><a href="#clientobjects">Client side objects</a></h3>
    <h3><a href="#virtualtables">Virtual tables</a></h3>
    <h2><a href="#administration">TOMUSS Administration</a></h2>
    <h3><a href="#semester">Semesters</a></h3>
    <h3><a href="#templates">Table templates</a></h3>
    <h3><a href="#authentication">Authentication process</a></h3>
    <h3><a href="#apache">Apache/NGINX configuration</a></h3>
    <h3><a href="#configuration">TOMUSS configuration</a></h3>
    <h3><a href="#regtests">TOMUSS Regressions Tests</a></h3>
    <h3><a href="#startstop">TOMUSS starting and stopping</a></h3>
    <h3><a href="#install">TOMUSS install on production server</a></h3>
    <h3><a href="#manage">TOMUSS managing</a></h3>
    <h2><a href="#pitfall">TOMUSS Pitfall</a></h2>
    <h2><a href="#local">Functions to redefine in order to customize TOMUSS</a></h2>
    </div>

    <object data="global.svg" class="svg"></object>

    <h2><a name="organisation">TOMUSS file organisation</a></h2>

    <p>The first part describes the hierarchy created by the default
       installation script and
       the second part describes the hierarchy of the running servers.</p>
    <p>You may skip the first part is you only want to test TOMUSS</p>

    <h3><a name="organisation1">The over top file organisation</a></h3>
    <p>
      The hierarchy created by the installation tree insure that
      a running server can be stopped and an older version of the
      server can be quickly started without data loss.
    </p>
    <p>
      The installation script needs 2 directories, one for the server
      and one for the backup server.
      They store exactly the same things (except temporary and log files).
      The database stay synchronized in the two directories.
    </p>
    <ul>
      <li> <b>SERVEUR_NOTES</b> The last server version.</li>
      <li> <b>LOGS</b> The log files.</li>
      <li> <b>TMP</b> The temporary files.</li>
      <li> <b>Trash</b> The deleted tables.</li>
      <li> <b>TOMUSS_DB</b> The tables.</li>
      <li> <b>TOMUSS.YYYY-MM-DD_HH:MM:SS</b>
           Older versions of the server</li>
    </ul>

    <h3><a name="organisation2">Running server file organisation</a></h3>

    <p>If the installation script is used, the following directories
      are symbolic links to the version independant directories.</p>
    <ul>
<li><b>DBtest</b> (<b>DB</b> in production): Where the data are stored.</li>
<li><b>LOGS</b>: The logs and statistics of the servers</li>
<li><b>TMP</b>: Data files generated by program, but costly to create.</li>
<li><b>Trash</b>: Where are moved the deleted tables</li>
</ul>
    <p>The top level directories</p>
    <ul>
<li><b>DBtest</b> (<b>DB</b> in production): Where the data are stored.<br/>
<b>BACKUP_DBtest</b> (<b>BACKUP_DB</b> in production): Where the backup is stored.
<ul>
<li> One directory per year named <b>Y9999</b> </li>
<li> One directory named <b>Y0</b> for year independent data.</li>
<li> <b>CLOSED/???/---UE CODE---</b> stores the semester for which the UE seems to be close</li>
<li> <b>RSSLOGINS/???/---RSS_KEY---</b>
 contains the login associated to the key.</li>
<li><b>LOGINS/???/login</b>: Directory with user data.
<ul>
<li> <b>favstu</b> Favorite student list</li>
<li> <b>master_of</b> The user is the master of theses UEs</li>
<li> <b>pages</b> Favorites UE</li>
<li> <b>charte_9999_semester</b> Exists if the charte was accepted</li>
<li> <b>rsskey</b> Key used to read RSS stream without login</li>
<li> <b>old_referent</b> The last referent of the student</li>
</ul>


</li>
</ul>
</li>


<li><b>FILES</b>: The static files sended by the server.
They are automaticaly reloaded when they are modified.</li>
<li><b>SCRIPTS</b>: Some scripts mostly local and obsolete except 'install' and 'crontab_run.py'</li>
<li><b>TEMPLATES</b>: Defines classes of table.</li>
<li><b>REGTEST_CLIENT</b>: An aborted experiment to do regressions tests on any web browser (the browsers do not react deterministicaly).</li>
<li><b>REGTEST_SERVER</b>: A working set of regression tests on the server.</li>
<li><b>DOCUMENTATION</b>: You are seeing it :-)
<ul>
	  <li><b>Makefile</b> The default goal create documentation.</li>
	  <li><b><a href="ChangeLog">ChangeLog</a></b> All the modifications done.</li>
	  <li><b><a href="TODO">TODO</a></b> The things that need to be done</li>
	  <li><b>Welcome.orig.xml</b> The documentation source.</li>
	</ul>
</li>
<li><b>PLUGINS</b>: They define how the servers react to an URL</li>
<li><b>COLUMN_TYPES</b>: They define define the possible column types.</li>
<li><b>ATTRIBUTES</b>: They define the possible column and table attributes.</li>
<li><b>LOCAL</b>: All the customization code and data must be inside.
<ul>
<li> <b>__init__.py</b>: Your real configuration</li>
<li> <b>LOCAL.TEMPLATES</b>: Your local TEMPLATES directory</li>
</ul>
</li>
    </ul>

    <p>The core of the code</p>
<ul>
<li><b>Makefile</b>: The goals are
<ul>
	  <li><b>start</b>: Starts all the servers.</li>
	  <li><b>stop</b>: Stops all the servers.</li>
	  <li><b>stopsuivi</b>: Stops TOMUSS 'suivi' server.</li>
	  <li><b>stoptomuss</b>: Stops TOMUSS server.</li>
	  <li><b>install</b>: Put development version in production.</li>
	  <li><b>stat</b>: Display some sources stats</li>
	  <li><b>clean</b>: Delete garbages.</li>
	  <li><b>diff</b>: Difference between development and production source version.</li>
	  <li><b>regtest</b>: Run server regression tests.</li>
	  <li><b>regtest1</b>: Run server regression tests only once</li>
	  <li><b>release</b> : Package a release.</li>
	</ul>

 </li>
<li><b>configuration.py</b>: configurations variables. <em>Read carefully, but do not modify this file.</em></li>
<li><b>tomuss.py</b>: Server managing the tables. Possible parameters are:
<ul>
	  <li><b>regtest</b>: server in regtest mode, ticket are in the form '=invited.teacher' or '=super.user'</li>
	  <li><b>xxx_toutes_les_ues</b>: Extract the UE list from database and store them in a Python module. Then the process stops.</li>
	  <li><b>all_ues.js</b>: Translate the UE list in a JavaScript module. Then the process stops.</li>	  
	</ul>

</li>
<li><b>suivi.py</b>: Server allowing the 'suivi'.</li>
<li><b>abj.py</b>: All the code needed to manage ABJ, DA, TT.</li>
<li><b>authentication.py</b>: The authentication core.</li>
<li><b>cell.py</b>: The Cell object</li>
<li><b>column.py</b>: The Column, Columns objects</li>
<li><b>data.py</b>: Contains the functions used by data files</li>
<li><b>document.py</b>: Define the Table and manages the tables.</li>
<li><b>files.py</b>: Manage the static files. If a file is modified,
 it is automaticaly updated in the server
(but they may stay in client cache one hour)</li>
<li><b>inscrits.py</b>: LDAP connector to retrieve students lists</li>
<li><b>plugin.py</b>: Plugin management and URL parsing.</li>
<li><b>plugins.py</b>: Load plugins used by the servers.</li>
<li><b>referent.py</b>: Manage the 'referent' table.</li>
<li><b>tablestat.py</b>: Utility to compute statistics on tables. It manages the list of table used to do the 'suivi'</li>
<li><b>teacher.py</b>: Interface to UE (courses) informations.</li>
<li><b>ticket.py</b>: Ticket management.</li>
<li><b>regtestpatch.py</b>: Loaded if TOMUSS is in regression test mode. It patches the module in order to have no random in server answer.</li>
<li><b>objgraph.py</b>: GPL utility to navigate in object graph.</li>
<li><b>servers.py</b>: Define the class describing a 'suivi' server.</li>
<li><b>utilities.py</b>: Many utilities low level.</li>
</ul>





    <h2><a name="plugins">TOMUSS plugins</a></h2>

    <p>When a TOMUSS process is started it loads its plugins.
      The received requests are dispatched to the fittest plugin.
      The most important attributes of a plugin are.
    </p>
    <p>Do not modify <tt>plugins.py</tt> source to add plugins,
       use a <a href="#local">redefine</a> to add plugins</p>
    <p>
       Do modify global state with plugins because
       it may cause problems if the plugin is reloaded while
       the server is running.</p>

    <h3><a name="url_template">URL template</a></h3>

    <p>
      It defines the pattern of the request.
      For example: <tt>{Y}/{S}/{U}/resume</tt>
    </p>
    <p>
      Here are the components of the URLs and where
      they are stored in the request objet in the server side.
    </p>

    <table border="1">
	<tr><th>Component</th><th>Attribute name</th><th>Comment</th></tr>
	<tr><td>{Y}</td><td>the_year</td><td>The year of interest</td></tr>
	<tr><td>{S}</td><td>the_semester</td><td>The semester of interest</td></tr>
	<tr><td>{U}</td><td>the_ue</td><td>The course of interest</td></tr>
	<tr><td>{P}</td><td>the_page</td><td>The <em>page</em> of the table</td></tr>
	<tr><td>{?}</td><td>something</td><td>Not important</td></tr>
	<tr><td>{I}</td><td>the_student</td><td>A student identifier</td></tr>
	<tr><td>{_I}</td><td>the_student</td><td>A student identifier prefixed by _</td></tr>
	<tr><td>{*}</td><td>the_path</td><td>The remaining of the path</td></tr>
	<tr><td>{=}</td><td>&nbsp;</td><td>Remove any /=.... from the path</td></tr>
	<tr><td>{ }</td><td>the_time</td><td>The navigator request time (avoid caching)</td></tr>
    </table>

    <h3><a name="plugin_attributes">Plugin attributes</a></h3>

    <p>
      The possible values of the decision attributes are
      <tt>None</tt> to indicate that it is not important,
      <tt>True</tt> to indicate that session must match the attribute,
      <tt>False</tt> to indicate that session must <b>not</b> match the attribute.
    </p>


    <ul>
      <li> Authenticated: The client authenticated itself</li>
      <li> Teacher: The user is a teacher</li>
      <li> Administrative: The user is an administrative as a secretary.</li>
      <li> Referent Master: The user manage the referent teachers </li>
      <li> Root: The user is a super user</li>
      <li> Password OK: The user has a not stupid password</li>
    </ul>
    
    <p>
The action attributes are:
</p>
    <ul>
      <li> Background: a subprocess must be launched.</li>
      <li> Keep Open: the client socket should not be closed.</li>
      <li> Cached: the answer must not be cached by client/proxies.</li>
      <li> Link: defines if and how the link to the plugin
  is diplayed on the home page.
   It is an object with many attributes : text, url, help text,
   safety, where it is display, authorization test...</li>
      <li> Mime type and Response: Defines the HTTP header answer.</li>
    </ul>
    

    <h3><a name="plugin_list">Plugin documentation</a></h3>
    <p>
Some of the plugins are used both in TOMUSS and 'suivi' servers.
</p>
    <p>
The ``link and position´´ defines the function generating the <tt>HTML</tt>
content to insert in an home page.
The position specify the name of the box where to insert the code.
</p>


<table border="1" class="plugin_doc"><thead><tr><th>Name and location</th><th>Link and position</th><th>Explanations</th></tr></thead><tr><td><b><a name="plugin_abj">abj</a></b><br/><a href="../PLUGINS/abj_change.py">abj_change.py:102</a></td><td><b>Modification des ABJ et DA</b> in abj_master<br/><em>Permet d'aller sur la page d'édition des justificatifs d'absence et des tiers temps.</em></td><td>Display the home page for ABJ management</td></tr><tr><td><b><a name="plugin_abj_display">abj_display</a></b><br/><a href="../PLUGINS/abj_change.py">abj_change.py:204</a></td><td></td><td>Only useful for regtests</td></tr><tr><td><b><a name="plugin_abjaction">abjaction</a></b><br/><a href="../PLUGINS/abj_change.py">abj_change.py:155</a></td><td></td><td>This plugin perform an action on the ABJ and DA list.
    These actions can be adding or removing an ABJ or a DA.
    The client receive a feedback image indicating the status
    of the change.
    </td></tr><tr><td><b><a name="plugin_abjalpha">abjalpha</a></b><br/><a href="../PLUGINS/abj_change.py">abj_change.py:119</a></td><td><b>Récapitulatif ABJ+DA des licences</b> in abj_master<br/><em>Liste les justificatifs d'absence ainsi que les dispenses d'assiduité qui ont été saisies pour tous les étudiants de licences.</em></td><td>Send a CSV file containing all the ABJ and DA information
    for all the students</td></tr><tr><td><b><a name="plugin_abjalphaauthor">abjalphaauthor</a></b><br/><a href="../PLUGINS/abj_change.py">abj_change.py:137</a></td><td><b>Récapitulatif ABJ+DA que vous avez saisies</b> in abj_master<br/><em></em></td><td>Send a CSV file containing all the ABJ and DA information
    for all the students</td></tr><tr><td><b><a name="plugin_abjalphaepu">abjalphaepu</a></b><br/><a href="../PLUGINS/abj_change.py">abj_change.py:131</a></td><td><b>Récapitulatif ABJ+DA de l'EPU</b> in abj_master<br/><em>Liste les justificatifs d'absence ainsi que les dispenses d'assiduité qui ont été saisies pour tous les étudiants de master.</em></td><td>Send a CSV file containing all the ABJ and DA information
    for all the students</td></tr><tr><td><b><a name="plugin_abjalphamaster">abjalphamaster</a></b><br/><a href="../PLUGINS/abj_change.py">abj_change.py:125</a></td><td><b>Récapitulatif ABJ+DA des masters</b> in abj_master<br/><em>Liste les justificatifs d'absence ainsi que les dispenses d'assiduité qui ont été saisies pour tous les étudiants de master.</em></td><td>Send a CSV file containing all the ABJ and DA information
    for all the students</td></tr><tr><td><b><a name="plugin_abjhacker">abjhacker</a></b><br/><a href="../PLUGINS/abj_change.py">abj_change.py:199</a></td><td></td><td>Display an error message becauses this action is unauthorized</td></tr><tr><td><b><a name="plugin_abjlistmail">abjlistmail</a></b><br/><a href="../PLUGINS/abj_change.py">abj_change.py:148</a></td><td><b>Messages aux responsables d'UE de licence</b> in abj_master<br/><em>Ceci permettra d'envoyer un message à tous les responsables d'UE de licence en leur indiquant la liste des étudiants avec des absences justifiées, des dispenses et les tiers temps.</em></td><td>Generate and display all the message to the UE teachers
    given them the names of the students with ABJ, DA and TT.
    <p>At the end of the page, there is a link to really send the messages</p>
    </td></tr><tr><td><b><a name="plugin_abjsendmail">abjsendmail</a></b><br/><a href="../PLUGINS/abj_change.py">abj_change.py:143</a></td><td></td><td>When triggered this function send all the mail
    generated by the plugin <a href="#plugin_abjlistmail">listmail</a>.</td></tr><tr><td><b><a name="plugin_accept">accept</a></b><br/><a href="../PLUGINS/suivi_student.py">suivi_student.py:340</a></td><td></td><td>The student signs the contract</td></tr><tr><td><b><a name="plugin_answer_page">answer_page</a></b><br/><a href="../PLUGINS/newpage.py">newpage.py:235</a></td><td></td><td>Connect the browser IFRAME to the page</td></tr><tr><td><b><a name="plugin_apogee_check">apogee_check</a></b><br/></td><td><b>Vérification des bilans APOGEE</b> in Vérifications<br/><em>Cherche des erreurs dans les bilans APOGEE</em></td><td>Check APOGEE bilan</td></tr><tr><td><b><a name="plugin_apogee_check_sr">apogee_check_sr</a></b><br/></td><td><b>Vérification des bilans APOGEE (sauf sans résultats)</b> in Vérifications<br/><em>Cherche des erreurs dans les bilans APOGEE</em></td><td>Check APOGEE bilan (sauf sans résultats)</td></tr><tr><td><b><a name="plugin_bad_ip">bad_ip</a></b><br/><a href="../PLUGINS/suivi_bad_students.py">suivi_bad_students.py:52</a></td><td><b>Étudiants sans IP</b> in abj_master<br/><em>Affiche la liste des étudiants sans IP mais pour lesquels des informations ont été saisies dans TOMUSS</em></td><td>Display the list of student attending courses
    but not officialy registered</td></tr><tr><td><b><a name="plugin_badname">badname</a></b><br/><a href="../PLUGINS/suivi_badname.py">suivi_badname.py:32</a></td><td><b>Mauvais noms</b> in deprecated<br/><em>Liste les noms d'étudiants qui sont dans TOMUSS avec un nom qui ne correspond pas au numéro d'étudiant.</em></td><td>Display the names not matching the student ID.</td></tr><tr><td><b><a name="plugin_badpassword">badpassword</a></b><br/><a href="../PLUGINS/badpassword.py">badpassword.py:25</a></td><td></td><td>Display an information page to indicate that the user
    password is not safe because it is too simple.
    So the user has not access to the software</td></tr><tr><td><b><a name="plugin_bilan">bilan</a></b><br/><a href="../PLUGINS/bilan.py">bilan.py:34</a></td><td></td><td>Displays all the informations about a student in TOMUSS for
    all the semesters.
    These information can be augmented with other data</td></tr><tr><td><b><a name="plugin_caches">caches</a></b><br/><a href="../PLUGINS/gc_state.py">gc_state.py:125</a></td><td><b>Affiche les caches</b> in debug<br/><em>Utile pour trouver les fuites mémoire</em></td><td>Display the caches statistics</td></tr><tr><td><b><a name="plugin_cell">cell</a></b><br/><a href="../PLUGINS/cell_change.py">cell_change.py:59</a></td><td></td><td>Modify an unique cell in a table. It is used by the 'suivi' page</td></tr><tr><td><b><a name="plugin_change_identity">change_identity</a></b><br/><a href="../PLUGINS/change_identity.py">change_identity.py:26</a></td><td><b>Prendre l'identité de : &lt;form style="margin:0" action="javascript:var m = document.getElementById('new_identity').value ; window.location='/='+ticket+'/change_identity/' + m"&gt;&lt;input id="new_identity" class="search_field" name="x" class="keyword" value="john.doe"&gt;&lt;/form&gt;</b> in debug<br/><em>Pour prendre réellement l'identité de quelqu'un d'autre</em></td><td>Change of identity for this ticket</td></tr><tr><td><b><a name="plugin_clean">clean</a></b><br/><a href="../PLUGINS/clean.py">clean.py:26</a></td><td><b>Détruit les UE vides</b> in root_rw<br/><em>Les UE détruites sont mises dans le répertoire Trash. C'est LENT : il ne faut pas le faire en charge. Il est préférable de ne pas le faire pour garder un historique des étudiants inscrits. Ceci n'est fait que pour les UE du semestre choisi.</em></td><td>Remove all the UE that where not modified and are not actives.
    Display the list of the tables and why they are not deleted.</td></tr><tr><td><b><a name="plugin_clean_other">clean_other</a></b><br/><a href="../PLUGINS/clean.py">clean.py:58</a></td><td><b>Détruit les tables vides</b> in root_rw<br/><em>Les tables détruites sont mises dans le répertoire Trash. C'est LENT : il ne faut pas le faire en charge. Seul les tables complètement vides sont détruites. Ceci ne détruit pas les UE. </em></td><td>Remove all the tables (not UE) that are completly empty.</td></tr><tr><td><b><a name="plugin_clients">clients</a></b><br/><a href="../PLUGINS/clients.py">clients.py:146</a></td><td><b>Statistiques clients</b> in informations<br/><em>Sur les navigateurs, les systèmes d'exploitation et les adresses IP. C'est pas très bien pour la vie privée.</em></td><td>Display client statistics</td></tr><tr><td><b><a name="plugin_compositions_semestre">compositions_semestre</a></b><br/></td><td><b>Composition des semestres en cours</b> in Vérifications<br/><em>Pour les semestres pour lesquels il y a des étudiants qui ne l'ont pas validé, cela affiche la liste des UE entrant dans la composition du semestre</em></td><td>Compute semester composition</td></tr><tr><td><b><a name="plugin_count">count</a></b><br/><a href="../PLUGINS/count.py">count.py:41</a></td><td></td><td>count the number of cells with the given value
    used in the given tables per weeks.
    The week date must be indicated in the column course date.
    It may be used to compute the number of ABINJ per week for a set of UE.

    http://127.0.0.1:SUIVI_PORT_NUMBER/count/ABINJ/UE-INF1001L/UE-INF1002L

    </td></tr><tr><td><b><a name="plugin_delete_this_table">delete_this_table</a></b><br/><a href="../PLUGINS/pageaction.py">pageaction.py:177</a></td><td></td><td>Delete the table.</td></tr><tr><td><b><a name="plugin_emptyname">emptyname</a></b><br/></td><td></td><td>Bad url, redirect user to the home page</td></tr><tr><td><b><a name="plugin_end_of_load">end_of_load</a></b><br/><a href="../PLUGINS/pageaction.py">pageaction.py:211</a></td><td></td><td>Keep track of the end of page loading to do stats.
    If the page takes too long to load, then 'newpage.py' assumes
    that something blocked TOMUSS because the page loading does not finish.
    </td></tr><tr><td><b><a name="plugin_evaluate">evaluate</a></b><br/><a href="../PLUGINS/evaluate.py">evaluate.py:25</a></td><td></td><td>Evaluate a Python statement and display result</td></tr><tr><td><b><a name="plugin_extension">extension</a></b><br/><a href="../PLUGINS/pageaction.py">pageaction.py:96</a></td><td></td><td>Extend the current table to the next semester.
    A symbolic link is created and the current table is accessible
    in the 2 semesters but modifiable only in the 'next' semester.
    </td></tr><tr><td><b><a name="plugin_favorite_student">favorite_student</a></b><br/><a href="../PLUGINS/favorite_student.py">favorite_student.py:26</a></td><td></td><td>Toggle the favorite state of a student</td></tr><tr><td><b><a name="plugin_fusion">fusion</a></b><br/><a href="../PLUGINS/suivi_extract.py">suivi_extract.py:203</a></td><td></td><td>Fusion of named columns from tables, display as an HTML table
             /fusion/UE-INF2011L:a/UE-INF2012L:
    </td></tr><tr><td><b><a name="plugin_gcobject">gcobject</a></b><br/><a href="../PLUGINS/gc_state.py">gc_state.py:68</a></td><td></td><td>Display the graph of instances using or used by the object</td></tr><tr><td><b><a name="plugin_gctop">gctop</a></b><br/><a href="../PLUGINS/gc_state.py">gc_state.py:31</a></td><td><b>Affiche les objets python en mémoire</b> in debug<br/><em>Utile pour trouver les fuites mémoire</em></td><td>Display a clickable list of Python classes and their number of instance.</td></tr><tr><td><b><a name="plugin_gctype">gctype</a></b><br/><a href="../PLUGINS/gc_state.py">gc_state.py:47</a></td><td></td><td>Display a clickable list of the instance of the classes specified</td></tr><tr><td><b><a name="plugin_groupe">groupe</a></b><br/><a href="../PLUGINS/suivi_groupe.py">suivi_groupe.py:35</a></td><td><b>Tous les groupes saisis dans TOMUSS</b> in <!--4-->Groupage<br/><em>Extrait pour toutes les UE la liste des affectations de groupes faites par les enseignants.</em></td><td>List all the students groups defined by the teacher and not by TOMUSS</td></tr><tr><td><b><a name="plugin_home">home</a></b><br/><a href="../PLUGINS/suivi_student.py">suivi_student.py:353</a></td><td></td><td>Display the home page for 'suivi', it asks the student id.</td></tr><tr><td><b><a name="plugin_homepage2">homepage2</a></b><br/><a href="../PLUGINS/home2.py">home2.py:63</a></td><td></td><td>Display TOMUSS home page, it extracts some links from the
    plugin list. The page content depends on user role.</td></tr><tr><td><b><a name="plugin_icone">icone</a></b><br/><a href="../PLUGINS/suivi_icone.py">suivi_icone.py:32</a></td><td></td><td>Generate an icon summarising the student information
    from all the tables.</td></tr><tr><td><b><a name="plugin_icone_withticket">icone_withticket</a></b><br/><a href="../PLUGINS/suivi_icone.py">suivi_icone.py:32</a></td><td></td><td>Generate an icon summarising the student information
    from all the tables.</td></tr><tr><td><b><a name="plugin_infos">infos</a></b><br/><a href="../PLUGINS/suivi_student.py">suivi_student.py:465</a></td><td></td><td>Display the informations about all the students indicated.</td></tr><tr><td><b><a name="plugin_ip">ip</a></b><br/><a href="../PLUGINS/suivi_ip.py">suivi_ip.py:26</a></td><td><b>Trouve IP</b> in deprecated<br/><em>Pour chaque étudiants dans TOMUSS, à partir des informations saisies, essaye de deviner son IP. Ceci a été utilisé l'année ou les IP n'ont pas été faites. </em></td><td>For each student, display the tables where he's present</td></tr><tr><td><b><a name="plugin_key_history">key_history</a></b><br/><a href="../PLUGINS/pageaction.py">pageaction.py:163</a></td><td></td><td>Store the received data in the key history.
    It is used in 'linear' interface to analyse how it is used.</td></tr><tr><td><b><a name="plugin_live_log">live_log</a></b><br/><a href="../PLUGINS/live_log.py">live_log.py:25</a></td><td><b>Live log</b> in debug<br/><em>Affiche en temps réel le fichier de log</em></td><td>This page continuously display logs of the server.</td></tr><tr><td><b><a name="plugin_live_status_svg">live_status_svg</a></b><br/><a href="../PLUGINS/live_status.py">live_status.py:31</a></td><td><b>Live status</b> in debug<br/><em>Affichage graphique temps réel de TOMUSS</em></td><td>This page continuously display logs of the server.</td></tr><tr><td><b><a name="plugin_locks">locks</a></b><br/><a href="../PLUGINS/gc_state.py">gc_state.py:159</a></td><td><b>Affiche les verrous</b> in debug<br/><em>Utile pour trouver ce qui bloque</em></td><td>Displays all the lock states</td></tr><tr><td><b><a name="plugin_log">log</a></b><br/><a href="../PLUGINS/log.py">log.py:28</a></td><td></td><td>Store a text in a log file</td></tr><tr><td><b><a name="plugin_login_list">login_list</a></b><br/></td><td></td><td></td></tr><tr><td><b><a name="plugin_logout">logout</a></b><br/><a href="../PLUGINS/logout.py">logout.py:25</a></td><td></td><td>User deconnexion from the server.</td></tr><tr><td><b><a name="plugin_master_of">master_of</a></b><br/><a href="../PLUGINS/master_of.py">master_of.py:29</a></td><td><b>Réinitialise responsables</b> in root_rw<br/><em>À utiliser seulement en cas de bug. Ceci recalcule pour chaque utilisateur la liste des tables dont il est responsable. Ceci est normalement inutile.</em></td><td>Update the preferences to add the list of UE where the user is master.
    This function is only used in case of bug in the algorithm updating
    the master list incrementaly</td></tr><tr><td><b><a name="plugin_my_picture">my_picture</a></b><br/><a href="../PLUGINS/picture.py">picture.py:48</a></td><td></td><td>Display the picture of a student</td></tr><tr><td><b><a name="plugin_one_groupe">one_groupe</a></b><br/><a href="../PLUGINS/suivi_groupe.py">suivi_groupe.py:72</a></td><td></td><td>Group affectation for one table</td></tr><tr><td><b><a name="plugin_orphan_students">orphan_students</a></b><br/><a href="../PLUGINS/referent_get.py">referent_get.py:103</a></td><td><b>Enlever le référent d'étudiants</b> in <!--3-->Contacts pédagogique<br/><em></em></td><td>Remove the teacher of students</td></tr><tr><td><b><a name="plugin_page_unload">page_unload</a></b><br/><a href="../PLUGINS/pageaction.py">pageaction.py:79</a></td><td></td><td>Unload page from memory</td></tr><tr><td><b><a name="plugin_pageaction">pageaction</a></b><br/><a href="../PLUGINS/pageaction.py">pageaction.py:36</a></td><td></td><td>An editing action on the table is queued.
    The client may not send the action in the good order.</td></tr><tr><td><b><a name="plugin_pagenew">pagenew</a></b><br/><a href="../PLUGINS/newpage.py">newpage.py:89</a></td><td></td><td>Create a new page and send the table editor to the client.</td></tr><tr><td><b><a name="plugin_pageresume">pageresume</a></b><br/><a href="../PLUGINS/pageaction.py">pageaction.py:54</a></td><td></td><td>Display the list of ABJ, DA and TT for the students in the table.
    It is the same list being sended by mail.</td></tr><tr><td><b><a name="plugin_pagerewrite">pagerewrite</a></b><br/><a href="../PLUGINS/pageaction.py">pageaction.py:28</a></td><td></td><td>Generate the minimal python module defining the current
    table state. So there is no more history.</td></tr><tr><td><b><a name="plugin_picture">picture</a></b><br/><a href="../PLUGINS/picture.py">picture.py:31</a></td><td></td><td>Display the connected user picture</td></tr><tr><td><b><a name="plugin_preferences">preferences</a></b><br/><a href="../PLUGINS/suivi_preferences.py">suivi_preferences.py:28</a></td><td><b>Fusion des préférences</b> in informations<br/><em>Pour voir globalement ce que les utilisateurs modifient dans leurs préférences TOMUSS</em></td><td>Join of all the preferences table</td></tr><tr><td><b><a name="plugin_profiling">profiling</a></b><br/><a href="../utilities.py">utilities.py:53</a></td><td><b>Profiling des plugins</b> in debug<br/><em>Calcule des performances des différents plugins TOMUSS</em></td><td>Display the statistics on the plugin usage, number of call and times.</td></tr><tr><td><b><a name="plugin_progression">progression</a></b><br/></td><td><b>Statistiques sur la progression des étudiants</b> in Vérifications<br/><em>Calculs fait à partir des bilan web</em></td><td>Statistiques sur la progression des étudiants</td></tr><tr><td><b><a name="plugin_referent">referent</a></b><br/><a href="../PLUGINS/suivi_referent.py">suivi_referent.py:28</a></td><td></td><td>Display the referent of the named student</td></tr><tr><td><b><a name="plugin_referent_get">referent_get</a></b><br/><a href="../PLUGINS/referent_get.py">referent_get.py:58</a></td><td></td><td>Add a student to its refered students</td></tr><tr><td><b><a name="plugin_referent_set">referent_set</a></b><br/><a href="../PLUGINS/referent_get.py">referent_get.py:75</a></td><td><b>Affecter des étudiants à un enseignant</b> in <!--3-->Contacts pédagogique<br/><em></em></td><td>Add a student to a referent :
            ...../referent_set/user.name/student1/student2/...
    </td></tr><tr><td><b><a name="plugin_referents">referents</a></b><br/><a href="../PLUGINS/suivi_referents.py">suivi_referents.py:118</a></td><td><b>Statistiques référents pédagogiques</b> in <!--3-->Contacts pédagogique<br/><em>Affiche des statistiques sur les enseignants référents pédagogiques</em></td><td>Display statistics about referents.</td></tr><tr><td><b><a name="plugin_referents.csv">referents.csv</a></b><br/><a href="../PLUGINS/suivi_referent_list.py">suivi_referent_list.py:73</a></td><td></td><td>Generate the referent list in CSV</td></tr><tr><td><b><a name="plugin_referents.html">referents.html</a></b><br/><a href="../PLUGINS/suivi_referent_list.py">suivi_referent_list.py:81</a></td><td><b>Liste des étudiants référés HTML</b> in <!--3-->Contacts pédagogique<br/><em>Tableau donnant pour chaque étudiant référé le nom/mail de son enseignant référent pédagogique.</em></td><td>Generate the referent list in HTML</td></tr><tr><td><b><a name="plugin_referents.xls">referents.xls</a></b><br/><a href="../PLUGINS/suivi_referent_list.py">suivi_referent_list.py:81</a></td><td><b>Liste des étudiants référés XLS</b> in <!--3-->Contacts pédagogique<br/><em>Tableau donnant pour chaque étudiant référé le nom/mail de son enseignant référent pédagogique.</em></td><td>Generate the referent list in HTML</td></tr><tr><td><b><a name="plugin_referents_update">referents_update</a></b><br/><a href="../PLUGINS/referent_update.py">referent_update.py:25</a></td><td><b>Faire l'affectation des référents pédagogiques</b> in <!--3-->Contacts pédagogique<br/><em>Met à jour les affectations des enseignants référents pédagogiques.</em></td><td>Dispatch students without referents to a referent.</td></tr><tr><td><b><a name="plugin_referents_update_R">referents_update_R</a></b><br/><a href="../PLUGINS/referent_update.py">referent_update.py:42</a></td><td><b>Enlever les anciens étudiants aux référents pédagogiques</b> in <!--3-->Contacts pédagogique<br/><em>N'ajoute pas les nouveaux</em></td><td>Dispatch students without referents to a referent.</td></tr><tr><td><b><a name="plugin_referents_update_safe">referents_update_safe</a></b><br/><a href="../PLUGINS/referent_update.py">referent_update.py:58</a></td><td><b>Afficher l'affectation des référents pédagogiques</b> in <!--3-->Contacts pédagogique<br/><em>Affiche ce que l'affectation va faire.</em></td><td>Dispatch students without referents to a referent.</td></tr><tr><td><b><a name="plugin_referents_update_safe_R">referents_update_safe_R</a></b><br/><a href="../PLUGINS/referent_update.py">referent_update.py:74</a></td><td><b>Afficher les étudiants qui vont être enlevés aux référents pédagogiques</b> in <!--3-->Contacts pédagogique<br/><em></em></td><td>Dispatch students without referents to a referent.</td></tr><tr><td><b><a name="plugin_reload_plugins">reload_plugins</a></b><br/><a href="../PLUGINS/reload_plugins.py">reload_plugins.py:31</a></td><td><b>Recharge les plugins</b> in debug<br/><em>Met à jours les plugins modifiés sur disque. C'est-à-dire : le contenu des répertoires PLUGINS, ATTRIBUTES et COLUMN_TYPES (y compris JavaScript). Les plugins modifiant l'état de l'application ou lançant des threads ne doivent pas être rechargés.</em></td><td>Reload all the plugins from PLUGINS, COLUMN_TYPES, ATTRIBUTES</td></tr><tr><td><b><a name="plugin_resume">resume</a></b><br/><a href="../PLUGINS/resume.py">resume.py:38</a></td><td></td><td>Resume the number of cells used in the given tables</td></tr><tr><td><b><a name="plugin_rss">rss</a></b><br/><a href="../PLUGINS/suivi_student.py">suivi_student.py:531</a></td><td></td><td>RSS for the student.</td></tr><tr><td><b><a name="plugin_rss2">rss2</a></b><br/><a href="../PLUGINS/suivi_student.py">suivi_student.py:633</a></td><td></td><td>RSS for the table.</td></tr><tr><td><b><a name="plugin_send_alert">send_alert</a></b><br/><a href="../PLUGINS/send_alert.py">send_alert.py:28</a></td><td><b>Envoyer le message suivant :&lt;br&gt;&lt;form style="margin:0" action="javascript:var m = document.getElementById('message').value ; if(confirm('Vous allez envoyer le message :\n\n' + m)) window.location='/='+ticket+'/send_alert//' + m"&gt;&lt;input id="message" class="search_field" name="x" class="keyword" value="Le serveur va être redémarré dans quelques secondes, il est conseillé (mais non obligatoire) de réactualiser la page après le redémarrage."&gt;&lt;/form&gt;</b> in root_rw<br/><em>Vous pouvez éditer le champ texte afin d'envoyer un message (fenêtre popup) à tous les utilisateurs actuellement connectés à TOMUSS. Taper 'return' pour envoyer le message.</em></td><td>Send an alert popup to the TABLE clients or all clients .</td></tr><tr><td><b><a name="plugin_send_mail">send_mail</a></b><br/><a href="../PLUGINS/send_mail.py">send_mail.py:28</a></td><td></td><td>Send personnalized mails</td></tr><tr><td><b><a name="plugin_set_page">set_page</a></b><br/><a href="../PLUGINS/newpage.py">newpage.py:298</a></td><td></td><td>Set the number of page load (for favorites management by users)</td></tr><tr><td><b><a name="plugin_statpage">statpage</a></b><br/><a href="../PLUGINS/statpage.py">statpage.py:37</a></td><td><b>Qui fait quoi</b> in informations<br/><em>Liste les tables TOMUSS en mémoire</em></td><td>Display the table in memory and the user on them.</td></tr><tr><td><b><a name="plugin_student">student</a></b><br/><a href="../PLUGINS/suivi_student.py">suivi_student.py:313</a></td><td></td><td>Display all the informations about a student.</td></tr><tr><td><b><a name="plugin_student_redirect">student_redirect</a></b><br/></td><td></td><td>Redirect the student on the 'suivi'.</td></tr><tr><td><b><a name="plugin_suivi_extract">suivi_extract</a></b><br/><a href="../PLUGINS/suivi_extract.py">suivi_extract.py:110</a></td><td></td><td>Extract named columns from tables, display as an HTML table
             /extract/UE-XXXXX:Column1:Column2/UE-YYYY:ColumnX:ColumnY...
    </td></tr><tr><td><b><a name="plugin_suivi_fusion_inscrit_author">suivi_fusion_inscrit_author</a></b><br/><a href="../PLUGINS/suivi_extract.py">suivi_extract.py:214</a></td><td></td><td>Fusion of named columns from tables, display as an HTML table
             /fusion_inscrit_author/UE-INF2011L:a/UE-INF2012L:
    </td></tr><tr><td><b><a name="plugin_tablecopy">tablecopy</a></b><br/><a href="../ATTRIBUTES/tablecopy.py">tablecopy.py:41</a></td><td></td><td>Copy the table in another EMPTY one</td></tr><tr><td><b><a name="plugin_tables">tables</a></b><br/><a href="../PLUGINS/suivi_tables.py">suivi_tables.py:132</a></td><td><b>Statistiques UE</b> in informations<br/><em>Pour chaque UE, affiche les statistiques concernant l'UE</em></td><td>Create a table of statistics about all the tables,
    redirect the browser on this table.</td></tr><tr><td><b><a name="plugin_teachers">teachers</a></b><br/><a href="../PLUGINS/suivi_teachers.py">suivi_teachers.py:142</a></td><td><b>Statistiques enseignants</b> in informations<br/><em>Pour chaque enseignant, affiche les statistiques sur les informations saisies dans TOMUSS</em></td><td>Create a table of statistics about all the teachers,
    redirect the browser on this table.</td></tr><tr><td><b><a name="plugin_threads">threads</a></b><br/><a href="../PLUGINS/gc_state.py">gc_state.py:172</a></td><td><b>Affiche les threads</b> in debug<br/><em>Utile pour trouver ce qui charge</em></td><td>Displays the running thread</td></tr><tr><td><b><a name="plugin_tickets">tickets</a></b><br/><a href="../PLUGINS/tickets.py">tickets.py:29</a></td><td><b>Tickets actifs</b> in informations<br/><em>Liste des tickets actuellement valides</em></td><td>Display tickets</td></tr><tr><td><b><a name="plugin_uninterested">uninterested</a></b><br/><a href="../PLUGINS/suivi_uninterested.py">suivi_uninterested.py:157</a></td><td><b>Étudiants suivis ne regardant pas TOMUSS</b> in informations<br/><em>Pour chaque étudiants suivi par un référent pédagogique, on regarde le nombre de présences/notes et leur nombre de visites à TOMUSS.</em></td><td>Display information about student and their referents and
    how student interact with TOMUSS and referents.</td></tr><tr><td><b><a name="plugin_unload">unload</a></b><br/><a href="../PLUGINS/unload.py">unload.py:25</a></td><td></td><td>Unload the table from memory.</td></tr></table>
    <h2><a name="protocols">TOMUSS protocols</a></h2>

    <p>
      The client perform action on the server by inserting image objects
in the HTML code.
The returned image give the user feedback
(green: ok, orange: waiting server, red: unauthorized, violet: bug...)
</p>
    <p>
The client does not poll server to retrieve changes,
but the server send javascript fragment when there is an update to send.
</p>

    <h3><a name="protocol_tomuss">TOMUSS client/server protocol</a></h3>

    <p>
      The client load the documents as usual.
      But if the document is a table:
    </p>
    <ul>
      <li> The socket is not closed, so the client will receive
	live table update as javascript commands.</li>
      <li> an unique <em>page</em> identifier is created,
	all the modification and identifiers will be associated
	with this unique identifier so there is no possible naming conflict.</li>
      <li> a <em>request identifier</em> is initied.
	It will be used to sort the requests by time.</li>
      <li> Subsequent requests from the client to modifiy the
	table are images load,
	the image URL contains all the transaction information
	and the returned image itself is the feedback.</li>
    </ul>

<table class="plugin" border="1"><thead><tr><th>Name</th><th>URL template</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Authenticated</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Teacher</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Referent</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Administrator</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Abj master</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Referent master</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">root</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Password OK</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Backgrounded</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Cached</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Keep open</text></svg>
</th><th>Mime Type</th></tr></thead><tbody>
<tr><td><a href="Welcome.xml#plugin_cell">cell</a></td><td>{Y}/{S}/{U}/cell/{*}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_student_redirect">student_redirect</a></td><td>{*}</td><td>T</td><td>F</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>F</td><td>F</td><td>F</td><td>307</td></tr>
<tr><td><a href="Welcome.xml#plugin_abj">abj</a></td><td>{Y}/{S}/abj</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>T</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_abjalpha">abjalpha</a></td><td>{Y}/{S}/abj/alpha.xls</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_abjalphaauthor">abjalphaauthor</a></td><td>{Y}/{S}/abj/alpha_author.xls</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_abjalphaepu">abjalphaepu</a></td><td>{Y}/{S}/abj/alpha_epu.xls</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_abjalphamaster">abjalphamaster</a></td><td>{Y}/{S}/abj/alpha_master.xls</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_abj_display">abj_display</a></td><td>{Y}/{S}/abj/display/{I}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_abjlistmail">abjlistmail</a></td><td>{Y}/{S}/abj/list_mail</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_abjsendmail">abjsendmail</a></td><td>{Y}/{S}/abj/send_mail</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_abjaction">abjaction</a></td><td>{Y}/{S}/abj/{P}/{I}/{*}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>image/png</td></tr>
<tr><td><a href="Welcome.xml#plugin_abjhacker">abjhacker</a></td><td>{Y}/{S}/abjs</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_answer_page">answer_page</a></td><td>{Y}/{S}/{U}/{P}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>T</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_end_of_load">end_of_load</a></td><td>{Y}/{S}/{U}/{P}/end_of_load</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>image/png</td></tr>
<tr><td><a href="Welcome.xml#plugin_key_history">key_history</a></td><td>{Y}/{S}/{U}/{P}/key_history/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>None</td></tr>
<tr><td><a href="Welcome.xml#plugin_pageaction">pageaction</a></td><td>{Y}/{S}/{U}/{P}/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>T</td><td>T</td><td>image/png</td></tr>
<tr><td><a href="Welcome.xml#plugin_apogee_check">apogee_check</a></td><td>apogee_check</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html;charset=utf8</td></tr>
<tr><td><a href="Welcome.xml#plugin_apogee_check_sr">apogee_check_sr</a></td><td>apogee_check_sr</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html;charset=utf8</td></tr>
<tr><td><a href="Welcome.xml#plugin_bilan">bilan</a></td><td>bilan/{I}</td><td>T</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_caches">caches</a></td><td>caches</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_change_identity">change_identity</a></td><td>change_identity/{*}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>T</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_clean_other">clean_other</a></td><td>clean_other</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_clients">clients</a></td><td>clients/{Y}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td><td>307</td></tr>
<tr><td><a href="Welcome.xml#plugin_compositions_semestre">compositions_semestre</a></td><td>compositions_semestre</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html;charset=utf8</td></tr>
<tr><td><a href="Welcome.xml#plugin_evaluate">evaluate</a></td><td>evaluate/{*}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/plain</td></tr>
<tr><td><a href="Welcome.xml#plugin_favorite_student">favorite_student</a></td><td>favorite_student/{?}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_gctop">gctop</a></td><td>gc</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_live_log">live_log</a></td><td>live_log</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>T</td><td>text/plain</td></tr>
<tr><td><a href="Welcome.xml#plugin_live_status_svg">live_status_svg</a></td><td>live_status.svg</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>T</td><td>image/svg+xml</td></tr>
<tr><td><a href="Welcome.xml#plugin_locks">locks</a></td><td>locks</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_log">log</a></td><td>log/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_login_list">login_list</a></td><td>login_list/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>application/x-javascript</td></tr>
<tr><td><a href="Welcome.xml#plugin_logout">logout</a></td><td>logout</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>F</td><td>F</td><td>F</td><td>307</td></tr>
<tr><td><a href="Welcome.xml#plugin_master_of">master_of</a></td><td>master_of</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_gcobject">gcobject</a></td><td>object/{*}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>image/png</td></tr>
<tr><td><a href="Welcome.xml#plugin_orphan_students">orphan_students</a></td><td>orphan_students/{*}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/plain; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_picture">picture</a></td><td>picture/{?}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>image/jpeg</td></tr>
<tr><td><a href="Welcome.xml#plugin_my_picture">my_picture</a></td><td>picture/{?}</td><td>T</td><td>F</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>image/jpeg</td></tr>
<tr><td><a href="Welcome.xml#plugin_profiling">profiling</a></td><td>profiling/{Y}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>T</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_progression">progression</a></td><td>progression</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_referent">referent</a></td><td>referent/{?}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_referent_get">referent_get</a></td><td>referent_get/{*}</td><td>T</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/plain; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_referent_set">referent_set</a></td><td>referent_set/{*}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/plain; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_referents_update">referents_update</a></td><td>referents</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_referents_update_R">referents_update_R</a></td><td>referents_R</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_referents_update_safe">referents_update_safe</a></td><td>referents_safe</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_referents_update_safe_R">referents_update_safe_R</a></td><td>referents_safe_R</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_reload_plugins">reload_plugins</a></td><td>reload_plugins</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_send_alert">send_alert</a></td><td>send_alert/{U}/{*}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_send_mail">send_mail</a></td><td>send_mail/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/plain;charset=utf-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_set_page">set_page</a></td><td>set_page/{U}/{P}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_statpage">statpage</a></td><td>stat</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_threads">threads</a></td><td>threads</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_tickets">tickets</a></td><td>tickets</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>307</td></tr>
<tr><td><a href="Welcome.xml#plugin_gctype">gctype</a></td><td>type/{*}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_badpassword">badpassword</a></td><td>{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>F</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_homepage2">homepage2</a></td><td>{=}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_emptyname">emptyname</a></td><td>{Y}/{S}/</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>307</td></tr>
<tr><td><a href="Welcome.xml#plugin_clean">clean</a></td><td>{Y}/{S}/clean</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_delete_this_table">delete_this_table</a></td><td>{Y}/{S}/{U}/delete_this_table</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_extension">extension</a></td><td>{Y}/{S}/{U}/extension</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_page_unload">page_unload</a></td><td>{Y}/{S}/{U}/page_unload</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_pageresume">pageresume</a></td><td>{Y}/{S}/{U}/resume</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/plain; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_pagerewrite">pagerewrite</a></td><td>{Y}/{S}/{U}/rewrite</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/plain; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_tablecopy">tablecopy</a></td><td>{Y}/{S}/{U}/tablecopy/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/plain; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_pagenew">pagenew</a></td><td>{Y}/{S}/{U}/{=}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>T</td><td>text/html; charset=UTF-8</td></tr>
</tbody></table>

    <h3><a name="protocol_suivi">TOMUSS suivi client/server protocol</a></h3>

<table class="plugin" border="1"><thead><tr><th>Name</th><th>URL template</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Authenticated</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Teacher</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Referent</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Administrator</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Abj master</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Referent master</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">root</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Password OK</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Backgrounded</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Cached</text></svg>
</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">Keep open</text></svg>
</th><th>Mime Type</th></tr></thead><tbody>
<tr><td><a href="Welcome.xml#plugin_home">home</a></td><td></td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_teachers">teachers</a></td><td>*</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>307</td></tr>
<tr><td><a href="Welcome.xml#plugin_bad_ip">bad_ip</a></td><td>*1</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_tables">tables</a></td><td>*2</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>307</td></tr>
<tr><td><a href="Welcome.xml#plugin_referents">referents</a></td><td>*3</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_accept">accept</a></td><td>accept</td><td>T</td><td>F</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_badname">badname</a></td><td>badname</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_count">count</a></td><td>count/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>T</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_suivi_extract">suivi_extract</a></td><td>extract/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_fusion">fusion</a></td><td>fusion/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_suivi_fusion_inscrit_author">suivi_fusion_inscrit_author</a></td><td>fusion_inscrit_author/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_groupe">groupe</a></td><td>groupe</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>text/html</td></tr>
<tr><td><a href="Welcome.xml#plugin_one_groupe">one_groupe</a></td><td>groupe/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>text/csv</td></tr>
<tr><td><a href="Welcome.xml#plugin_ip">ip</a></td><td>ip</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/plain</td></tr>
<tr><td><a href="Welcome.xml#plugin_referents.csv">referents.csv</a></td><td>referents.csv</td><td>F</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/csv; charset=latin1</td></tr>
<tr><td><a href="Welcome.xml#plugin_referents.html">referents.html</a></td><td>referents.html</td><td>F</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=latin1</td></tr>
<tr><td><a href="Welcome.xml#plugin_referents.xls">referents.xls</a></td><td>referents.xls</td><td>F</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>F</td><td>application/excel; charset=latin1</td></tr>
<tr><td><a href="Welcome.xml#plugin_resume">resume</a></td><td>resume/{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>F</td><td>T</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_rss">rss</a></td><td>rss/{*}</td><td>F</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>F</td><td>F</td><td>F</td><td>application/rss+xml</td></tr>
<tr><td><a href="Welcome.xml#plugin_rss2">rss2</a></td><td>rss2/{*}</td><td>F</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>F</td><td>F</td><td>F</td><td>application/rss+xml</td></tr>
<tr><td><a href="Welcome.xml#plugin_preferences">preferences</a></td><td>stat_preferences</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td><td>307</td></tr>
<tr><td><a href="Welcome.xml#plugin_uninterested">uninterested</a></td><td>uninterested</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>T</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_unload">unload</a></td><td>unload/{U}</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_student">student</a></td><td>{*}</td><td>T</td><td>F</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_infos">infos</a></td><td>{*}</td><td>T</td><td>T</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>text/html; charset=UTF-8</td></tr>
<tr><td><a href="Welcome.xml#plugin_icone_withticket">icone_withticket</a></td><td>{?}/{_I}</td><td>F</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>image/png</td></tr>
<tr><td><a href="Welcome.xml#plugin_icone">icone</a></td><td>{_I}</td><td>F</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>T</td><td>F</td><td>F</td><td>F</td><td>image/png</td></tr>
</tbody></table>

    <h2><a name="storage">TOMUSS storage</a></h2>

    <p>
The table data is stored as a Python module, the module source
is only modified by doing an append to prevent any data loss.
File size is checked before and after append to insure that
the data was really saved.
</p>
    <p>
All the TOMUSS data are stored in two files,
so two identical hierarchies are managed.
</p>


    <h3><a name="table_storage">Table storage</a></h3>
<p>
The columns and lines are identified by a key usualy created
as: page_id + '_' + number.
With this scheme, two identical keys are not possible.
</p>
<p>
The functions to modify the table are:
</p>

<p><b>table_attr</b>(attr, page_id, value, page): None</p><p><b>column_attr</b>(attr, page_id, col_id, value, col, page): None</p><p><b>column_empty_is</b>(page_id, col, empty_is): Change the column empty_is value.</p><p><b>comment_change</b>(page_id, col, lin, value): Change the comment on a cell.</p><p><b>column_change</b>(page_id, col, title, ttype, test, weight, freezed, hidden, width): Change some attributes of the column.</p><p><b>end</b>(): Stop the importation of a table (release lock)</p><p><b>column_comment</b>(page_id, col, comment): Change the column comment.</p><p><b>table_comment</b>(page_id, comment): Change the table comment.</p><p><b>column_visibility_date</b>(page_id, col, date): Specify the date of visibility</p><p><b>begin</b>(table): Start the importation of a table (set lock)</p><p><b>new_page</b>(ticket, user_name, user_ip, user_browser, date): Create a new page in the table, the identifier of page (page_id)
    is an integer starting from 0.</p><p><b>private_toggle</b>(page_id): Change the privacy state of the table.</p><p><b>default_nr_columns</b>(nr): Specify the default number of columns displayed on screen.</p><p><b>add_master</b>(name, page_id): Add or remove a master on the page.</p><p><b>date_change</b>(page_id, dates): Change the table dates.</p><p><b>column_delete</b>(page_id, col): Delete the column if it is empty.</p><p><b>column_position</b>(page_id, col, position): Define the column position (the columns are sorted by position.</p><p><b>cell_change</b>(page_id, col, lin, value, date): Change the value of a cell.</p>
    <h3><a name="abj_storage">ABJ + DA storage</a></h3>

    <p>As table data, informations about ABJ and DA are stored
in a Python module. The parameters are:</p>
<ul>
     <li> '<b>login</b>' The student identifier</li>
      <li> '<b>from</b>', '<b>to</b>' Start and end date
 of the justification (DD/MM/YYYY[MA]) M=Morning A=Afternoon</li>
      <li> '<b>date</b>' (DD/MM/YYYY) Start of the dispense of assiduity.</li>
      <li> '<b>user_name</b>', '<b>action_date</b>' The user that modified the information and when.</li>
      </ul>

<p>
The functions to modify the data are:
</p>

<pre>add   (login, from, to  , user_name, action_date)
rem   (login, from, to  , user_name, action_date)
add_da(login, ue  , date, user_name, action_date)
rem_da(login, ue        , user_name, action_date)
</pre>


    <h3><a name="tt_storage">TT storage</a></h3>

    <p>The information about 'Tiers Temps' are stored in a normal table,
the columns order must not be modified.
There is a line per student.</p>

    <h3><a name="referent_storage">Referent storage</a></h3>

    <p>The information about referents teacher are stored
       in a normal table named 'orientation_rp'.
       Each line contains the teacher login, its portals.
    </p>
    <p>The list of students per referent 
       is stored in 'referents_students' table
    </p>



    <h2><a name="objects">TOMUSS objects server side</a></h2>


<h3><a name="Cell">Cell</a></h3>
<p>Define a cell with the given attributes.
    It also manage the cell history and a cache to minimize
    CPU time to generate the minimal JavaScript code for the cell.
    Attributes of the cell should not be modified directly.
    </p>
Methods : <ul><li>'<b>__init__</b>': Create a new cell with some attributes in the list:
        'value', 'author', 'date' and 'comment'.
        The date is formatted as YYYYMMDDHHMMSS.
        </li>
<li>'<b>copy</b>'</li>
<li>'<b>js</b>': Generate the Cell JavaScript object with the minimal code,
        in order to minimize file size.</li>
<li>'<b>js_student</b>'</li>
<li>'<b>set_comment</b>': change the comment on the cell.</li>
<li>'<b>set_value</b>': change the value of the cell.</li></ul>
<h3><a name="CellEmpty">CellEmpty</a></h3>
<p>Define an empty cell. It is only to use less memory than a Cell</p>
Methods : <ul><li>'<b>copy</b>'</li>
<li>'<b>empty</b>': Returns True</li>
<li>'<b>js</b>': Create the javascript code for an empty cell.</li>
<li>'<b>js_student</b>': Create the javascript code for an empty cell.</li>
<li>'<b>set_comment</b>': Create a non empty cell with the comment and returns it.</li>
<li>'<b>set_value</b>': Create a non empty cell with the value and returns it.</li></ul>
<h3><a name="CellValue">CellValue</a></h3>
<p>Define a cell without history nor comment
    It is the most used cell content
    </p>
Methods : <ul><li>'<b>__init__</b>'</li>
<li>'<b>copy</b>'</li>
<li>'<b>empty</b>': Returns True if the cell is empty</li>
<li>'<b>js</b>': Generate the Cell JavaScript object with the minimal code,
        in order to minimize file size.</li>
<li>'<b>js_student</b>': Generate the Cell JavaScript object with the minimal code,
        in order to minimize file size.</li>
<li>'<b>set_comment</b>': change the comment on the cell.</li>
<li>'<b>set_value</b>'</li></ul>
<h3><a name="CellVirtual">CellVirtual</a></h3>
<p>The <b>set_</b> methods in cells returns a new object if needed.
    It is because an CellEmpty can become a Cell.
    </p>
<h3><a name="Column">Column</a></h3>
<p>The Column object contains all the informations about the column.
    Once the Column is integrated in a table, it memorizes the table pointer.
    </p>
Methods : <ul><li>'<b>__init__</b>': Create the column with the same defaults than in the column object
        in the javascript side.</li>
<li>'<b>cell</b>': Format a cell value in order to display it.
        It uses the formatter defined by the column type.</li>
<li>'<b>cell_indicator</b>': Compute if the cell value is a good or bad one.
        Returns the HTML class name associated and a value between 0 and 1</li>
<li>'<b>cell_values</b>': Compute the list of cells FLOAT values with the given Grp and Seq</li>
<li>'<b>copy_on_browser</b>': Returns True if the column is needed to compute a visible result</li>
<li>'<b>depends_on</b>': Return the list of columns used to compute this one</li>
<li>'<b>dump</b>': 'print' all the column information for debugging</li>
<li>'<b>empty</b>': Returns True if all the cells in the column are empty.</li>
<li>'<b>is_modifiable</b>'</li>
<li>'<b>js</b>': Returns the JavaScript describing the column.</li>
<li>'<b>min_max</b>': From the Note 'test' value stored as [min;red;green;max]
        returns the min and the max as float numbers.</li>
<li>'<b>nr_cells_not_empty_and_empty</b>': Compute the total number of cells and the number of empty cell
        in the column.</li>
<li>'<b>visible</b>': Returns true if the column is visible for the student</li></ul>
<h3><a name="Columns">Columns</a></h3>
<p>A set of Column associated to a table.
    The columns are stored in a list, so they have an index.
    They also have an unique 'id' used to communicate with the client
    because the columns are not in the same order in all the clients.
    </p>
Methods : <ul><li>'<b>__getitem__</b>': Get the column at the given index.</li>
<li>'<b>__init__</b>': Create an empty set associated to the table.</li>
<li>'<b>__iter__</b>': Iterate over the columns.</li>
<li>'<b>__len__</b>': Returns the number of columns.</li>
<li>'<b>__setitem__</b>': Set the column at the given index and update some information
        in order to have faster access.</li>
<li>'<b>append</b>': Add a new column.</li>
<li>'<b>data_col_from_title</b>': Retrieve the column from its 'title'.</li>
<li>'<b>from_id</b>': Retrieve the column from its 'id'.</li>
<li>'<b>from_title</b>': Retrieve the column from its 'title'.</li>
<li>'<b>get_grp</b>': Get the data_col of the column named 'Grp'</li>
<li>'<b>get_seq</b>': Get the data_col of the column named 'Seq'</li>
<li>'<b>js</b>': Returns the javaScript code describing all NEEDED columns.</li>
<li>'<b>pop</b>': Remove the column at the given index.</li>
<li>'<b>use</b>': Columns using this one.</li></ul>
<h3><a name="Line">Line</a></h3>
<p>The Line object is usable as a Python list.</p>
Methods : <ul><li>'<b>__getitem__</b>': Take a Cell in the line.</li>
<li>'<b>__init__</b>': Create the Line from a Cell list.</li>
<li>'<b>__len__</b>': The the number of cell in the line.</li>
<li>'<b>__setitem__</b>': Replace a Cell in the line.</li>
<li>'<b>append</b>': Add a new cell in the line.</li>
<li>'<b>js</b>': Translate the line in JavaScript</li>
<li>'<b>pop</b>': Remove the selected cell from the line.</li></ul>
<h3><a name="Lines">Lines</a></h3>
<p>The Lines object is usable as a Python list of Line.
    But as it knows the columns it performs some high level functions.
    It is a dictionnary of lines, the key is the line_id.
    </p>
Methods : <ul><li>'<b>__contains__</b>': Returns True if the line_id is known.</li>
<li>'<b>__getitem__</b>': Access to a non existent Line create a new line
        with the good number of empty cells.</li>
<li>'<b>__init__</b>': Create an empty set of lines for the given columns</li>
<li>'<b>__iter__</b>': Iterate over all the lines.</li>
<li>'<b>__len__</b>': Returns the number of lines.</li>
<li>'<b>get_grp</b>': Get the 'Grp' value for the student</li>
<li>'<b>get_seq</b>': Get the 'Seq' value for the student</li>
<li>'<b>items</b>': Iterate on the line to get the pair: (line_id, line).</li>
<li>'<b>js</b>': Create JavaScript generating all the lines data.</li>
<li>'<b>keys</b>': Iterate on the line to get the line keys: line_id.</li>
<li>'<b>line_compute_js</b>': Create the JavaScript program that initalizes 'line' with
        all the informations for the student.
        The value computing is done in javascript.</li>
<li>'<b>line_html</b>': Returns an HTML string displaying all the information in the line.
        This function is used to display the 'suivi'.
        The content is not the same for teacher and students because
        teacher have access to table links and boolean toggle to directly
        modify the tables.
        This function is expensive because the student rank is computed
        for all 'Note' columns.
        </li>
<li>'<b>line_indicator</b>': Returns a list of (date, cell_indicator)
        for the column type given. It is used to compute icons.</li>
<li>'<b>values</b>': Iterate on the line to get the line values.</li></ul>
<h3><a name="UE">UE</a></h3>
<p>Contains all the information about the UE (Unity of Evaluation).</p>
Methods : <ul><li>'<b>__cmp__</b>'</li>
<li>'<b>__init__</b>': This object may retrieve information from other data sources</li>
<li>'<b>__str__</b>': Returns the Python code creating this object.</li>
<li>'<b>code</b>': Returns a database code to access this UE.</li>
<li>'<b>intitule</b>': Returns the title of the UE.</li>
<li>'<b>js</b>': Returns the code to create the JavaScript object for this one.
        If read_tt is a list, then TT students are stored in the list.
        </li>
<li>'<b>mails</b>': Returns all the mails of the managers of this UE.</li>
<li>'<b>parcours</b>': Return the list of diploma using this UE.</li>
<li>'<b>responsables</b>': Returns the NAME list of the manager of the UE.</li>
<li>'<b>responsables_login</b>': Returns the LOGIN list of the manager of the UE.</li>
<li>'<b>short_parcours</b>': Returns the UFR managing this UE.</li></ul>

    <h3><a name="column_types">Types of columns</a></h3>
    <p>
They are defined in <tt>COLUMN_TYPES</tt> directory.
To add a new column type, the only thing to do is
to add 2 new files in <tt>COLUMN_TYPES</tt>.
There is a Python and a JavaScript file per type.
Types are defined by a class tree.
</p>
    <p><a href="xxx_type2.html">The table of types</a> display
    for each types how to manage the column and the cell.
    More explanations are in the <tt>text.py</tt> file.
   </p>

    <h2><a name="column_attributes">Attributes of table columns</a></h2>

<table border="1">
<tbody>
<tr><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">name</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">display_table</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">update_horizontal_scrollbar</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">update_headers</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">update_table_headers</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">need_authorization</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">formatter</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">empty</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">default_value</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">computed</text></svg>
&nbsp;&nbsp;</th></tr><tr><td>type</td><td>1</td><td>0</td><td>1</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>Note</td><td>0</td></tr>
<tr><td>author</td><td>0</td><td>0</td><td>1</td><td>0</td><td>1</td><td>get_author2</td><td>function(column, value) { return value === "" ; }</td><td></td><td>1</td></tr>
<tr><td>columns</td><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td></td><td>0</td></tr>
<tr><td>comment</td><td>1</td><td>0</td><td>1</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td></td><td>0</td></tr>
<tr><td>course_dates</td><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td><td>course_dates_formatter</td><td>function(column, value) { return value === "" ; }</td><td></td><td>0</td></tr>
<tr><td>delete</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>1</td><td>0</td></tr>
<tr><td>empty_is</td><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td></td><td>0</td></tr>
<tr><td>enumeration</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td></td><td>0</td></tr>
<tr><td>export</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>1</td><td>0</td></tr>
<tr><td>fill</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>1</td><td>0</td></tr>
<tr><td>freezed</td><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td></td><td>0</td></tr>
<tr><td>green</td><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td></td><td>0</td></tr>
<tr><td>hidden</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>0</td><td>1</td></tr>
<tr><td>import</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>1</td><td>0</td></tr>
<tr><td>locked</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>0</td><td>0</td></tr>
<tr><td>minmax</td><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>[0;20]</td><td>0</td></tr>
<tr><td>modifiable</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(column, value) { var e = document.getElementById('t_column_modifiable') ; if ( e ) if ( value &gt;= 2 ) e.style.background = '#F88' ; else e.style.background = '' ; return value ;}</td><td>function(column, value) { return value === "" ; }</td><td>0</td><td>0</td></tr>
<tr><td>position</td><td>1</td><td>0</td><td>0</td><td>1</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td></td><td>0</td></tr>
<tr><td>red</td><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td></td><td>0</td></tr>
<tr><td>repetition</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>0</td><td>0</td></tr>
<tr><td>stats</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>1</td><td>0</td></tr>
<tr><td>test_filter</td><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>!ABINJ</td><td>0</td></tr>
<tr><td>title</td><td>0</td><td>0</td><td>0</td><td>1</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value.substr(0,default_title.length) == default_title &amp;&amp; !isNaN(value.substr(default_title.length))  ; }</td><td></td><td>0</td></tr>
<tr><td>visibility_date</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>
function(column, value)
{
  if ( value === '' ) return '' ;
  return column.visibility_date.substr(6,2) + '/' +
	 column.visibility_date.substr(4,2) + '/' +
	 column.visibility_date.substr(0,4) ;
}</td><td>function(column, value) { return value === "" ; }</td><td></td><td>0</td></tr>
<tr><td>weight</td><td>1</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>1</td><td>0</td></tr>
<tr><td>width</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(column, value) { return value ; }</td><td>function(column, value) { return value === "" ; }</td><td>4</td><td>0</td></tr>
</tbody></table>

    <h2><a name="table_attributes">Attributes of tables</a></h2>

<table border="1">
<tbody>
<tr><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">name</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">display_table</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">update_horizontal_scrollbar</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">update_headers</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">update_table_headers</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">need_authorization</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">formatter</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">empty</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">default_value</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">computed</text></svg>
&nbsp;&nbsp;</th><th><svg xmlns="http://www.w3.org/2000/svg"><text transform="matrix(0,-1,1,0,12,108)">only_masters</text></svg>
&nbsp;&nbsp;</th></tr><tr><td>masters</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>
function(value)
{
if ( value.join )
  {
   teachers = value ;
   value = value.join(' ') ;
  }
else
  {
   teachers = value.split(/ +/) ;
  }
if ( teachers.length )
    i_am_the_teacher = myindex(teachers, my_identity) != -1 ;
else
    i_am_the_teacher = false ;
return value ;
}</td><td>function(value) { return value === "" ; }</td><td>[]</td><td>0</td><td>0</td></tr>
<tr><td>abj</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td></td><td>0</td><td>0</td></tr>
<tr><td>autosave</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>1</td><td>0</td><td>0</td></tr>
<tr><td>bookmark</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>1</td><td>0</td><td>0</td></tr>
<tr><td>code</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td></td><td>0</td><td>0</td></tr>
<tr><td>comment</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td></td><td>0</td><td>0</td></tr>
<tr><td>dates</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>date_formatter</td><td>function(value) { return value === "" ; }</td><td>[0, 2000000000]</td><td>0</td><td>0</td></tr>
<tr><td>default_nr_columns</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>0</td><td>0</td><td>0</td></tr>
<tr><td>default_sort_column</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>0</td><td>0</td><td>0</td></tr>
<tr><td>facebook</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td></td><td>0</td><td>0</td></tr>
<tr><td>forms</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>1</td><td>0</td><td>0</td></tr>
<tr><td>linear</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>1</td><td>0</td><td>0</td></tr>
<tr><td>mail</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td></td><td>0</td><td>0</td></tr>
<tr><td>mails</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>{}</td><td>1</td><td>0</td></tr>
<tr><td>modifiable</td><td>0</td><td>0</td><td>1</td><td>0</td><td>1</td><td>table_modifiable_toggle</td><td>function(value) { return value === "" ; }</td><td>1</td><td>0</td><td>0</td></tr>
<tr><td>nr_columns</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>0</td><td>0</td><td>0</td></tr>
<tr><td>nr_lines</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>0</td><td>0</td><td>0</td></tr>
<tr><td>official_ue</td><td>0</td><td>0</td><td>1</td><td>0</td><td>1</td><td>function(v){return v;}</td><td>function(value) { return value === "" ; }</td><td>0</td><td>0</td><td>0</td></tr>
<tr><td>portails</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>{}</td><td>1</td><td>0</td></tr>
<tr><td>print</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td></td><td>0</td><td>0</td></tr>
<tr><td>private</td><td>0</td><td>0</td><td>1</td><td>0</td><td>1</td><td>
function(value)
{
  if ( (table_attr.masters.length == 0 || ! i_am_the_teacher) &amp;&amp; value == 1
       &amp;&amp; ! i_am_root )
    {
      alert('Vous ne pouvez pas rendre cette table privée car\nvous ne pourriez plus la voir.\nCommencez par vous ajouter comme étant\nun des responsable de cette table') ;
      return ;
    }
  return value ;
}</td><td>function(value) { return value === "" ; }</td><td>0</td><td>0</td><td>0</td></tr>
<tr><td>statistics</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td></td><td>0</td><td>0</td></tr>
<tr><td>t_copy</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>1</td><td>0</td><td>0</td></tr>
<tr><td>t_export</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>1</td><td>0</td><td>0</td></tr>
<tr><td>t_import</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>1</td><td>0</td><td>0</td></tr>
<tr><td>table_title</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td></td><td>0</td><td>0</td></tr>
<tr><td>update_content</td><td>0</td><td>0</td><td>0</td><td>0</td><td>1</td><td>function(value) { return value ; }</td><td>function(value) { return value === "" ; }</td><td>1</td><td>0</td><td>0</td></tr>
</tbody></table>

    <h2><a name="serverthreads">TOMUSS server side threads</a></h2>
    <p>
The server need informations from other servers,
in order to be responsive, the requests must be
performed in threads.
To simplify the program, there is a single thread per
data type to be updated.
</p>
<ul>
<li> <b>authentication_thread</b>: It verifies the user identity
and redirect the browser if it is not known.</li>
<li> <b>check_send_queue</b>: In order to not block the server if a client
is slow to receive the data, the send action is performed in a thread.
The thread read a FIFO to get its job.</li>
<li> <b>check_new_students</b>: The thread read a FIFO to get its job.
It launches the 'check' function of the UE template in order to update
the student list and informations.</li>
<li> <b>check_students_in_tables</b>: periodicaly push job to the 'check_new_students'
thread. The push interval is <tt>configuration.students_check_interval</tt>
</li>
<li> <b>check_requests</b>: The thread read a FIFO to get its job.
It's this thread that perform all actions on a table.
The requests stays in the FIFO if there is a missing request.
It may happen because image loading in the browser is not sequential.</li>
    </ul>

<p>
Some external processes launched by <tt>crontab</tt>
compute some files periodicaly.
TOMUSS reread the file is they appear to be modified.
</p>

    <h2><a name="client">TOMUSS client side</a></h2>
    <p>
A maximum of work is done by the client,
for exemple, average and other computation are not
done by the server.
</p>


    <h3><a name="clienthome">The home page</a></h3>

    <p>
The page is generated by the 'home2' plugin.
The client side knows the full UE list and the UFR of the user,
so it can automaticaly filter the interestings UE.
</p>
<p>
Most of the URL are computed with a small javascript
function who take the current semester/year from the user selection
and append the current ticket.
</p>
<p>
The page template is stored in '<tt>top2.html</tt>' file.</p>

    <h3><a name="clientabj">The ABJ/DA editing page</a></h3>

    <p>
The page stay open in order to receive server information.
The client load image to ask things to the server.
the server answer is a JavaScript program updating the screen data.
</p>
<p>
The clients files are <tt>abj.html</tt> and <tt>abj.js</tt>
</p>

    <h3><a name="clienttable">The table editing page</a></h3>

    <p>
The client load the page that stay open in order to receive updates
as JavaScript codes.
The client actions are done by inserting image,
the image is inserted where the user interaction took place
and under the table.
It is done so because, an image must stay visible when the user
change of page or filter the table.
The image is at the same time the server feedback indicating
if the action performed well.
</p>
<p>
The server, indicate that the action was performed with
a javascript code.
When the client receive this code, it removes the matching
images from under the table.
In the normal case, there is no image under the table.
</p>
<p>
If an image is not loaded after some time, the image URL
is modified in order to retry the aborted load because
navigator do not retry failed loads.
</p>
<p>
If image loads fails and the server is active, then we assume
that communication fails because the ticket is no more valid
(IP change for example).
In this case, the user is asked to authenticate once more.
</p>


    <h3><a name="clientobjects">Client side objects</a></h3>
    <p>
As TOMUSS started as a trivial program,
it was not developed with JavaScript objects.
The only objects are:</p>
<ul>
<li> '<b>UE</b>': matches the informations from the Python UE object.</li>
<li> '<b>Cell</b>': The table cell.</li>
<li> '<b>Current</b>': The management of the current cell in the screen.</li>
<li> '<b>Stats</b>': An object allowing to compute statistics.</li>
<li> '<b>Request</b>': Allow fiable communication with the server.</li>
<li> '<b>types[]</b>': Defines all the columns types.</li>
<li> '<b>columns[]</b>': Defines the columns, needs methods to be really an object.</li>
    </ul>
<p>
A Table object should be added in order to clean the code.
</p>

    <h3><a name="virtualtables">Virtual tables</a></h3>
    <p>
These tables are not stored into files.
They can be&nbsp;:
</p>
    <ul>
      <li> Server side : See '<b>PLUGIN/resume.py</b>' as a template.</li>
      <li> Client side : See '<b>FILES/lib.js/statistics_per_group</b>' as a template.</li>
    </ul>

    <h2><a name="administration">TOMUSS Administration</a></h2>

    <h3><a name="semester">Semesters</a></h3>

    <p>
      TOMUSS retrieves the <b>current student list</b> without
      knowing to which semester it apply.
    </p>

    <p>
      The procedure to change of semester is the following:
    </p>

    <table border="1">
      <tr><th>When</th><th>What</th></tr>
      <tr>
	<td>
	  Before the first removal of students from any list.
	</td>
	<td>
	  Set '<tt>allow_student_removal</tt>' to <tt>False</tt>
	  in the TOMUSS configuration table.
	</td>
      </tr>

      <tr>
	<td>
	  New lists of students are accessible.
	</td>
	<td>
	  In your local configuration
	  file, add the new semester with a new port number :<br/>
	  <tt>suivi.add(2009, 'Automne'  , socket.getfqdn() + ':%d', 8891)</tt>
	</td>
      </tr>
      <tr>
	<td>
	  When it is possible
	</td>
	<td>
          Copy the 'referents_students' table in the new semester.<br/>
	    <p>
	  Restart TOMUSS: <tt>make stop ; make</tt>
</p>
	</td>
      </tr>
      <tr>
	<td>
	  When the users are allowed to use the new semester.
	</td>
	<td>
	  Set the next semester name in '<tt>year_semester_next</tt>'
	  in the TOMUSS configuration table.
	  <p>
	    The tables with more than 15% student removal are editable
	    in both semesters.
	    In the next semester the student list is fully synchronized.
	  </p>
	</td>
      </tr>
      <tr>
	<td>
	  When nobody need to modify old semester table.
	</td>
	<td>
	  Set '<tt>year_semester</tt>' to the same value than
	  '<tt>year_semester_next</tt>'.
	  <p>
	    Set '<tt>allow_student_removal</tt>' to <tt>True</tt>
	    in the TOMUSS configuration table.
	  </p>
	</td>
      </tr>
    </table>

    <h3><a name="templates">Table templates</a></h3>

    <p>Table templates are stored in 'TEMPLATES' directory.
     When loading a page, if the file match a template it is applied,
     if the semester match a template it is applied.
Only one template can be applied.
    The templates may defines&nbsp;:
</p>
    <ul>
      <li> '<b>create</b>': How the table is initialized on creation.</li>
      <li> '<b>init</b>': A hook called before table is loaded.</li>
      <li> '<b>onload</b>': A hook called after table is loaded.</li>
      <li> '<b>content</b>': A function returning the content to be appended to web page.
           It is mostly JavaScript redefinition and content update.</li>
      <li> '<b>check</b>': A function updating the student list in the table.</li>
      <li> '<b>cell_change</b>', <b>column_change</b>: A function called on cell content change or column_change.
 It must not use external services because it must return quickly.
If this function raise an exception, the value will not be stored in the table.
</li>
    </ul>
    <p>
     Semester templates defined are:
</p>
    <ul>
      <li> '<b>Printemps</b>', '<b>Automne</b>','<b>Test</b>' are
          standard table to enter data about students.</li>
      <li> '<b>Preferences</b>' is the standard table to enter user
         preferences.</li>
      <li> '<b>Referents</b>' is the definition of the
         referent teacher notepad.</li>
      <li> '<b>Favoris</b>' is the definition of the
         favoris notepad.</li>
      <li> '<b>Dossiers</b>' is the definition of an empty table
         modifiable only by the user that created it.</li>
      <li> '<b>Test</b>' this semester contains real students but
           it is a sandbox allowing to try anything.</li>
    </ul>
    <p>
     Table templates defined are:
</p>
    <ul>
      <li> '<b>config_table</b>' the TOMUSS configuration.</li>
      <li> '<b>config_plugin</b>' the TOMUSS plugin access list.</li>
      <li> '<b>ticket</b>' store the live tickets.</li>
      <li> '<b>FAQ</b>', '<b>wishlist</b>' are
       stored in year 0 and allow a user/developper interaction.</li>
      <li> '<b>tt</b>' create the table used to enter TT.</li>
      <li> '<b>average</b>' create a test table about averages.</li>
    </ul>
    <p>
The table template must be created in order to match
the teaching organisation.
</p>
<p>
Do not store information in the module itself because it will be
lost when the TEMPLATE is reloaded (it is triggered by a file change).
</p>
<p>
The recommended way to define columns in a template is the table
method <tt>update_columns</tt>.
It will create and update columns definitions.
</p>

    <h3><a name="authentication">Authentication process</a></h3>

    <p>
      The URLs start by a ticket.
      If there is no ticket, the navigator is redirected to a service
      in order to get a ticket.
      <em>If CAS is not used, then a random ticket number is generated.</em>
    </p>
    <p>
      The tickets are ``exchanged´´ between the TOMUSS processes
      as a python module containing all the valid tickets.
    </p>
    <p>
      To be valid, a ticket must be used with the same navigator
      and the same IP than the first time.
    </p>
    <p>
      'ticket.py' define the ticket object that store tickets
      and parse URLs. It is a generic and not configuration dependant.
    </p>
    <p>
      A new ticket can be used to revalidate an old ticket,
      it is useful if the client IP changed.
    </p>
    <p>
      'authentication.py' can be redefined by a local plugin
      in order to have the good code for the functions
      'ticket_login_name' and 'ticket_ask'.
      <em>If CAS is not used, then these functions assume that
	the authentication is done with Apache/.htaccess/.htpasswd
	with the <tt>Basic</tt> authentication method
	(the clear password is checked with <tt>su</tt>)</em>
    </p>
    <p>
      In <tt>regtest</tt> mode, the URLs accept <tt>=username</tt>
      as a valid ticket for the user.
    </p>

    <h3><a name="apache">Apache/NGINX configuration</a></h3>

    <p>
      TOMUSS urls are for example:
    </p>
    <ul>
      <li> <tt>http://tomuss1.fr:8888/</tt> The main process</li>
      <li> <tt>http://tomuss2.fr:8889/</tt> 'Suivi' for Automne 2008</li>
      <li> <tt>http://tomuss3.fr:8890/</tt> 'Suivi' for Printemps 2009</li>
    </ul>
    <p>
      To have TOMUSS working with nicer URLs we can configure Apache as:
    </p>
      <pre>&lt;VirtualHost tomuss.fr:80&gt;
ServerName tomuss.univ-lyon1.fr
RewriteEngine On
RewriteRule ^(.*) http://tomuss1.fr:8888$1 [P]
&lt;/VirtualHost&gt;

&lt;VirtualHost tomusss.fr:80&gt;
ServerName tomusss.univ-lyon1.fr
RewriteEngine On
RewriteRule ^(.*/2009/Printemps/.*) http://tomuss3.fr:8890$1 [P]
RewriteRule ^(.*/2008/Automne/.*)   http://tomuss2.fr:8889$1 [P]
# 'Suivi' on the current semester
RewriteRule ^(.*)                   http://tomuss2.fr:8889$1 [P]
&lt;/VirtualHost&gt;
</pre>

    <p>
     We can configure NGINX with:</p>
<pre>proxy_buffering off;         # To be really interactive
proxy_read_timeout 1000000;  # To keep connection open

server {
    listen   80;
    server_name  tomuss.fr;
    location    /                       { proxy_pass http://tomuss1.fr:8888; }
}

server {
  listen   80;
  server_name  tomusss.fr;
  location    /         { proxy_pass http://tomuss3.fr:8890/2009/Printemps/; }
  location ~ ^/(=[^/]*/)?2009/Printemps { proxy_pass http://tomuss3.fr:8890; }
  location ~ ^/(=[^/]*/)?2008/Automne   { proxy_pass http://tomuss2.fr:8889; }
}
</pre>

    <p>
      With this, the URLs become:
    </p>
    <ul>
      <li> <tt>http://tomuss.fr/</tt> The main process</li>
      <li> <tt>http://tomusss.fr/2008/Automne/</tt> 'Suivi' for Automne 2008</li>
      <li> <tt>http://tomusss.fr/2009/Printemps/</tt> 'Suivi' for Printemps 2009</li>
    </ul>

    <h3><a name="configuration">TOMUSS configuration</a></h3>
    <p>
The initial TOMUSS configuration is stored in <tt>configuration.py</tt>.
To avoid mistakes, <tt>DB</tt> is the production database name and
<tt>DBtest</tt> is the development database.
<tt>configuration.py</tt> tests the host name in order to
to setup a production or a development environment.
</p>
<p>
<b><tt>configuration.py</tt> must not be modified because
it will be overwrited by each TOMUSS release.</b>
The file <tt>LOCAL/__init__.py</tt> must be edited
to customize and configure TOMUSS.
</p>
<p>
This customization is done by replacing default functions by yours.
For the UCBL university there is:</p>
<pre># The login and the student ID are not the same at the UCBL
# login_to_student_id, the_login, login_to_id (JavaScript)
import LOCAL.student_id

# get_ue_dict: Retrieve all the informations about all the UE
import LOCAL.spiral

if not regtest:
    # ticket_login_name: Get login name from ticket
    # ticket_ask: redirect the browser to ask a ticket
    import LOCAL.auth

    # stupid_password: Returns True if the password is stupid
    import LOCAL.checkpassword

    # students: Iterator on all the students of an UE, returns :
    # (student_id, firstname, surname, mail, group, sequence)
    import LOCAL.students_of_ue
</pre>
    <p>
Most of the configuration values are modifiable while TOMUSS
is running by editing as 'root' the table named 
'<tt>http://......../0/Dossiers/config_table</tt>'
The values in this table have precedence over values stored
in the configuration source files.
Nevertheless the first time, this table is created using the
configuration source files
</p>


    <h3><a name="regtests">TOMUSS Regressions Tests</a></h3>

    <p>
The makefile goal <tt>regtest</tt> run an infinite loop on some server tests.
The loop is broken if there is a problem.
</p>

<p>
The <tt>URL http://SERVER/2009/Test/javascript_regtest_ue</tt>
run some javascripts tests on the user interface.
These tests works on FireFox and Opera but not on IE.
</p>


    <h3><a name="startstop">TOMUSS starting and stopping</a></h3>

<p>The makefile goals <tt>start</tt> and <tt>stop</tt> allow to
manage the TOMUSS services.
There is one database modification service and one 'suivi' service
per semester in order to not have huge processes.
The 'suivi' processes are huge because they load the full semester
in memory.</p>

    <h3><a name="install">TOMUSS install on production server</a></h3>

    <p>
The goal <tt>install</tt> of the <tt>Makefile</tt> runs
the script <tt>install</tt> that do all the work to replace
a running TOMUSS by the new release.
</p>
    <ul>
      <li> The script contains the two directories where TOMUSS programs
and databases must be copied.</li>
      <li> At any time, the current version can be stopped and an older
restarted because all versions are kept.</li>
      <li> When running, the two databases are perfectly synchronized.</li>
      <li> When installing a new version, the synchronisation is verified.</li>
      <li> If the active directory is no more accessible, the backup directory is usable without modification.</li>
      <li> The installation process configure <tt>crontab</tt> in order to:
<ul>
	  <li> restart automaticaly stopped programs,</li>
	  <li> make daily backup,</li>
	  <li> create statistics charts,</li>
	  <li> update 'xxx_toute_les_ues.py', 'all_ues.js'.</li>
    </ul>
</li>
    </ul>
<p>
   Two symbolic links (<tt>DB</tt> and <tt>BACKUP_DB</tt>) points
   on directory where the database is stored.
</p>
<p>
   Required packages : python-ldap, python-imaging, gzip
</p>
<p>
   Recommended packages : gnuplot, graphviz, rsync (distant mirroring), catdoc (csv extract from xls)
</p>


    <h3><a name="manage">TOMUSS managing</a></h3>

    <p>
The TOMUSS <tt>root</tt> can use the following features:
</p>
    <ul>
<li> Interactively browse the python server object memory.</li>
<li> Compute the favorite list for the semester once there is some data.</li>
<li> Remove tables without any user generated content.</li>
<li> See the live log of server actions.</li>
<li> See the time statistics about the servers functions.</li>
<li> See the tickets alive.</li>
    </ul>
    <p>
Some non automatic work to do: see <tt>LOCAL/Makefile</tt>
</p>

    <h2><a name="pitfall">TOMUSS Pitfall</a></h2>

    <p>When loading a Python module, never store the 'configuration'
values in local variables because the configuration module may
not be fully loaded.</p>

    <p>Use the function 'unload_module' to unload a Python module
in order to not have a memory leak.</p>

    <p>Never modify the database files if the table is loaded in the
TOMUSS server.</p>

    <h2><a name="local">Functions to redefine in order to customize TOMUSS</a></h2>

    <p>
     If you want your TOMUSS customization to not be destroyed
     by a version change you must follow the procedure.
     The functions listed are the ones that you need to modify,
     but you can modify any function you want.
</p>

    <table><tr><td><p>
	In the following table the javascript functions must not be
modified in the javascript source, they must be redefined
by <tt>LOCAL/__init__.py</tt> script using this procedure:
</p>

<pre>import files

files.files['lib.js'].append("a_key", """
function the_function_to_be_redefined()
{
}
"""</pre>

    </td><td><p>
 The Python function must not be modified in the Python sources,
they must be redefined in <tt>LOCAL/__init__.py</tt>
script using this procedure:
</p>
<pre>import a_module

old_one = a_module.to_be_redefined

def to_be_redefined():
   old_one()

a_module.to_be_redefined = to_be_redefined
</pre></td></tr></table>



<table border="1">
<tr><td>utilities.py</td><td><tt><small>def&nbsp;the_login(student):<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;safe(student)
<br/>
<br/></small></tt></td><td> If the student login in LDAP is not the same as the student ID.  This function translate student ID to student login.  The returned value must be usable safely. </td></tr>
<tr><td>utilities.py</td><td><tt><small>def&nbsp;stupid_password(login,&nbsp;passwords):<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;False
<br/>
<br/></small></tt></td><td> This function returns True if the user uses a stupid password.  Potential stupid passwords are in the 'passwords' list.  Each of the passwords should be tried to login,  if the login is a success, the password is bad. </td></tr>
<tr><td>document.py</td><td><tt><small>def&nbsp;table_head_more(ue):<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;''
<br/>
<br/></small></tt></td><td> This function returns javascript code to be included  in the header of the 'ue' table </td></tr>
<tr><td>authentication.py</td><td><tt><small>def&nbsp;password_is_good(login,&nbsp;password):<br/>&nbsp;&nbsp;&nbsp;&nbsp;import&nbsp;pexpect
<br/>&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;=&nbsp;pexpect.spawn('/bin/su&nbsp;-c&nbsp;"echo&nbsp;OK"&nbsp;%s'&nbsp;%&nbsp;utilities.safe(login))
<br/>&nbsp;&nbsp;&nbsp;&nbsp;p.expect(':')
<br/>&nbsp;&nbsp;&nbsp;&nbsp;p.sendline(password)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;r&nbsp;=&nbsp;p.read()
<br/>&nbsp;&nbsp;&nbsp;&nbsp;p.close()
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;'OK'&nbsp;in&nbsp;r
<br/>
<br/></small></tt></td><td> Return True if the user password is good </td></tr>
<tr><td>authentication.py</td><td><tt><small>def&nbsp;ticket_login_name(ticket_key,&nbsp;service,&nbsp;server=None):<br/>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;not&nbsp;configuration.cas:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Not&nbsp;CAS:&nbsp;assume&nbsp;Apache&nbsp;and&nbsp;.htaccess
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;auth&nbsp;=&nbsp;server.headers['authorization'].split('&nbsp;')[1].decode('base64')
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;login,&nbsp;password&nbsp;=&nbsp;auth.split(':',&nbsp;1)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;password_is_good(login,&nbsp;password):
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;login
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;False
<br/>
<br/></small></tt></td><td> From the CAS ticket and the service required by client,  returns the login name of the user. </td></tr>
<tr><td>authentication.py</td><td><tt><small>def&nbsp;ticket_ask(server,&nbsp;server_url,&nbsp;service):<br/>&nbsp;&nbsp;&nbsp;&nbsp;service&nbsp;=&nbsp;canonize(service)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;server.send_response(307)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;not&nbsp;configuration.cas:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Assume&nbsp;you&nbsp;are&nbsp;using&nbsp;.htaccess&nbsp;and&nbsp;Apache
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;import&nbsp;random
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server.send_header('Location',&nbsp;service&nbsp;+&nbsp;'?ticket='
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;str(random.randrange(1000000000000,
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10000000000000))&nbsp;)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;else:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server.send_header('Location',
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'%s/login?service=%s'&nbsp;%&nbsp;(
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;configuration.cas,
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;service))
<br/>&nbsp;&nbsp;&nbsp;&nbsp;server.end_headers()
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;None,&nbsp;None
<br/>
<br/></small></tt></td><td> This function is called when the browser does not give a valid ticket.  The browser is redirected on the CAS authentification service. </td></tr>
<tr><td>authentication.py</td><td><tt><small>def&nbsp;get_path(server,&nbsp;server_url):<br/>&nbsp;&nbsp;&nbsp;&nbsp;
<br/></small></tt></td><td> If the user is connected, the function returns the ticket object  and a clean path.  If the user is not connected, it is redirected to the  authentication service. </td></tr>
<tr><td>plugins.py</td><td><tt><small>def&nbsp;plugins_tomuss_more():<br/>&nbsp;&nbsp;&nbsp;&nbsp;pass
<br/>
<br/></small></tt></td><td> This function do the import of LOCAL Plugins for the TOMUSS server </td></tr>
<tr><td>plugins.py</td><td><tt><small>def&nbsp;plugins_suivi_more():<br/>&nbsp;&nbsp;&nbsp;&nbsp;pass
<br/>
<br/></small></tt></td><td> This function do the import of LOCAL Plugins for the 'suivi' server </td></tr>
<tr><td>referent.py</td><td><tt><small>def&nbsp;port():<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;configuration.the_portails['UFRFST']&nbsp;
<br/>
<br/></small></tt></td><td> List of LDAP OU of student to be affected to a 'referent' </td></tr>
<tr><td>referent.py</td><td><tt><small>def&nbsp;not_in():<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;()
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br/></small></tt></td><td> List of LDAP OU of student not to be affected to a 'referent' </td></tr>
<tr><td>referent.py</td><td><tt><small>def&nbsp;need_a_referent(login):<br/>&nbsp;&nbsp;&nbsp;&nbsp;"""To&nbsp;be&nbsp;redefined"""
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;True
<br/>
<br/></small></tt></td><td> Return True if the student need a referent teacher </td></tr>
<tr><td>referent.py</td><td><tt><small>def&nbsp;need_a_charte(login):<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;False
<br/>
<br/></small></tt></td><td> Return True if the student must sign an interactive document </td></tr>
<tr><td>referent.py</td><td><tt><small>def&nbsp;student_list(f,&nbsp;pportails,&nbsp;not_in_list):<br/>&nbsp;&nbsp;&nbsp;&nbsp;f.write('&lt;h1&gt;Get&nbsp;the&nbsp;student&nbsp;list&nbsp;for&nbsp;UEs&lt;/h1&gt;\n')
<br/>&nbsp;&nbsp;&nbsp;&nbsp;students&nbsp;=&nbsp;{}
<br/>&nbsp;&nbsp;&nbsp;&nbsp;print&nbsp;pportails
<br/>&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;portail&nbsp;in&nbsp;pportails:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.write('&lt;h2&gt;'&nbsp;+&nbsp;portail&nbsp;+&nbsp;'&lt;/h2&gt;')
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.flush()
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;sstudent&nbsp;in&nbsp;inscrits.L_batch.member_of(portail):
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.write(sstudent[0])
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;p&nbsp;in&nbsp;sstudent[4]:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;p&nbsp;in&nbsp;not_in_list:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Not&nbsp;in&nbsp;any&nbsp;forbidden&nbsp;list
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.write('&nbsp;')
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.flush()
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s&nbsp;=&nbsp;Student(sstudent,&nbsp;portail=portail)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;not&nbsp;s.licence:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.write('(!L)&nbsp;')
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;len(s.ues)&nbsp;==&nbsp;0:&nbsp;#&nbsp;Aucune&nbsp;IP&nbsp;!!!!
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.write('(!IP)&nbsp;')
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;XXX&nbsp;continue
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;students[sstudent[0]]&nbsp;=&nbsp;s
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.write('(L3)&nbsp;')
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br/></small></tt></td><td> Compute the student list needing a referent </td></tr>
<tr><td>referent.py</td><td><tt><small>def&nbsp;analyse_groups(student,&nbsp;groups):<br/>&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Update&nbsp;list&nbsp;of&nbsp;the&nbsp;UE&nbsp;of&nbsp;the&nbsp;student
<br/>&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;ue&nbsp;in&nbsp;groups:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;ue.startswith(configuration.ou_ue_starts):
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;student.ues.append(ue[6:].split('&nbsp;')[0])
<br/>
<br/></small></tt></td><td> This function computes for the student some attributes needed to  assign it a referent teacher.  Attributes are :  discipline (set), ues (list), licence, licence_first_year </td></tr>
<tr><td>referent.py</td><td><tt><small>def&nbsp;remove_student_from_referent_hook(referent,&nbsp;student_id):<br/>&nbsp;&nbsp;&nbsp;&nbsp;return
<br/>
<br/></small></tt></td><td> Triggered when a student is removed from a teacher </td></tr>
<tr><td>referent.py</td><td><tt><small>def&nbsp;add_student_to_referent_hook(referent,&nbsp;student_id):<br/>&nbsp;&nbsp;&nbsp;&nbsp;return
<br/>
<br/></small></tt></td><td> Triggered when a student is added to a teacher </td></tr>
<tr><td>referent.py</td><td><tt><small>def&nbsp;search_best_teacher_local(student,&nbsp;sorted_teachers,&nbsp;f,&nbsp;all_teachers):<br/>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;'INFO_L3'&nbsp;in&nbsp;student.discipline&nbsp;and&nbsp;student.primo_entrant:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;all_teachers['elodie.desseree']
<br/>
<br/></small></tt></td><td> This function returns the teacher to assign to a student.  It is only needed for special case of the generic algorithm. </td></tr>
<tr><td>referent.py</td><td><tt><small>def&nbsp;student_need_a_referent(student,&nbsp;all_cells,&nbsp;debug_file):<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;True
<br/>
<br/></small></tt></td><td> Returns True if the student need a referent.  This function can heavely modify 'all_cells' and other data  to make some adjustement. </td></tr>
<tr><td>inscrits.py</td><td><tt><small>class&nbsp;LDAP(LDAP_Logic):<br/></small></tt></td><td> This class can be replaced by a subclass of itself in order to replace  the 'students' method returning the student list for an UE.  The student list may be computed without using LDAP. </td></tr>
<tr><td>inscrits.py</td><td><tt><small>def&nbsp;login_to_student_id(login):<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;utilities.safe(login)
<br/>
<br/></small></tt></td><td> If the student login in LDAP is not the same as the student ID.  This function translate student login to student ID.  The returned value must be usable safely. </td></tr>
<tr><td>abj.py</td><td><tt><small>def&nbsp;prune_abjs(abj_list,&nbsp;group,&nbsp;sequence,&nbsp;ue_code):<br/>&nbsp;&nbsp;&nbsp;&nbsp;"""Trim&nbsp;unecessary&nbsp;ABJS"""
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;abj_list
<br/>
<br/></small></tt></td><td> When a student has an ABJ it may be outside of UE schedule.  The function returns the ABJs without ABJ outside of UE planning. </td></tr>
<tr><td>teacher.py</td><td><tt><small>def&nbsp;get_ue_dict():<br/>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;configuration.ldap_server[0]&nbsp;!=&nbsp;'ldap1.domain.org':
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ldap_ues&nbsp;=&nbsp;inscrits.L_batch.get_ldap_ues()
<br/>&nbsp;&nbsp;&nbsp;&nbsp;else:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ldap_ues&nbsp;=&nbsp;()
<br/>&nbsp;&nbsp;&nbsp;&nbsp;ues&nbsp;=&nbsp;{}
<br/>&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;apoge&nbsp;in&nbsp;ldap_ues:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ues[apoge]&nbsp;=&nbsp;UE(apoge,&nbsp;[],&nbsp;'UNKNOWN&nbsp;TITLE',
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[],&nbsp;0,&nbsp;[],&nbsp;1,&nbsp;1,&nbsp;[],&nbsp;0)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;range(1,10):
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name&nbsp;=&nbsp;"UE%d"&nbsp;%&nbsp;i
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ues[name]&nbsp;=&nbsp;UE(
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name,&nbsp;#&nbsp;UE&nbsp;Name
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;['ue%d.master'&nbsp;%&nbsp;i],#&nbsp;Teacher&nbsp;names
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u'UE%d&nbsp;Title'&nbsp;%&nbsp;i,&nbsp;&nbsp;#&nbsp;UE&nbsp;title
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[],&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;Departments&nbsp;of&nbsp;UE
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1000+i,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;SPIRAL&nbsp;key&nbsp;for&nbsp;the&nbsp;UE
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;['ue%d.master'&nbsp;%&nbsp;i],#&nbsp;Login&nbsp;of&nbsp;teachers
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;#students&nbsp;registered
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;#students&nbsp;registered&nbsp;in&nbsp;EC
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;['ue%d_test@test.org'&nbsp;%&nbsp;i],&nbsp;#&nbsp;Teachers&nbsp;mails
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;ADE&nbsp;key&nbsp;for&nbsp;the&nbsp;UE
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)&nbsp;
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ues
<br/>
<br/></small></tt></td><td> Returns the UE list from LDAP or other sources.  This function is called once per night,  so it can be long to execute.  The function result is stored as a Python and JavaScript file </td></tr>
<tr><td>configuration.py</td><td><tt><small>def&nbsp;semester_span(year,&nbsp;semester):<br/>&nbsp;&nbsp;&nbsp;&nbsp;try:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i&nbsp;=&nbsp;semesters.index(semester)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;except&nbsp;ValueError:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return
<br/>&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;p(month):
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;overflow&nbsp;=&nbsp;int(month&nbsp;&gt;&nbsp;12)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;'%d/%d'&nbsp;%&nbsp;(month&nbsp;-&nbsp;overflow*12,&nbsp;year&nbsp;+&nbsp;overflow)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;'1/%s&nbsp;31/%s'&nbsp;%&nbsp;(p(semesters_months[i][0]),
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p(semesters_months[i][1]))
<br/>
<br/></small></tt></td><td> Time span of the given semester </td></tr>
<tr><td>configuration.py</td><td><tt><small>def&nbsp;student_in_first_year(login):<br/>&nbsp;&nbsp;&nbsp;&nbsp;import&nbsp;inscrits
<br/>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;login[1:3]&nbsp;==&nbsp;current_year:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;group&nbsp;in&nbsp;inscrits.L_batch.member_of_list(login):
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;'1A,OU='&nbsp;in&nbsp;group:
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;True
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;False
<br/>
<br/></small></tt></td><td> Returns True if the student is in the first year.  The information is displayed in the 'blocnote' </td></tr>
<tr><td>configuration.py</td><td><tt><small>def&nbsp;picture(student_id,&nbsp;ticket):<br/>&nbsp;&nbsp;&nbsp;&nbsp;import&nbsp;utilities
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(utilities.StaticFile._url_&nbsp;+&nbsp;'/='&nbsp;+&nbsp;ticket.ticket
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;'/picture/'&nbsp;+&nbsp;student_id&nbsp;+&nbsp;'.JPG')
<br/>
<br/></small></tt></td><td> This function returns the URL of the student picture.  This example assumes that TOMUSS itself send pictures. </td></tr>
<tr><td>configuration.py</td><td><tt><small>def&nbsp;more_on_suivi(student_login):<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;''
<br/>
<br/></small></tt></td><td> This function returns a string inserted into student suivi page </td></tr>
<tr><td>configuration.py</td><td><tt><small>def&nbsp;external_bilan(login):<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;"[]"
<br/>
<br/></small></tt></td><td> To add external information in the 'bilan'  This function is used by PLUGINS/bilan.py to get external information </td></tr>
<tr><td>configuration.py</td><td><tt><small>def&nbsp;suivi_check_student_lists(login):<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;True
<br/>
<br/></small></tt></td><td> Returns True to display the UE name if the student is registered for the UE  but is not in the TOMUSS table (because it is uncreated for example) </td></tr>
<tr><td>FILES/lib.js</td><td><tt><small>function&nbsp;login_to_id(login)<br/>{
<br/>&nbsp;&nbsp;return&nbsp;login&nbsp;;
<br/>}
<br/></small></tt></td><td>If LDAP students login and real students ID are not equals then a translation must be done. This function translate the login to a student number. </td></tr>
<tr><td>FILES/lib.js</td><td><tt><small>function&nbsp;the_login(login)<br/>{
<br/>&nbsp;&nbsp;return&nbsp;login&nbsp;;
<br/>}
<br/></small></tt></td><td>If LDAP students login and real students ID are not equals then a translation must be done. This function translate the student number to a login </td></tr>
<tr><td>FILES/lib.js</td><td><tt><small>function&nbsp;do_change_abjs(m)<br/>{
<br/>&nbsp;&nbsp;the_student_abjs&nbsp;=&nbsp;m&nbsp;;
<br/>}
<br/></small></tt></td><td></td></tr>
<tr><td>FILES/middle.js</td><td><tt><small>function&nbsp;change_title()<br/>{
<br/>&nbsp;&nbsp;var&nbsp;p_title_links&nbsp;=&nbsp;document.getElementById('title_links')&nbsp;;
<br/>&nbsp;&nbsp;if&nbsp;(&nbsp;p_title_links&nbsp;==&nbsp;undefined&nbsp;)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;;&nbsp;//&nbsp;Linear&nbsp;interface
<br/>&nbsp;&nbsp;//&nbsp;Use&nbsp;table_attr.code&nbsp;for&nbsp;example
<br/>&nbsp;&nbsp;//&nbsp;p_title_links.innerHTML&nbsp;=&nbsp;'&lt;a&nbsp;href=""&gt;xxx&lt;/a&gt;'&nbsp;;
<br/>}
<br/></small></tt></td><td>This function is called to set the UE title links on the page. </td></tr>
<tr><td>FILES/middle.js</td><td><tt><small>function&nbsp;modification_allowed_on_this_line(line_id,&nbsp;data_col)<br/>{
<br/>&nbsp;&nbsp;if&nbsp;(&nbsp;myindex(semesters,&nbsp;semester)&nbsp;==&nbsp;-1&nbsp;)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;true&nbsp;;
<br/>&nbsp;&nbsp;if&nbsp;(&nbsp;tr_classname&nbsp;===&nbsp;undefined&nbsp;)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;true&nbsp;;
<br/>&nbsp;&nbsp;if&nbsp;(&nbsp;!&nbsp;popup_on_red_line&nbsp;)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;true&nbsp;;
<br/>&nbsp;&nbsp;if&nbsp;(&nbsp;lines[line_id][tr_classname].value&nbsp;==&nbsp;'non'&nbsp;)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;true&nbsp;;&nbsp;//&nbsp;Returns&nbsp;false&nbsp;here&nbsp;to&nbsp;forbid&nbsp;red&nbsp;line&nbsp;editing
<br/>&nbsp;&nbsp;return&nbsp;true&nbsp;;
<br/>}
<br/></small></tt></td><td>Some table lines must not be modified. This function return 'true' to allow the line editing. </td></tr>
<tr><td>FILES/middle.js</td><td><tt><small>function&nbsp;update_student_information(line)<br/>{
<br/>&nbsp;&nbsp;update_student_information_default(line)&nbsp;;
<br/>}
<br/></small></tt></td><td>Template can redefine this function. </td></tr>
<tr><td>FILES/utilities.js</td><td><tt><small>function&nbsp;student_picture_url(login)<br/>{
<br/>&nbsp;&nbsp;if&nbsp;(&nbsp;login&nbsp;)
<br/>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;&nbsp;'_URL_/='&nbsp;+&nbsp;ticket&nbsp;+&nbsp;'/picture/'&nbsp;+&nbsp;login_to_id(login)&nbsp;+&nbsp;'.JPG'&nbsp;;
<br/>&nbsp;&nbsp;return&nbsp;''&nbsp;;
<br/>}
<br/></small></tt></td><td>This function returns the URL of the student picture. </td></tr>
</table>


  </body>
</html>

