Migration des données 4.1 vers 4.3


Avant d'appliquer les migrations Ametys ODF, vous devez appliquer les migrations Ametys CMS 4.2.x vers 4.3.x

Ce guide traite de la migration des données d'une version Ametys ODF 4.1.x à 4.3.0.
Reportez-vous également au guide de migration technique et au guide de migration graphique.   

  1. Fusion des contacts
  2. Workflow de gestion ne versionne pas
  3. Type de période
  4. Champs Apogée migrés en table de référence
  5. Gestion des labels des formations
  6. Approche par compétence

Fusion des contacts

Pour plus de souplesse, les contacts des formations, parcours et ELPs ont été fusionnés dans un seul "repeater" de contacts avec des rôles différents:

  • sur les formations et parcours, le repeater "personInCharge" (Responsables) et le champ multiple "contact" (Contacts administratifs) sont remplacés par le repeater "contacts". Chaque entrée du repeater est composée d'un rôle, et d'un champ multiple pour les personnes
  • sur les ELP, les champs multiples "personInCharge" (Responsables) et "contact" (Contacts administratifs) sont remplacés par le repeater "contacts". Chaque entrée du repeater est composée d'un role, et d'un champs multiple pour les personnes

Exécutez le script suivant pour migrer les contacts de vos formations, parcours et ELP.
Personnalisez si besoin le code et le titre pour le role des contacts administratifs ainsi que le code et le titre pour le role des responsables pédagogique.

// Administrative role (A PERSONNALISER SI BESOIN)                     
var administrativeRoleCode = "contact";                     
var administrativeRoleTitle = new java.util.HashMap();                     
administrativeRoleTitle.put('fr', 'Contact administratif');                     
administrativeRoleTitle.put('en', 'Administrative contact');                     
             
var StringArray = Java.type("java.lang.String[]");                     
             
var administrativeRoleId = _createRoleIfNeeded(administrativeRoleTitle, administrativeRoleCode);                     
             
// Responsable pédagogique pour les ELPs (A PERSONNALISER SI BESOIN)                     
var respPRoleCode = "RespP";                     
var respPRoleTitle = new java.util.HashMap();                     
respPRoleTitle.put('fr', 'Responsable pédagogique');                     
respPRoleTitle.put('en', 'Education manager');                     
             
var respPRoleId = _createRoleIfNeeded(respPRoleTitle, respPRoleCode);                     
             
var t0 = new java.util.Date().getTime();                     
             
java.lang.System.out.println(new java.util.Date() + "----------------------- DEBUT SCRIPT : MIGRATION CONTACTS ----------------------- "                     
    + " en " +((new java.util.Date().getTime()) - t0)/1000.0 + "seconds "                     
    + " - le " + (new java.util.Date())                     
)                     
             
function _createRoleIfNeeded(roleTitle, roleCode)                     
{                     
    var qm = session.getWorkspace().getQueryManager();                     
    var query = qm.createQuery("//element(*, ametys:content)[@ametys-internal:contentType = 'odf-enumeration.PersonRole' and @ametys:code='" + roleCode + "']", javax.jcr.query.Query.XPATH);                     
    var nodes = query.execute().getNodes();                     
    if (nodes.hasNext())                     
    {                     
        print("Found role with code '" + roleCode + "'");                     
        return "content://" + nodes.next().getUUID();                     
    }                     
    else                     
    {                     
        var contentWorkflowHelper= serviceManager.lookup('org.ametys.cms.workflow.ContentWorkflowHelper');                     
        var contentTypes = new StringArray(1);                     
        contentTypes[0] = 'odf-enumeration.PersonRole';                     
        var result = contentWorkflowHelper.createContent("reference-table", 1, roleTitle.get('fr'), roleTitle, contentTypes, new StringArray(0), null, null, new java.util.HashMap());                     
        var roleId = result.contentId;                     
        var content = result.get('org.ametys.cms.repository.Content');                     
        content.getNode().setProperty("ametys:code", roleCode);                     
        contentWorkflowHelper.doAction(content, 22);                     
        session.save();                     
        print("Create new role with code '" + roleCode + "'");                     
        return content.getId();                     
    }                     
}                     
             
