'use strict'
const request = require('request')
const flow = require('an-flow')
const elementtree = require('elementtree')
const config = require('./config')
const arbreCateg = config.constantes.categories.liste
const sjtObj = require('sesajstools/utils/object')
const Ref = require('../../constructors/Ref')
const xmls = ['cp', 'ce1', 'ce2', 'cm1', 'cm2', '6eme']
/**
* Controleur /importEc/ pour importer les xml calculatice (appelé par le site ressources, après réplication des js calculatice)
* @controller controllerImportEc
*/
module.exports = function (component) {
component.controller('importEc', function ($ressourceRepository, $ressourceConverter, $ressourceControl, $accessControl, $json) {
/**
* Met à jour un arbre calculatice
* @route GET /importEc/:xml
* @param {Context} context
* @param {string} xmlSuffifx Le suffixe du xml (cm2 pour ressources-cm2.xml)
* @param {ressourceCallback} next
*/
function getAndParseXml (context, xmlSuffix, next) {
const arbre = getArbreDefaultValues(xmlSuffix)
flow().seq(function () {
$accessControl.isSesamathClient(context, this)
}).seq(function (isSesamathClient) {
if (isSesamathClient) this()
else $json.denied(context, "Vous n'avez pas les droits suffisant pour accéder à cette commande")
}).seq(function () {
// on peut importer
const nextStep = this
const url = config.imports.ecBase + '/xml/ressources-' + xmlSuffix + '.xml'
request.get(url, function (error, response, body) {
if (error) {
log.error(error)
nextStep(new Error('impossible de récupérer ' + url))
} else if (body) {
nextStep(null, body)
} else {
log.error("Sur l'url " + url + ' on récupère', response)
nextStep(new Error(url + ' renvoie une réponse vide'))
}
})
}).seq(function (xmlString) {
// log.debug('analyse de', xmlString)
const arbreXml = elementtree.parse(xmlString)
if (arbreXml._root) {
if (!arbreXml._root._children || !arbreXml._root._children.length) this(new Error('xml ' + xmlSuffix + ' vide'))
else if (arbreXml._root.tag !== 'niveau') this(new Error('xml ' + xmlSuffix + ' ne contient pas de tag niveau à la racine'))
else if (arbreXml._root.attrib.id !== xmlSuffix) this(new Error('xml ' + xmlSuffix + ' ne contient pas le bon niveau (trouvé ' + arbreXml._root.attrib.id + ')'))
else this(null, arbreXml._root._children)
} else {
this(new Error('xml ' + xmlSuffix + ' sans racine'))
}
}).seq(function (children) {
// log.debug('obj xml', children, 'xml', {max:1000, indent:2})
log.debug('parsing des enfants de ' + xmlSuffix)
parseEnfants(children, this)
}).seq(function (enfants) {
arbre.enfants = enfants
next(null, arbre)
}).catch(function (error) {
next(error)
})
} // getAndParseXml
/**
* Retourne les valeurs par défaut d'un arbre de ressources calculatice
* @param xmlSuffix
* @returns {object} {titre: string, type: string, origine: string, idOrigine: *, categories: *[], publie: boolean, restriction: number, enfants: Array}
*/
function getArbreDefaultValues (xmlSuffix) {
const classe = (xmlSuffix === '6eme') ? xmlSuffix : xmlSuffix.toUpperCase()
let titre = 'Ressources Calcul@tice ' + classe
if (xmlSuffix === 'all') {
titre = 'Exercices de calcul mental Calcul@TICE'
niveaux = [
config.constantes.niveaux.cp,
config.constantes.niveaux.ce1,
config.constantes.niveaux.ce2,
config.constantes.niveaux.cm1,
config.constantes.niveaux.cm2,
config.constantes.niveaux['6e']
]
} else if (xmlSuffix === '6eme') {
niveaux = [config.constantes.niveaux['6e']]
} else {
niveaux = [config.constantes.niveaux[xmlSuffix]]
}
return {
titre: titre,
type: 'arbre',
origine: 'calculatice',
idOrigine: xmlSuffix,
categories: [arbreCateg],
niveaux: niveaux,
publie: true,
restriction: 0,
enfants: []
}
}
/**
* Retourne une ressource à partir d'un child exercice
* @param child
* @returns {Ressource}
*/
function getEcRessource (child) {
let ressource
if (child.attrib.uid) {
ressource = {
titre: '???',
origine: 'calculatice',
idOrigine: child.attrib.uid,
categories: [config.constantes.categories.exerciceInteractif],
niveaux: niveaux,
parametres: {}
}
let swf, js, options
child._children.forEach(function (elt) {
if (elt.tag === 'nom') ressource.titre = elt.text
else if (elt.tag === 'fichier') swf = elt.text
else if (elt.tag === 'fichierjs') js = elt.text
else if (elt.tag === 'options') options = elt.text
else log.debug("tag d'enfant d'exo ec inconnu", elt)
})
if (options && options !== 'default') {
try {
ressource.parametres.options = JSON.parse(options)
} catch (error) {
log.debug("parsing d'options HS", options)
log.error(new Error("erreur sur le parsing des options de l'exercice calculatice " + ressource.idOrigine +
' : ' + error.toString() + '\navec\n' + options))
}
} else if (!options) {
log.error(new Error('exercice calculatice ' + ressource.idOrigine + ' sans options'))
}
if (js) {
ressource.type = 'ecjs'
ressource.parametres.fichierjs = js
} else if (swf) {
ressource.type = 'ec2'
ressource.parametres.fichier = swf
}
}
return ressource
} // getEcRessource
/**
* Passe à next les enfants d'un élément du xml
* @param children
* @param next callback(error, enfants)
*/
function parseEnfants (children, next) {
const enfants = []
flow(children).seqEach(function (child) {
const nextChild = this
if (child.tag === 'exercice') {
save(getEcRessource(child), function (error, ressource) {
if (error) log.error(error)
else enfants.push(new Ref(ressource))
nextChild()
})
} else if (child._children.length) {
const enfant = {}
enfant.type = 'arbre'
enfant.titre = getNom(child._children)
parseEnfants(child._children, function (error, ptifils) {
if (error) {
nextChild(error)
} else {
enfant.enfants = ptifils
enfants.push(enfant)
nextChild()
}
})
} else {
if (child.tag !== 'nom') log.debug('child ignoré', child)
nextChild()
}
}).seq(function () {
next(null, enfants)
}).catch(function (error) {
log.error(error)
next(error)
})
} // parseEnfants
/**
* Renvoie le text du premier tag nom trouvé dans les enfants passés en argument
* @param {object[]} children
*/
function getNom (children) {
let i = 0
let nom
while (!nom && i < children.length) {
if (children[i].tag === 'nom') {
nom = children[i].text
}
i++
}
return nom || '???'
}
/**
* Enregistre une ressource
* @param {Ressource} ressource
* @param next Appelé avec (error, entiteRessource)
*/
function save (ressource, next) {
if (ressource.idOrigine) {
$ressourceRepository.loadByOrigin(ressource.origine, ressource.idOrigine, function (error, ressourceLoaded) {
if (error) {
log.error('pb au chargement : ' + error.toString(), ressource)
next(new Error('Impossible de sauvegarder la ressource récupérée (probablement mal interprétée)'))
} else {
const ressourceNew = sjtObj.clone(ressourceLoaded) || {}
sjtObj.update(ressourceNew, ressource)
if (ressource.idOrigine == 1) { // eslint-disable-line eqeqeq
log.debug('ressource 1 en bdd', ressourceLoaded.parametres)
log.debug('ressource 1 passée', ressourceNew.parametres)
}
if (sjtObj.isEqual(ressourceLoaded, ressourceNew)) {
next(null, ressourceNew)
} else {
log.debug('ressource calculatice/' + ressource.idOrigine + ' modifiée')
$ressourceRepository.save(ressourceNew, next)
}
}
})
} else {
log.debug('ressource incomplète', ressource)
log.error(new Error('ressource sans idOrigine'))
next(new Error('ressource incomplète'))
}
}
/**
* Enregistre la ressource et affiche la réponse
* @param {Context} context
* @param {Ressource} ressource
*/
function saveAndSendReponse (context, ressource) {
save(ressource, function (error, ressource) {
$json.send(context, error, new Ref(ressource))
})
}
/**
* Le controleur
* @param context
*/
function xmlController (context) {
$accessControl.isSesamathClient(context, function (error, isSesamathClient) {
if (error) {
log.error(error)
$json.denied(context, error.toString())
} else if (isSesamathClient) {
const xmlSuffix = context.arguments.xml
if (!xmlSuffix) throw new Error('Il manque un argument') // devrait jamais arriver
if (xmlSuffix === 'all') {
const arbreAll = getArbreDefaultValues(xmlSuffix)
flow(xmls).seqEach(function (suffix) {
const nextXml = this
log.debug('pour all on lance ' + suffix)
getAndParseXml(context, suffix, function (error, arbre) {
if (error) {
nextXml(error)
} else {
save(arbre, function (error, arbre) {
if (error) {
nextXml(error)
} else {
arbreAll.enfants.push(new Ref(arbre))
nextXml()
}
})
}
})
}).seq(function () {
saveAndSendReponse(context, arbreAll)
}).catch(function (error) {
$json.send(context, error)
})
} else {
getAndParseXml(context, xmlSuffix, function (error, arbre) {
if (error) $json.send(context, error)
else saveAndSendReponse(context, arbre)
})
}
} else {
$json.denied(context, "Vous n'avez pas les droits suffisant pour accéder à cette commande")
}
})
}
let niveaux // affecté dans getArbreDefaultValues et utilisé au save
xmlController.timeout = 60000
this.get(':xml', xmlController)
})
}