function _moveContactToRepeater(content, oldPropertyName, newRoleId)                     
{                     
    if (content.getNode().hasProperty(oldPropertyName))                     
    {                     
        var values = content.getNode().getProperty(oldPropertyName).getValues();                     
        if (values.length > 0)                     
        {                     
            hasChanges = true;                     
             
            var repeaterNode = null;                     
            if (content.getNode().hasNode("ametys:contacts"))                     
            {                     
                repeaterNode = content.getNode().getNode("ametys:contacts");                     
            }                     
            else                     
            {                     
                repeaterNode = content.getNode().addNode("ametys:contacts", "ametys:compositeMetadata");                     
            }                     
             
            var size = repeaterNode.getNodes().getSize();                     
            var entryNode = repeaterNode.addNode("ametys:" + (size+1), "ametys:compositeMetadata");                     
             
            // Move values                     
            entryNode.setProperty("ametys:persons", values);                     
            if (newRoleId)                     
            {                     
                entryNode.setProperty("ametys:role", newRoleId);                     
            }                     
        }                     
             
        // remove old property                     
        content.getNode().getProperty(oldPropertyName).remove();                     
        return true;                     
    }                     
    return false;                     
}                     
             
function _contactMigration(content)                     
{                     
    var hasChanges = false;                     
             
    // Move repeater 'ametys:personInCharge' to 'ametys:contact'                     
    if (content.getNode().hasNode("ametys:personInCharge"))                     
    {                     
        var node = content.getNode().getNode("ametys:personInCharge");                     
        session.move(node.getPath(), node.getParent().getPath() + '/ametys:contacts');                     
        hasChanges = true;                     
    }                     
             
    // Move 'contact' into a new entry of 'contacts' repeater with the administrative role                     
    hasChanges = _moveContactToRepeater(content, "ametys:contact", administrativeRoleId) || hasChanges;                     
             
    if (hasChanges) count++;                     
}                     
             
function _contactMigrationForCourses(content)                     
{                     
    var hasChanges = _moveContactToRepeater(content, "ametys:contact", administrativeRoleId);                     
    hasChanges = _moveContactToRepeater(content, "ametys:personInCharge", respPRoleId) || hasChanges;                     
             
    if (hasChanges) count++;                     
}              
             
var outgoingReferencesExtractor = serviceManager.lookup("org.ametys.cms.content.references.OutgoingReferencesExtractor");        
function _updateOutgoingReferences(content)           
{           
    var outgoingReferencesByPath = outgoingReferencesExtractor.getOutgoingReferences(content);           
    content.setOutgoingReferences(outgoingReferencesByPath);           
    content.saveChanges();           
}                 
             
var totalCount = 0;                     
var count = 0;                     
             
// Migrate programs                     
jcrXPathQuery("//element(*, ametys:programContent)").forEach(function (content) {                     
    migrateContent(content, [_contactMigration], true /* old versions incompatible */, null /* no tag */, false /* not verbose */);                     
_updateOutgoingReferences(content);           
});                     
print(count + " programs have been migrated");                     
totalCount += count;                     
             
// Migrate subprograms                     
count = 0;                     
jcrXPathQuery("//element(*, ametys:subProgramContent)").forEach(function (content) {                     
    migrateContent(content, [_contactMigration], true /* old versions incompatible */, null /* no tag */, false /* not verbose */);                     
    _updateOutgoingReferences(content);           
});                     
print(count + " subprograms have been migrated");                     
totalCount += count;                     
             
// Migrate courses                     
count = 0;                     
jcrXPathQuery("//element(*, ametys:courseContent)[@ametys:contact or @ametys:personInCharge]").forEach(function (content) {                     
    migrateContent(content, [_contactMigrationForCourses], true /* old versions incompatible */, null /* no tag */, false /* not verbose */);                     
    _updateOutgoingReferences(content);           
});                     
print(count + " courses have been migrated");                     
totalCount += count;                     
             
java.lang.System.out.println(new java.util.Date() + "----------------------- FIN SCRIPT : MIGRATION CONTACTS ----------------------- "                     
    + " en " +((new java.util.Date().getTime()) - t0)/1000.0 + "seconds "                     
    + " - le " + (new java.util.Date())                     
)                     
             
if (totalCount > 0)                     
    print(" => BUILD LIVE WORKSPACE");                     

Workflow de gestion ne versionne pas

var ArrayUtils = Java.type("org.apache.commons.lang3.ArrayUtils");                   
             
jcrXPathQuery("//element(*, ametys:programContent)").forEach(function(program) {                   
    var currentVersionIsLive = ArrayUtils.contains(program.getLabels(), "Live");                   
             
    program.checkpoint();                   
             
    // Move the Live label if the last version was validated.                   
    if (currentVersionIsLive)                   
    {                   
        program.addLabel("Live", true);                   
    }                   
});                   

Type de période

Le champ "Type de période" de la table de référence "Période" devient une table de référence.

Pour migrer les données de la table de référence "Période", exécutez le script de migration suivant (il transforme les chaînes de caractères existantes en entrée dans la nouvelle table de référence "Type de période" et fait le lien avec la table de référence "Periode")

// Imports   
var ArrayList = Java.type('java.util.ArrayList');   
var HashMap = Java.type('java.util.HashMap');   
var AbstractWorkflowComponent = Java.type('org.ametys.plugins.workflow.AbstractWorkflowComponent');   
var AbstractContentWorkflowComponent = Java.type('org.ametys.cms.workflow.AbstractContentWorkflowComponent');   

// Components   
var refTableHelper = serviceManager.lookup("org.ametys.odf.enumeration.OdfReferenceTableHelper");   
var workflowHelper = serviceManager.lookup("org.ametys.cms.workflow.ContentWorkflowHelper");   
var workflowProvider = serviceManager.lookup("org.ametys.plugins.workflow.support.WorkflowProvider");   

var count = 0;   

jcrXPathQuery("//element(*, ametys:content)[@ametys-internal:contentType = 'odf-enumeration.Period']").forEach(function(period)   
    {   
        if (updatePeriod(period))   
        {   
            count++;   
        }   
    });   
print("[info] " + count + " periods have been migrated");   

// Update the type field of a period entry from String to Content   
function updatePeriod(period)   
{   
    var periodNode = period.getNode();   
    if (periodNode.hasProperty("ametys:type"))   
    {   
        var periodTypeCode = periodNode.getProperty("ametys:type").getString();   
        if (periodTypeCode.length > 0 && !periodTypeCode.startsWith("content://"))   
        {   
            var periodType = getOrCreatePeriodType(periodTypeCode);   
            if (periodType != null)   
            {   
                period.setValue("type", periodType);   
                period.saveChanges();   
                doAction(period, 2);   
                return true;   
            }   
            else   
            {   
                print("[WARN] Impossible to get or create the period type with the code '" + periodeTypeCode + "'.");   
            }   
        }   
    }   
    return false;   
}   

// Create an entry in the PeriodType table ref   
function getOrCreatePeriodType(code)   
{   
    var periodTypeEntry = refTableHelper.getItemFromCode("odf-enumeration.PeriodType", code);   
    if (periodTypeEntry != null)   
    {   
        return periodTypeEntry.getContent();   
    }   

    var result = workflowHelper.createContent("reference-table", 1, code, code, ["odf-enumeration.PeriodType"], [], "fr");   
    var periodType = result.get(AbstractContentWorkflowComponent.CONTENT_KEY);   
    periodType.setValue("code", code);   
    periodType.saveChanges();   
    doAction(periodType, 22);   
    print("[INFO] Period type entry '" + code + "' created");   
    return periodType;   
}   

// Update the current workflow state   
function doAction(content, actionId)   
{   
    var inputs = new HashMap();   
    inputs.put(AbstractWorkflowComponent.RESULT_MAP_KEY, new HashMap());   
    inputs.put(AbstractContentWorkflowComponent.CONTENT_KEY, content);   
    inputs.put(AbstractWorkflowComponent.FAIL_CONDITIONS_KEY, new ArrayList());   
    inputs.put(AbstractWorkflowComponent.CONTEXT_PARAMETERS_KEY, new HashMap());   

    try   
    {   
        var workflow = workflowProvider.getAmetysObjectWorkflow(content);   
        workflow.doAction(content.getWorkflowId(), actionId, inputs);   
    }   
    catch (e)   
    {   
        print("[ERROR] An error occured while do workflow action '" + actionId + "' on content '" + content.getId() + "'", e);   
    }   
}   

Champs Apogée migrés en table de référence

4 champs liés à l'Export Apogée sont passés de énumération à table de référence :

  • Cycle
  • Régimes d'inscriptions autorisées
  • Liste de CIP
  • Durée de l'étape

Il faut donc passer le script suivant (les données des tables de référence peuvent être adaptées en modifiant les tableaux de script)

const filterNameHelper = Java.type("org.ametys.cms.FilterNameHelper");             
const odfTableRefHelper = Ametys.serviceManager.lookup("org.ametys.odf.enumeration.OdfReferenceTableHelper");             
             
var cycles = {             
'0': 'Pré-universitaire',             
'1': 'Premier cycle',             
'2': 'Deuxième cycle',             
'3': 'Troisième cycle'             
};             
var inscriptions = {             
'1': 'Régime initial',             
'2': 'Régime continu'             
};             
var cips = {             
'A': 'CIP Apogée',             
'G': 'Eco-Gest'             
};             
var durations = {             
'AN': 'Année',             
'PA': 'Pluri-annuelle',             
'SE': 'Semestre',             
'TR': 'Trimestre'             
};             
             
function createTableRef(mapping, contentType)             
{             
    var count = 0;             
    for (var code in mapping)             
    {             
        var item = odfTableRefHelper.getItemFromCode(contentType, code);             
        if (item == null)             
        {             
            var tableRefContent = Content.create({             
                contentTypes: contentType,             
                contentTitle: {             
                    fr: mapping[code],             
                },             
                contentName: filterNameHelper.filterName(mapping[code]),             
                workflowName: "reference-table"             
            });             
            tableRefContent.setValue("code", code);             
            tableRefContent.setValue("codeApogee", code);             
            Content.save(tableRefContent);             
            count++;             
        }             
    }             
             
    print(count + " contenu(s) créé(s) pour la table de référence " + contentType);             
}             
             
createTableRef(cycles, "odf-enumeration.CycleApogee");             
createTableRef(inscriptions, "odf-enumeration.InscriptionType");             
createTableRef(cips, "odf-enumeration.Cips");             
createTableRef(durations, "odf-enumeration.DurationApogee");             
             
print("Migration des programmes");             
migrateContents('org.ametys.plugins.odf.Content.program');             
print("Migration des sous-programmes");             
migrateContents('org.ametys.plugins.odf.Content.subProgram');             
print("Migration des conteneurs");             
migrateContents('org.ametys.plugins.odf.Content.container');             
print("Migration des ELPs");             
migrateContents('org.ametys.plugins.odf.Content.course');             
             
function migrateContents(contentType)             
{             
    var count = {             
        existing: 0,             
        done: 0             
    };             
             
    Repository.query("//element(*, ametys:content)[@ametys-internal:contentType = '" + contentType + "']").forEach(function(content) {             
        count.existing++;             
        Content.migrate(content,             
            migrateODFContent,             
            false,             
            null,             
            false);             
        count.done++;             
    });             
             
    print(count.done + " contents migrated on " + count.existing + " existing contents");            
}             
             
function migrateODFContent(content)             
{             
    migrateSingleAttribute(content, "cycleApogee", "odf-enumeration.CycleApogee");             
    migrateMultipleAttribute(content, "inscription-types", "odf-enumeration.InscriptionType");             
    migrateMultipleAttribute(content, "cips", "odf-enumeration.Cips");             
    migrateSingleAttribute(content, "duration-apogee", "odf-enumeration.DurationApogee");             
}             
             
function migrateSingleAttribute(content, attributeName, attributeContentType)             
{             
    if (content.hasValue(attributeName))             
    {             
        var node = content.getNode();             
        if (node.isLocked())             
        {             
            content.unlock();             
        }             
             
        if (node.hasProperty("ametys:" + attributeName))             
        {             
            var property = node.getProperty("ametys:" + attributeName);             
            var oldVal = property.getString();             
            var item = odfTableRefHelper.getItemFromCode(attributeContentType, oldVal);             
            property.remove();             
            if (item != null)             
            {             
                content.setValue(attributeName, item.getId());             
            }             
        }             
    }             
}            
             
function migrateMultipleAttribute(content, attributeName, attributeContentType)             
{             
    if (content.hasValue(attributeName))             
    {             
        var node = content.getNode();             
        if (node.isLocked())             
        {             
            content.unlock();             
        }             
             
        if (node.hasProperty("ametys:" + attributeName))             
        {             
            var newValues = [];             
            var property = node.getProperty("ametys:" + attributeName);             
            var values = property.getValues();             
            for (var i in values)             
            {             
                var item = odfTableRefHelper.getItemFromCode(attributeContentType, values[i].getString());             
                if (item != null)             
                {             
                    newValues.push(item.getId());             
                }             
            }             
                        
            property.remove();             
            if (newValues.length != 0)             
            {             
                Repository.helper.setProperty(node, attributeName, newValues);             
            }             
        }             
    }             
}             

Gestion des labels des formations

Depuis peu, il est nécessaire d'apposer un label aux formations contrôlée par l'Etat.
La liste des labels existant est imposée par le ministère de l'Enseignement: https://www.enseignementsup-recherche.gouv.fr/cid141235/labels-des-formations-controlees-par-l-etat.html

Initialisation de la table des diplômes

Vous pouvez exécuter le script suivant pour positionner un label en fonction du type de diplôme.
Attention ce script n'est qu'un exemple, l'objet __MAPPING  doit être adapté en fonction de vos propres types de diplômes.

// Table de correspondance entre code diplome -> code labelisation               
// A PERSONNALISER EN FONCTION DES DIPLOMES EXISTANT               
const __MAPPING = {               
    "XA" : "DNL", // Licence               
    "XB" : "DNM", // Master               
    "YA" : "DND", // Doctorat               
    "YB" : "DND", // Doctorat               
    "DP" : "LP", // Licence professionnelle               
    "CB" : "DUT", // DUT               
    "ZF" : "DCG", // DCG               
    "ZG" : "DCG", // DCG               
    "CD" : "DEUST", // DEUST               
    "FJ" : "Dip_Etat", // Diplôme d'état (chirurgie dentaire)               
    "IB" : "Dip_Etat", // Diplôme d'état(medecine)               
    "GD" : "Dip_Etat", // Diplôme d'état (pharmacie)               
    "FI" : "Dip_Ing", // Diplome d'ingénieur,               
    "YI" : "Dip_Ing", // Diplome d'ingénieur,               
    "RD" : "Dip_vise", // Diplôme visé (bac+3)               
    "RE" : "Dip_vise", // Diplôme visé (bac+2)               
    "RC" : "Dip_vise", // Diplôme visé (bac+4)               
    "RB" : "Dip_vise", // Diplôme visé (bac+5)               
    "RA" : "Dip_vise", // Diplôme visé (bac+5 grade master)               
    // "__" : "Classe_Prepa", // Classe préparatoire               
    // "__" : "Classe_Prepa_sans", // Classe préparatoire universitaire               
    // "__" : "BTS", // BTS               
    // "__" : "DTS", // DTS,               
    // "__" : "DMA", // DMA (métiers d'art)               
    // "__" : "DNMADE", // DNMADE               
    // "__" : "Grade_L", // Grade licence               
    // "__" : "Grade_M", // Grade master               
}               
             
const CertificationLabelsType = Java.type("org.ametys.odf.enumeration.CertificationLabels");               
const CertificationLabels = Ametys.serviceManager.lookup(CertificationLabelsType.ROLE);               
             
let nbcontent = 0;               
             
function _setCertificationLabel(content)               
{               
    var code = content.getValue("code");               
    if (__MAPPING[code])               
    {               
        nbcontent++;               
        var label = CertificationLabels.getLabelTitle(__MAPPING[code]).getLabel();               
             
        print(`#${content.getTitle()} (${code}) -> ${label} (${__MAPPING[code]})`);               
             
        content.setValue("certificationLabel", __MAPPING[code]);               
    }               
}               
             
Repository.query("//element(*, ametys:content)[not(@ametys:certificationLabel) and @ametys-internal:contentType='odf-enumeration.Degree']")               
    .forEach(function(content)               
    {               
        Content.migrate(               
            content,               
            [_setCertificationLabel],               
            false /* old versions are still compatible */,               
            null /* no tag */,               
            false /* not verbose */               
        );               
    }               
);               
             
Ametys.console.info(`${nbcontent} degree(s) has been updated`);               
Ametys.console.info(`Live workspace has to be rebuilt.`);               

Initialisation des formations/parcours existants

Pour identifier les formations contrôlées par l'Etat, un nouveau champ "certified" de type boolean a été ajouté au niveau des formations et parcours.
Si la plupart des formations sont des formations contrôlées par l'Etat, il sera fastidieux de passer sur toutes les formations pour modifier ce champ à true.

Voici ci-dessous un exemple de script pour positionner ce champ à true pour les formations et parcours selon les règles suivantes :

  • Le champ "certified" est positionné à true pour toutes formations autre que licence, master et licence professionelle
  • Le champ "certified" est positionné à true pour les parcours (direct) d'une formation de type licence, master ou licence professionnelle

Vous pouvez adapter ce script selon vos propres besoins.

const RefTableHelperType = Java.type("org.ametys.odf.enumeration.OdfReferenceTableHelper");               
const refTableHelper = Ametys.serviceManager.lookup(RefTableHelperType.ROLE);               
             
// Licence               
const licenceId = refTableHelper.getItemFromCode("odf-enumeration.Degree", "XA").getId();               
// Master               
const masterId = refTableHelper.getItemFromCode("odf-enumeration.Degree", "XB").getId();               
// Licence pro               
const licenceProId = refTableHelper.getItemFromCode("odf-enumeration.Degree", "DP").getId()               
             
function _setCertification(content)               
{               
    content.setValue("certified", true);               
}               
             
// Itère sur toutes les formations SAUF les licences, master et licences professionnelles               
// et positionne le champ "formation controlée par l'état" à true               
let nbcontent = 0;               
const predicate = "@ametys:degree != '" + licenceId + "' and @ametys:degree != '" + masterId + "' and @ametys:degree != '" + licenceProId + "'";               
             
Repository.query("//element(*, ametys:programContent)[not(@ametys:certified) and " + predicate + "]")               
    .forEach(function(content) {               
        nbcontent++;               
        Content.migrate(               
            content,               
            [_setCertification],               
            false /* old versions are still compatible */,               
            null /* no tag */,               
            false /* not verbose */               
        );               
    }               
);               
Ametys.console.info(`${nbcontent} programs(s) has been marked as certified`);               
             
// Itère sur tous les parcours des licences, master et licences professionnelles               
// et positionne le champ "formation controlée par l'état" à true               
nbcontent = 0;               
const predicate2 = "@ametys:degree = '" + licenceId + "' or @ametys:degree = '" + masterId + "' or @ametys:degree = '" + licenceProId + "'";               
             
Repository.query("//element(*, ametys:programContent)[" + predicate2 + "]")               
    .forEach(function(program)               
    {               
        program.getProgramPartChildren().forEach(function(child){               
             
            if (child.getClass().getName() == 'org.ametys.odf.program.SubProgram' && !child.hasValue("certified"))               
            {               
                nbcontent++;               
                Content.migrate(               
                    child,               
                    [_setCertification],               
                    false /* old versions are still compatible */,               
                    null /* no tag */,               
                    false /* not verbose */               
                );               
            }               
        }               
    );               
});               
             
Ametys.console.info(`${nbcontent} subprograms(s) has been marked as certified`);               
Ametys.console.info(`Live workspace has to be rebuilt.`);          

Approche par compétence

La nouvelle approche par compétence entraîne des modifications de modèles :    

  • ajout d'une table de référence "Niveau d'acquisition"
  • suppression des liens Blocs de compétences <-> Compétences
  • Formation/Parcours :
    • requiredSkillSets (valeur multiple de Blocs de compétences) devient requiredSkills (valeur multiple de Compétences)
    • acquiredSkillSets est supprimé
  • ELP
    • requiredSkillSets (valeur multiple de Blocs de compétences) devient requiredSkills (valeur multiple de Compétences)
    • acquiredSkillSets (valeur multiple de Blocs de compétences) devient acquiredSkills qui est un repeater de bloc de compétence, lui même un repeater de compéténce avec un niveau d'acquisition

En premieu lieu, exécutez le script suivant pour migrer les compétences sur vos contenus ODF :

const RefTableHelperType = Java.type("org.ametys.odf.enumeration.OdfReferenceTableHelper");       
const refTableHelper = Ametys.serviceManager.lookup(RefTableHelperType.ROLE);       
const ContentDataHelper = Java.type("org.ametys.cms.data.ContentDataHelper");       
const ConsoleHelper = Java.type("org.ametys.workspaces.repository.ConsoleHelper");       
             
function _getSkillsId(content, attributeName)       
{       
    var skillIds = [];       
    if (content.getNode().hasProperty('ametys:' + attributeName))       
    {       
        var skillSets = content.getNode().getProperty('ametys:' + attributeName).getValues();       
        if (skillSets.length > 0)       
        {       
            for (var i in skillSets)       
            {       
                var skillSetId = skillSets[i].getString();       
                var skillSet = Repository.resolver.resolveById(skillSetId);       
                if (skillSet.getNode().hasProperty('ametys:skills'))       
                {       
                    var skills = skillSet.getNode().getProperty('ametys:skills').getValues();       
                    for (var j in skills)       
                    {       
                        skillIds.push(skills[j].getString());       
                    }       
                }       
            }       
        }       
    }       
    return skillIds;       
}       
             
function _setSkills(content, attributeName, skillIds)       
{       
    if (skillIds.length > 0)       
    {       
        ConsoleHelper.setProperty(content.getNode(), "ametys:" + attributeName, skillIds);       
    }       
}       
             
function _removeSkillSets(content, attributeName)       
{       
    if (content.getNode().hasProperty('ametys:' + attributeName))       
    {       
        content.getNode().getProperty('ametys:' + attributeName).remove();       
    }       
}       
function _migrateSkills(content)       
{       
    var requiredSkills = _getSkillsId(content, 'requiredSkillSets');       
    _setSkills(content, 'requiredSkills', requiredSkills);       
    _removeSkillSets(content, 'requiredSkillSets');       
             
    var acquiredSkills = _getSkillsId(content, 'acquiredSkillSets');       
    _setSkills(content, 'acquiredSkills', acquiredSkills);       
    _removeSkillSets(content, 'acquiredSkillSets');       
}       
             
function _migrateCourseSkills(content)       
{       
    var requiredSkills = _getSkillsId(content, 'requiredSkillSets');       
    _setSkills(content, 'requiredSkills', requiredSkills);       
    _removeSkillSets(content, 'requiredSkillSets');       
    _setRepeaterSkillSet(content, 'acquiredSkillSets', 'acquiredSkills');       
    _removeSkillSets(content, 'acquiredSkillSets');       
}       
             
function _setRepeaterSkillSet(content, attributeName, repeaterName)       
{       
    var node = content.getNode();       
    if (node.hasProperty('ametys:' + attributeName))       
    {       
        var skillSets = node.getProperty('ametys:' + attributeName).getValues();       
        if (skillSets.length > 0)       
        {       
            if (node.hasNode('ametys:' + repeaterName))       
            {       
                node.getNode('ametys:' + repeaterName).remove();       
            }       
            var repeater = node.addNode('ametys:' + repeaterName, 'ametys:compositeMetadata');       
             
            var index1 = 1;       
            for (var i in skillSets)       
            {       
                var skillSetId = skillSets[i].getString();       
                var entry = repeater.addNode('ametys:' + index1++, 'ametys:compositeMetadata');       
             
                entry.setProperty('ametys:skillSet', skillSetId);       
             
                var skillSet = Repository.resolver.resolveById(skillSetId);       
                if (skillSet.getNode().hasProperty('ametys:skills'))       
                {       
                    var skills = skillSet.getNode().getProperty('ametys:skills').getValues();       
                    var skillRep = entry.addNode('ametys:skills', 'ametys:compositeMetadata');       
             
                    var index2 = 1;       
                    for (var j in skills)       
                    {       
                        var skillEntry = skillRep.addNode('ametys:' + index2++, 'ametys:compositeMetadata');       
                        skillEntry.setProperty("ametys:skill", skills[j].getString());       
                    }       
                }       
            }       
        }       
    }       
}       
             
let nbcontent = 0;       
Repository.query("//element(*, ametys:programContent)[@ametys:requiredSkillSets or @ametys:acquiredSkillSets]")       
    .forEach(function(content) {       
        nbcontent++;       
             
        Content.migrate(       
            content,       
            [_migrateSkills],       
            false /* old versions are still compatible */,       
            null /* no tag */,       
            false /* not verbose */       
        );       
    }       
);       
Ametys.console.info(`${nbcontent} programs(s) has been migrated`);       
             
nbcontent = 0;       
Repository.query("//element(*, ametys:subProgramContent)[@ametys:requiredSkillSets or @ametys:acquiredSkillSets]")       
    .forEach(function(content) {       
        nbcontent++;       
             
        Content.migrate(       
            content,       
            [_migrateSkills],       
            false /* old versions are still compatible */,       
            null /* no tag */,       
            false /* not verbose */       
        );       
    }       
);       
Ametys.console.info(`${nbcontent} subprograms(s) has been migrated`);       
             
nbcontent = 0;       
Repository.query("//element(*, ametys:courseContent)[@ametys:requiredSkillSets or @ametys:acquiredSkillSets]")       
    .forEach(function(content) {       
        nbcontent++;       
             
        Content.migrate(       
            content,       
            [_migrateCourseSkills],       
            false /* old versions are still compatible */,       
            null /* no tag */,       
            false /* not verbose */       
        );       
    }       
);       
Ametys.console.info(`${nbcontent} course(s) has been migrated`);       
             
Ametys.console.info(`Live workspace has to be rebuilt.`);       

Puis exécutez le script suivant (uniquement si le 1er script s'est bien déroulé !) pour supprimer le lien entre les tables de référence Compétences et Blocs de compétences :

let nbcontent = 0;       
             
function _removeSkills(content)       
{       
    if (content.hasValue('ametys:skills'))       
    {       
        content.getProperty('ametys:skills').remove();       
        nbcontent++;       
    }       
}       
             
function _removeSkillSets(content)       
{       
    if (content.hasValue('ametys:skillSets'))       
    {       
        content.getProperty('ametys:skillSets').remove();       
    }       
}       
             
Repository.query("//element(*, ametys:content)[@ametys-internal:contentType = 'odf-enumeration.SkillSet']")       
    .forEach(function(content) {       
        nbcontent++;       
             
        Content.migrate(       
            content,       
            [_removeSkills],       
            false /* old versions are still compatible */,       
            null /* no tag */,       
            false /* not verbose */       
        );       
    }       
);       
             
Ametys.console.info(`${nbcontent} skill set(s) has been migrated`);       
             
Repository.query("//element(*, ametys:content)[@ametys-internal:contentType = 'odf-enumeration.Skill']")       
    .forEach(function(content) {       
        nbcontent++;       
             
        Content.migrate(       
            content,       
            [_removeSkillSets],       
            false /* old versions are still compatible */,       
            null /* no tag */,       
            false /* not verbose */       
        );       
    }       
);       
Ametys.console.info(`${nbcontent} skill(s) has been migrated`);           

Après l'application de ces scripts, lancez une reconstruction du Live

Retour en haut