From 75e9c4e0d4fdf889498dd207bb1d083bfef358d8 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sun, 29 Jun 2014 11:36:00 -0300 Subject: [PATCH 01/54] =?UTF-8?q?Implementa=C3=A7=C3=A3o=20de=20parte=20do?= =?UTF-8?q?s=20testes=20da=20feature=20XMLImport,=20incluindo=20tamb=C3=A9?= =?UTF-8?q?m=20corre=C3=A7=C3=A3o=20em=20XMLController=20referente=20?= =?UTF-8?q?=C3=A0=20transa=C3=A7=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rgms/publication/XMLController.groovy | 41 +- grails-app/services/rgms/XMLService.groovy | 885 +++-- test/cucumber/XMLImport.feature | 74 +- test/cucumber/steps/XMLImportSteps.groovy | 300 +- test/files/cv-completo.xml | 3092 +++++++++++++++++ test/files/cv-duplicatedConflictedDetails.xml | 424 +++ test/files/cv-duplicatedRPC.xml | 454 +++ test/files/cv.xml | 454 +++ test/functional/pages/XMLImportPage.groovy | 11 +- .../steps/ArticleTestDataAndOperations.groovy | 10 +- .../ConferenciaTestDataAndOperations.groovy | 5 +- ...esearchProjectTestDadaAndOperations.groovy | 21 +- .../steps/TestDataAndOperations.groovy | 3 +- .../XMLImportTestDataAndOperations.groovy | 196 ++ 14 files changed, 5495 insertions(+), 475 deletions(-) create mode 100644 test/files/cv-completo.xml create mode 100644 test/files/cv-duplicatedConflictedDetails.xml create mode 100644 test/files/cv-duplicatedRPC.xml create mode 100644 test/files/cv.xml create mode 100644 test/functional/steps/XMLImportTestDataAndOperations.groovy diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index 62fb08ad..7cc63ba8 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -1,7 +1,6 @@ package rgms.publication import org.apache.shiro.SecurityUtils -import rgms.XMLService import rgms.authentication.User import rgms.member.Member @@ -14,6 +13,8 @@ import rgms.member.Member */ class XMLController { + def XMLService + def home() {} def upload() { @@ -23,7 +24,7 @@ class XMLController { return } - private Closure savePublication = { + private savePublication = { Node xmlFile -> Member user = getCurrentUser() XMLService.createPublications(xmlFile, user) @@ -36,9 +37,10 @@ class XMLController { return } - private Closure saveTools = { + private saveTools = { Node xmlFile -> - XMLService.createFerramentas(xmlFile) + Member user = getCurrentUser() + XMLService.createTools(xmlFile, user.name) } def uploadXMLBook() { @@ -48,16 +50,17 @@ class XMLController { return } - private Closure saveBook = { + private saveBook = { Node xmlFile -> - XMLService.createBooks(xmlFile) + Member user = getCurrentUser() + XMLService.createBooks(xmlFile, user.name) } def uploadXMLResearchLine() { XMLService.Import(saveResearchLine, returnWithMessage, 'default.researchline.import.flashmessage.success', "ResearchLine", request) } - private Closure saveResearchLine = { + private saveResearchLine = { Node xmlFile -> XMLService.createResearchLines(xmlFile) } @@ -66,7 +69,7 @@ class XMLController { XMLService.Import(saveReseachProject, returnWithMessage, 'default.researchproject.import.flashmessage.success', "ResearchProject", request) } - private Closure saveReseachProject = { + private saveReseachProject = { Node xmlFile -> XMLService.createResearchProjects(xmlFile) } @@ -78,9 +81,10 @@ class XMLController { return } - public Closure saveBookChapters = { + public saveBookChapters = { Node xmlFile -> - XMLService.createBooksChapters(xmlFile) + Member user = getCurrentUser() + XMLService.createBooksChapters(xmlFile, user.name) } def uploadXMLDissertacao() { @@ -90,9 +94,10 @@ class XMLController { return } - private Closure saveDissertations = { + private saveDissertations = { Node xmlFile -> - XMLService.createDissertations(xmlFile) + Member user = getCurrentUser() + XMLService.createMasterDissertation(xmlFile, user.name) } def enviarConferenciaXML() { @@ -102,9 +107,10 @@ class XMLController { return } - private Closure saveConferencias = { + private saveConferencias = { Node xmlFile -> - XMLService.createConferencias(xmlFile) + Member user = getCurrentUser() + XMLService.createConferencias(xmlFile, user.name) } def uploadOrientationXML() { @@ -114,7 +120,7 @@ class XMLController { return } - private Closure saveOrientations = { + private saveOrientations = { Node xmlFile -> Member user = getCurrentUser() @@ -128,9 +134,10 @@ class XMLController { return } - private Closure saveJournals = { + private saveJournals = { Node xmlFile -> - XMLService.createJournals(xmlFile) + Member user = getCurrentUser() + XMLService.createJournals(xmlFile, user.name) } def uploadMemberXML() { diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index 107c729b..256cca76 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -1,7 +1,8 @@ package rgms import org.springframework.web.multipart.MultipartHttpServletRequest -import org.springframework.web.multipart.commons.CommonsMultipartFile +import org.springframework.web.multipart.MultipartFile +import org.xml.sax.SAXParseException import rgms.member.Member import rgms.member.Orientation import rgms.publication.* @@ -10,18 +11,24 @@ import rgms.researchProject.ResearchProject class XMLService { + public static final String PUB_STATUS_STABLE = "stable" + public static final String PUB_STATUS_TO_UPDATE = "to update" + public static final String PUB_STATUS_CONFLICTED= "conflicted" + public static final String PUB_STATUS_DUPLICATED = "duplicated" + /* saveEntity - closure que salva a classe de domínio que está usando a importação */ - static boolean Import(Closure saveEntity, Closure returnWithMessage, + def boolean Import(Closure saveEntity, Closure returnWithMessage, String flashMessage, String controller, javax.servlet.http.HttpServletRequest request) { boolean errorFound = false + def publications try { Node xmlFile = parseReceivedFile(request) - saveEntity(xmlFile) + publications = saveEntity(xmlFile) } //If file is not XML or if no file was uploaded catch (SAXParseException) { @@ -33,357 +40,540 @@ class XMLService { flashMessage = 'default.xml.structure.message' errorFound = true } - catch (Exception) { + catch (Exception ex) { flashMessage = 'default.xml.unknownerror.message' errorFound = true } - returnWithMessage(flashMessage, controller) + returnWithMessage(flashMessage, controller, publications) return !errorFound } - static void createPublications(Node xmlFile, Member user) { - createFerramentas(xmlFile) - createBooksChapters(xmlFile) - createDissertations(xmlFile) - createConferencias(xmlFile) - createOrientations(xmlFile, user) - createJournals(xmlFile) + def createPublications(Node xmlFile, Member user) { + def publications = [:] + if(!xmlFile) return publications + + //#if($Article) + def journals = createJournals(xmlFile, user.name) + if(journals) publications.put("journals", journals) + //#end + + def tools = createTools(xmlFile, user.name) + if(tools) publications.put("tools", tools) + + def books = createBooks(xmlFile, user.name) + if(books) publications.put("books", books) + + def bookChapters = createBooksChapters(xmlFile, user.name) + if(bookChapters) publications.put("bookChapters", bookChapters) + + def masterDissertation = createMasterDissertation(xmlFile, user.name) + if(masterDissertation) publications.put("masterDissertation", masterDissertation) + + def thesis = createThesis(xmlFile, user.name) + if(thesis) publications.put("thesis", thesis) + + def conferences = createConferencias(xmlFile, user.name) + if(conferences) publications.put("conferences", conferences) + //#if($researchLine) - createResearchLines(xmlFile) + def researchLines = createResearchLines(xmlFile, user.name) + if(researchLines) publications.put("researchLines", researchLines) //#end + //#if($researchProject) - createResearchProjects(xmlFile) + def researchProjects = createResearchProjects(xmlFile, user.name) + publications.put("researchProjects", researchProjects) //#end - println "All imports done!" - } + //#if($Orientation) + def orientations = createOrientations(xmlFile, user) + publications.put("orientations", orientations) + //#end + + return publications + } - static void createFerramentas(Node xmlFile) { - Node producaoTecnica = (Node) xmlFile.children()[2] + def createTools(Node xmlFile, String authorName) { + def softwares = xmlFile.depthFirst().findAll{ it.name() == 'SOFTWARE' } + def tools = [] - for (Node currentNode : producaoTecnica.children()) { - if (currentNode.name().equals("SOFTWARE")) - saveNewFerramenta(currentNode) + for (Node currentNode : softwares) { + def newTool = saveNewTool(currentNode, authorName) + def status = checkToolStatus(newTool) + if (status && status != XMLService.PUB_STATUS_DUPLICATED) { + def obj = newTool.properties.findAll{it.key in ["title","publicationDate", "authors", "description"]} + tools += ["obj": obj, "status": status] + } } + + return tools } - private static void saveNewFerramenta(Node currentNode) { - Node dadosBasicos = (Node) currentNode.children()[0] - Node detalhamentoDoSoftware = (Node) currentNode.children()[1] - Node informacoesAdicionais = getNodeFromNode(currentNode, "INFORMACOES-ADICIONAIS") + private static saveNewTool(Node currentNode, String authorName) { + Node basicData = (Node) currentNode.children()[0] + Node softwareDetails = (Node) currentNode.children()[1] + Node additionalInfo = getNodeFromNode(currentNode, "INFORMACOES-ADICIONAIS") Ferramenta newTool = new Ferramenta() + newTool = (Ferramenta) addAuthors(currentNode, newTool) + if(!newTool.authors.contains(authorName)) return null //the user is not author - fillPublicationDate(newTool, dadosBasicos, "ANO") - newTool.file = 'no File' - newTool.website = 'no Website' - newTool.title = getAttributeValueFromNode(dadosBasicos, "TITULO-DO-SOFTWARE") + fillPublicationDate(newTool, basicData, "ANO") + newTool.title = getAttributeValueFromNode(basicData, "TITULO-DO-SOFTWARE") + String description = getAttributeValueFromNode(additionalInfo, "DESCRICAO-INFORMACOES-ADICIONAIS") + newTool.description = "País: " + getAttributeValueFromNode(basicData, "PAIS") + ", Ambiente: " + + getAttributeValueFromNode(softwareDetails, "AMBIENTE") + + (description.equals("") ? "" : ", Informacoes adicionais: " + description) - String descricao = getAttributeValueFromNode(informacoesAdicionais, "DESCRICAO-INFORMACOES-ADICIONAIS") - newTool.description = "País: " + getAttributeValueFromNode(dadosBasicos, "PAIS") + ", Ambiente: " + getAttributeValueFromNode(detalhamentoDoSoftware, "AMBIENTE") + (descricao.equals("") ? "" : ", Informacoes adicionais: " + descricao) - newTool.save(flush: false) + return newTool } - static void createBooks(Node xmlFile) { - Node books = (Node) ((Node) ((Node) xmlFile.children()[1]).children()[2]).children()[0] - List bookChildren = books.children() - - int i = 0 - - for (Node currentNode : bookChildren) { - List book = currentNode.children() - Node dadosBasicos = (Node) book[0] - Node detalhamentoLivro = (Node) book[1] + static checkToolStatus(Ferramenta tool){ + if(!tool) return null + def status = XMLService.PUB_STATUS_STABLE + def toolDB = Ferramenta.findByTitle(tool.title) + if(toolDB) status = checkPublicationStatus(toolDB, tool) + return status + } - Book newBook = new Book() + def createBooks(Node xmlFile, String authorName) { + def publishedBooks = xmlFile.depthFirst().findAll{ it.name() == 'LIVRO-PUBLICADO-OU-ORGANIZADO' } + def booksList = [] - for (int j = 2; j < book.size() - 2; ++j) { - newBook.addToAuthors(getAttributeValueFromNode(book[j], "NOME-COMPLETO-DO-AUTOR")) + int i = 0 + for (Node currentNode : publishedBooks) { + def newBook = createNewBook(currentNode, i, authorName) + def status = checkBookStatus(newBook) + if(status && status != PUB_STATUS_DUPLICATED) { + def obj = newBook.properties.findAll{it.key in ["title","publicationDate", "authors", "publisher", "volume", "pages"]} + booksList += ["obj": obj, "status": status] } - - createNewBook(newBook, dadosBasicos, detalhamentoLivro, i) ++i } + + return booksList } - private static void createNewBook(Book newBook, Node dadosBasicos, Node detalhamentoLivro, int i) { + private static createNewBook(Node currentNode, int i, String authorName) { + List book = currentNode.children() + Node basicData = (Node) book[0] + Node bookDetails = (Node) book[1] + Book newBook = new Book() - newBook.title = getAttributeValueFromNode(dadosBasicos, "TITULO-DO-LIVRO") - newBook.publisher = getAttributeValueFromNode(detalhamentoLivro, "NOME-DA-EDITORA") + newBook = (Book) addAuthors(currentNode, newBook) + if(!newBook.authors.contains(authorName)) return null //the user is not author - if (Publication.findByTitle(newBook.title) == null) { - fillPublicationDate(newBook, dadosBasicos, "ANO") + newBook.title = getAttributeValueFromNode(basicData, "TITULO-DO-LIVRO") + newBook.publisher = getAttributeValueFromNode(bookDetails, "NOME-DA-EDITORA") + fillPublicationDate(newBook, basicData, "ANO") + newBook.pages = getAttributeValueFromNode(bookDetails, "NUMERO-DE-PAGINAS") + newBook.volume = getAttributeValueFromNode(bookDetails, "NUMERO-DE-VOLUMES").toInteger() - newBook.file = 'emptyfile' + i.toString() - newBook.pages = getAttributeValueFromNode(detalhamentoLivro, "NUMERO-DE-PAGINAS") - newBook.volume = getAttributeValueFromNode(detalhamentoLivro, "NUMERO-DE-VOLUMES").toInteger() - newBook.save(flush: false) - } + return newBook } - //#if($researchLine) - static void createResearchLines(Node xmlFile) { - //Nesse ponto eu já estou com a lista de Atuacoes Profissionais do XML - List pro_perf = ((Node) ((Node) xmlFile.children()[0]).children()[4]).children() - //Navega ate atuacoes profissionais e extrai a lista de atuacoes - - for (Node i : pro_perf) { //Atuaçao profissional - for (Node j : i.children()) { //Atividades de pesquisa e desenvolvimento - if (((String) j.name()).equals("ATIVIDADES-DE-PESQUISA-E-DESENVOLVIMENTO")) { - for (Node k : j.children()) { //Pesquisa e desenvolvimento - if (((String) k.name()).equals("PESQUISA-E-DESENVOLVIMENTO")) { - for (Node l : k.children()) { //Linha de pesquisa - saveResearchLine(l) - } - } - } - } + static checkBookStatus(Book book){ + if(!book) return null + def status = PUB_STATUS_STABLE + def bookDB = Book.findByTitleAndVolume(book.title, book.volume) + if(bookDB) status = checkPublicationStatus(bookDB, book) + return status + } + + def createBooksChapters(Node xmlFile, String authorName) { + def publishedBookChapters = xmlFile.depthFirst().findAll{ it.name() == 'CAPITULO-DE-LIVRO-PUBLICADO' } + def bookChaptersList = [] + + for (int i = 0; i < publishedBookChapters?.size(); ++i) { + def newBookChapter = createNewBookChapter(publishedBookChapters, i, authorName) + def status = checkBookChapterStatus(newBookChapter) + if(status && status != PUB_STATUS_DUPLICATED) { + def obj = newBookChapter.properties.findAll{it.key in ["title","publicationDate", "authors", "publisher"]} + bookChaptersList += ["obj": obj, "status": status] } } + + return bookChaptersList } - //#end - //#if($researchLine) - private static void saveResearchLine(Node xmlFile) { - ResearchLine newResearchLine = new ResearchLine() - newResearchLine.name = getAttributeValueFromNode(xmlFile, "TITULO-DA-LINHA-DE-PESQUISA") - newResearchLine.description = getAttributeValueFromNode(xmlFile, "OBJETIVOS-LINHA-DE-PESQUISA") - newResearchLine.save(flush: false) + private static createNewBookChapter(List bookChaptersChildren, int i, String authorName) { + List bookChapter = ((Node) bookChaptersChildren[i]).children() + Node basicData = (Node) bookChapter[0] + Node chapterDetails = (Node) bookChapter[1] + + BookChapter newBookChapter = new BookChapter() + newBookChapter = (BookChapter) addAuthors(bookChapter, newBookChapter) + if(!newBookChapter.authors.contains(authorName)) return null + + newBookChapter.title = getAttributeValueFromNode(basicData, "TITULO-DO-CAPITULO-DO-LIVRO") + newBookChapter.publisher = getAttributeValueFromNode(chapterDetails, "NOME-DA-EDITORA") + fillPublicationDate(newBookChapter, basicData, "ANO") + + return newBookChapter } - //#end - //#if($researchProject) - static void createResearchProjects(Node xmlFile) { - //Nesse ponto eu já estou com a lista de Atuacoes Profissionais do XML - List pro_perf = ((Node) ((Node) xmlFile.children()[0]).children()[4]).children() - //Navega ate atuacoes profissionais e extrai a lista de atuacoes - - for (Node i : pro_perf) { //Atuaçao profissional - for (Node j : i.children()) { //Atividades de pesquisa em projeto - if (((String) j.name()).equals("ATIVIDADES-DE-PARTICIPACAO-EM-PROJETO")) { - for (Node k : j.children()) { //Participacao em projeto - if (((String) k.name()).equals("PARTICIPACAO-EM-PROJETO")) { - for (Node l : k.children()) { //Projeto de pesquisa - saveResearchProject(l) - } - } - } - } + static checkBookChapterStatus(BookChapter bookChapter){ + if(!bookChapter) return null + def status = PUB_STATUS_STABLE + def bookChapterDB = BookChapter.findByTitleAndChapter(bookChapter.title, bookChapter.chapter) + if(bookChapterDB) status = checkPublicationStatus(bookChapterDB, bookChapter) + return status + } + + private static Publication addAuthors(publication, newPublication) { + publication.each{ + if(it.name() == "AUTORES"){ + newPublication.addToAuthors(getAttributeValueFromNode(it, "NOME-COMPLETO-DO-AUTOR")) } } + return newPublication } - //#end - //#if($researchProject) - private static void saveResearchProject(Node xmlFile) { - String name = getAttributeValueFromNode(xmlFile, "NOME-DO-PROJETO") - ResearchProject project = ResearchProject.findByProjectName(name) - - if (project == null) { //Se o projeto de pesquisa ainda nao existe no sistema hora de adiciona-lo - ResearchProject newProject = new ResearchProject() - newProject.projectName = name - newProject.description = getAttributeValueFromNode(xmlFile, "DESCRICAO-DO-PROJETO") - newProject.status = getAttributeValueFromNode(xmlFile, "SITUACAO") - newProject.startYear = getAttributeValueFromNode(xmlFile, "ANO-INICIO").toInteger() - newProject.endYear = getAttributeValueFromNode(xmlFile, "ANO-FIM").equals("") ? 0 : getAttributeValueFromNode(xmlFile, "ANO-FIM").toInteger() - fillProjectMembers(getNodeFromNode(xmlFile, "EQUIPE-DO-PROJETO"), newProject) - fillFunders(getNodeFromNode(xmlFile, "FINANCIADORES-DO-PROJETO"), newProject) - newProject.save(flush: false) - } + def createMasterDissertation(Node xmlFile, String authorName) { + def newDissertation = saveMasterDissertation(xmlFile, authorName) + if(!newDissertation) return null + + def dissertationDB = Dissertacao.findByTitle(newDissertation?.title) + if(dissertationDB?.authors != newDissertation?.authors) dissertationDB = null + + def status = checkDissertationOrThesisStatus(dissertationDB, newDissertation) + if(status == PUB_STATUS_DUPLICATED) return null + def obj = newDissertation.properties.findAll{it.key in ["title","publicationDate", "authors", "school"]} + return ["obj": obj, "status":status] } - //#end - //#if($funder) - private static void fillFunders(Node xmlFile, ResearchProject project) { - for (Node node : xmlFile?.children()) { - String code = getAttributeValueFromNode(node, "CODIGO-INSTITUICAO") - Funder funder = Funder.findByCode(code) + private static saveMasterDissertation(Node xmlFile, String authorName){ + def mestrado = xmlFile.depthFirst().find{ it.name() == 'MESTRADO' } + if(!mestrado) return null - if (funder) { - project.addToFunders(funder) - } else { - Funder newFunder = new Funder() - newFunder.code = code - newFunder.name = getAttributeValueFromNode(node, "NOME-INSTITUICAO") - newFunder.nature = getAttributeValueFromNode(node, "NATUREZA") - newFunder.save(flush: false) - project.addToFunders(newFunder).save(flush: false) - } - } + String author = xmlFile.depthFirst().find{it.name() == 'DADOS-GERAIS'}.'@NOME-COMPLETO' + if(author != authorName) return null + + Dissertacao newDissertation = new Dissertacao() + newDissertation = getDissertationOrThesisDetails(mestrado, newDissertation) + newDissertation.addToAuthors(author) + + return newDissertation } - //#end - //#if($researchProject) - private static void fillProjectMembers(Node xmlFile, ResearchProject project) { + static checkDissertationOrThesisStatus(TeseOrDissertacao pubDB, TeseOrDissertacao pub){ + if(!pub) return null + def status = PUB_STATUS_STABLE + if(pubDB) status = checkPublicationStatus(pubDB, pub) + return status + } - for (Node node : xmlFile?.children()) { //Para cada integrante presente no projeto - String name = (String) (node.attribute("NOME-COMPLETO")) - Boolean responsavel = ((String) (node.attribute("FLAG-RESPONSAVEL"))).equals("SIM") - if (responsavel) project.responsible = name - project.addToMembers(name).save(validate: true) - } + def createThesis(Node xmlFile, String authorName) { + def newThesis = saveThesis(xmlFile, authorName) + if(!newThesis) return null + + def thesisDB = Tese.findByTitle(newThesis?.title) + if(thesisDB?.authors != newThesis?.authors) thesisDB = null + + def status = checkDissertationOrThesisStatus(thesisDB, newThesis) + if(status == PUB_STATUS_DUPLICATED) return null + + def obj = newThesis.properties.findAll{it.key in ["title","publicationDate", "authors", "school"]} + return ["obj": obj, "status":status] } - //#end - static void createBooksChapters(Node xmlFile) { - Node bookChapters = (Node) ((Node) ((Node) xmlFile.children()[1]).children()[2]).children()[1] - List bookChaptersChildren = bookChapters.children() + private static saveThesis(Node xmlFile, String authorName){ + def doutorado = xmlFile.depthFirst().find{ it.name() == 'DOUTORADO' } + if(!doutorado) return null - for (int i = 0; i < bookChaptersChildren.size(); ++i) { - List bookChapter = ((Node) bookChaptersChildren[i]).children() - Node dadosBasicos = (Node) bookChapter[0] - Node detalhamentoCapitulo = (Node) bookChapter[1] + String author = xmlFile.depthFirst().find{it.name() == 'DADOS-GERAIS'}.'@NOME-COMPLETO' + if(author != authorName) return null - BookChapter newBookChapter = new BookChapter() + Tese newThesis = new Tese() + newThesis = getDissertationOrThesisDetails(doutorado, newThesis) + newThesis.addToAuthors(author) - newBookChapter = (BookChapter) addAuthors(bookChapter, newBookChapter) + return newThesis + } - createNewBookChapter(newBookChapter, dadosBasicos, detalhamentoCapitulo, i) + private static getDissertationOrThesisDetails(Node xmlNode, TeseOrDissertacao publication) { + publication.title = getAttributeValueFromNode(xmlNode, "TITULO-DA-DISSERTACAO-TESE") + fillPublicationDate(publication, xmlNode, "ANO-DE-OBTENCAO-DO-TITULO") + publication.school = getAttributeValueFromNode(xmlNode, "NOME-INSTITUICAO") + return publication + } + + def createConferencias(Node xmlFile, String authorName) { + def conferencePublications = xmlFile.depthFirst().findAll{ it.name() == 'TRABALHO-EM-EVENTOS' } + def conferences = [] + + for (Node currentNode : conferencePublications) { + def newConference = saveNewConferencia(currentNode, authorName); + def status = checkConferenceStatus(newConference) + + if(status && status != PUB_STATUS_DUPLICATED){ + def obj = newConference.properties.findAll{it.key in ["title","publicationDate", "authors", "booktitle", "pages"]} + conferences += ["obj": obj, "status":status] + } } + + return conferences } - private - static void createNewBookChapter(BookChapter newBookChapter, Node dadosBasicos, Node detalhamentoCapitulo, int i) { + private static saveNewConferencia(conferenceNode, authorName) { + def newConference = null + def basicData = conferenceNode?.depthFirst()?.find{ it.name() == 'DADOS-BASICOS-DO-TRABALHO' } + def details = conferenceNode?.depthFirst()?.find{ it.name() == 'DETALHAMENTO-DO-TRABALHO' } + + if (basicData && details) { + def eventName = getAttributeValueFromNode(details, "NOME-DO-EVENTO") + + if (eventName.contains("onferenc")) { + newConference = new Conferencia() - newBookChapter.title = getAttributeValueFromNode(dadosBasicos, "TITULO-DO-CAPITULO-DO-LIVRO") - newBookChapter.publisher = getAttributeValueFromNode(detalhamentoCapitulo, "NOME-DA-EDITORA") + def authorsNode = conferenceNode.depthFirst().findAll{ it.name() == 'AUTORES'} + newConference = (Conferencia) addAuthors(authorsNode, newConference) + if(!newConference.authors.contains(authorName)) return null - if (Publication.findByTitle(newBookChapter.title) == null) - fillBookChapterInfo(newBookChapter, dadosBasicos, i) + newConference.title = eventName + fillPublicationDate(newConference, basicData, "ANO-DO-TRABALHO") + newConference.booktitle = getAttributeValueFromNode(basicData, "TITULO-DO-TRABALHO") + String initialPage = getAttributeValueFromNode(details, "PAGINA-INICIAL") + String finalPage = getAttributeValueFromNode(details, "PAGINA-FINAL") + newConference.pages = initialPage + "-" + finalPage + } + } + return newConference } - private static Publication addAuthors(publication, newPublication) { + static checkConferenceStatus(Conferencia conference){ + if(!conference) return null + def status = PUB_STATUS_STABLE + def conferenceDB = Conferencia.findByTitleAndBooktitle(conference.title, conference.booktitle) + if(conferenceDB) status = checkPublicationStatus(conferenceDB, conference) + return status + } - for (int j = 2; j < publication.size() - 4; ++j) { - newPublication.addToAuthors(getAttributeValueFromNode(publication[j], "NOME-COMPLETO-DO-AUTOR")) + //#if($Article) + def createJournals(Node xmlFile, String authorName) { + def publishedArticles = xmlFile.depthFirst().findAll{ it.name() == 'ARTIGO-PUBLICADO' } + def journals = [] + + for (int i = 0; i < publishedArticles?.size(); ++i) { + def newJournal = saveNewJournal(publishedArticles, i, authorName) + def status = checkJournalStatus(newJournal) + + if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newJournal.properties.findAll{it.key in ["title","publicationDate", "authors", "journal", "volume", "number", "pages"]} + journals += ["obj": obj, "status":status] + } } - return newPublication + return journals } - private static void fillBookChapterInfo(BookChapter newBookChapter, Node dadosBasicos, int i) { - fillPublicationDate(newBookChapter, dadosBasicos, "ANO") + private static saveNewJournal(List publishedArticlesChildren, int i, String authorName) { + List firstArticle = ((Node) publishedArticlesChildren[i]).children() + Node basicData = (Node) firstArticle[0] + Node articleDetails = (Node) firstArticle[1] + Periodico newJournal = new Periodico() + getJournalTitle(basicData, newJournal) + + newJournal = (Periodico) addAuthors(firstArticle, newJournal) + if(!newJournal.authors.contains(authorName)) return null //the user is not author + + fillPublicationDate(newJournal, basicData, "ANO-DO-ARTIGO") + getJournalVolume(articleDetails, newJournal) + getJournalNumber(articleDetails, newJournal) + getJournalNumberOfPages(articleDetails, newJournal) + getPeriodicTitle(articleDetails, newJournal) - newBookChapter.file = 'emptyfile' + i.toString() - newBookChapter.chapter = 2 - newBookChapter.save(flush: false) + return newJournal } - static void createDissertations(Node xmlFile) { - Node dadosGerais = (Node) xmlFile.children()[0] + static checkJournalStatus(Periodico journal){ + if(!journal) return null + def status = XMLService.PUB_STATUS_STABLE + def journalDB = Periodico.findByJournalAndTitle(journal.journal,journal.title) + if(journalDB) status = checkPublicationStatus(journalDB, journal) + return status + } - Node formacaoAcademica = getNodeFromNode(dadosGerais, "FORMACAO-ACADEMICA-TITULACAO") - Node mestrado = (Node) formacaoAcademica.children()[1] - Node doutorado = (Node) formacaoAcademica.children()[2] + private static void getJournalTitle(Node basicData, Periodico newJournal) { + newJournal.title = getAttributeValueFromNode(basicData, "TITULO-DO-ARTIGO") + } - createDissertation(mestrado) - createDissertation(doutorado) + private static void getPeriodicTitle(Node articleDetails, Periodico newJournal) { + newJournal.journal = getAttributeValueFromNode(articleDetails, "TITULO-DO-PERIODICO-OU-REVISTA") } - private static void createDissertation(Node xmlNode) { - Dissertacao newDissertation = new Dissertacao() - newDissertation.title = getAttributeValueFromNode(xmlNode, "TITULO-DA-DISSERTACAO-TESE") + private static void getJournalNumberOfPages(Node articleDetails, Periodico newJournal) { + String initialPage = getAttributeValueFromNode(articleDetails, "PAGINA-INICIAL") + String finalPage = getAttributeValueFromNode(articleDetails, "PAGINA-FINAL") + newJournal.pages = initialPage + "-" + finalPage + } - fillPublicationDate(newDissertation, xmlNode, "ANO-DE-OBTENCAO-DO-TITULO") - newDissertation.school = getAttributeValueFromNode(xmlNode, "NOME-INSTITUICAO") - newDissertation.file = 'no File' - newDissertation.address = 'no Address' - newDissertation.save(flush: false) + private static void getJournalNumber(Node articleDetails, Periodico newJournal) { + String number = getAttributeValueFromNode(articleDetails, "FASCICULO") + if (number.isInteger()) + newJournal.number = number.toInteger() + else + newJournal.number = 1 //if not parsed successfully, least value possible } - static void createConferencias(Node xmlFile) { - Node trabalhosEmEventos = (Node) ((Node) xmlFile.children()[1]).children()[0] + private static void getJournalVolume(Node articleDetails, Periodico newJournal) { + String volume = getAttributeValueFromNode(articleDetails, "VOLUME") + if (volume.isInteger()) + newJournal.volume = volume.toInteger() + else + newJournal.volume = 1 //if not parsed successfully, least value possible + } + //#end - for (Node currentNode : trabalhosEmEventos.children()) { - List nodeConferencia = currentNode.children() - saveNewConferencia(nodeConferencia); - } + private static prepareDate(Date date){ + def year = date.toCalendar().get(Calendar.YEAR) + def newDate = new Date() + newDate.clearTime() + newDate.set(year: year) + return newDate } - private static void saveNewConferencia(List nodeConferencia) { - Node dadosBasicos = (Node) nodeConferencia[0] - Node detalhamento = (Node) nodeConferencia[1] - String nomeEvento = "" - if (((String) detalhamento.name()).equals("DETALHAMENTO-DO-TRABALHO")) - nomeEvento = getAttributeValueFromNode(detalhamento, "NOME-DO-EVENTO") + private static checkPublicationStatus(Publication pubDB, Publication pub){ + def status = XMLService.PUB_STATUS_DUPLICATED - if (nomeEvento.contains("onferenc")) { - Conferencia novaConferencia = new Conferencia() - novaConferencia.title = nomeEvento + //necessário para analisar apenas diferença de ano + if(pubDB.publicationDate && pub.publicationDate) { + pubDB.publicationDate = prepareDate(pubDB.publicationDate) + pub.publicationDate = prepareDate(pub.publicationDate) + } - if (Publication.findByTitle(novaConferencia.title) == null) { - fillPublicationDate(novaConferencia, dadosBasicos, "ANO-DO-TRABALHO") + def missingPropertiesDB = pubDB.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} + def missingProperties = pub.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} - String tryingToParse = getAttributeValueFromNode(dadosBasicos, "TITULO-DO-TRABALHO") - novaConferencia.booktitle = tryingToParse; - tryingToParse = getAttributeValueFromNode(detalhamento, "PAGINA-INICIAL") - String tryingToParse2 = getAttributeValueFromNode(detalhamento, "PAGINA-FINAL") - novaConferencia.pages = tryingToParse + " - " + tryingToParse2 - novaConferencia.file = 'emptyfile' - novaConferencia.save(flush: false) - } + if(missingPropertiesDB != missingProperties){ + status = XMLService.PUB_STATUS_TO_UPDATE } + + def detailsDB = pubDB.properties.findAll{it.key!='id' && it.key != 'members'} - missingPropertiesDB + def details = pub.properties.findAll{it.key!='id '&& it.key != 'members'} - missingProperties + + if(detailsDB != details){ + status = XMLService.PUB_STATUS_CONFLICTED + } + + return status } - static void createOrientations(Node xmlFile, Member user) { - List completedOrientations = findCompletedOrientations(xmlFile) + static Node parseReceivedFile(MultipartHttpServletRequest request) { + MultipartHttpServletRequest mpr = (MultipartHttpServletRequest) request; + MultipartFile f = (MultipartFile) mpr.getFile("file"); + File file = new File("xmlimported.xml"); + f.transferTo(file) + def records = new XmlParser() + records.parse(file) + } - if (!XMLService.isNullOrEmpty(completedOrientations)) - for (int i = 0; i < completedOrientations.size(); i++) - saveNewOrientation(completedOrientations, i, user) + static String getAttributeValueFromNode(Node n, String attribute) { + n?.attribute attribute } - private static void saveNewOrientation(List completedOrientations, int i, Member user) { - Node node = (Node) completedOrientations[i] - Orientation newOrientation = new Orientation() - String name = (String) node.name() + static Node getNodeFromNode(Node n, String nodeName) { + for (Node currentNodeChild : n?.children()) { + if ((currentNodeChild.name() + "").equals((nodeName))) + return currentNodeChild + } + } - fillNewOrientation(name, node, newOrientation, user) - saveOrientation(newOrientation) + static void fillPublicationDate(Publication publication, Node currentNode, String field) { + publication.publicationDate = new Date() + String tryingToParse = getAttributeValueFromNode(currentNode, field) + if (tryingToParse.isInteger()) + publication.publicationDate.set(year: tryingToParse.toInteger()) + } + + //#if($Orientation) + static createOrientations(Node xmlFile, Member user) { + def author = xmlFile.depthFirst().find{it.name() == 'DADOS-GERAIS'}.'@NOME-COMPLETO' + if(author != user.name) return null + + def orientations = [] + Node completedOrientationNode = xmlFile.depthFirst().find{ it.name() == 'ORIENTACOES-CONCLUIDAS' } + orientations = createMasterOrientations(orientations, completedOrientationNode, user) + orientations = createThesisOrientations(orientations, completedOrientationNode, user) + orientations = createUndergraduateResearch(orientations, completedOrientationNode, user) + return orientations } - private static void fillNewOrientation(String name, Node node, Orientation newOrientation, Member user) { - if (name.toLowerCase().contains("mestrado")) { - fillOrientationData(node, newOrientation, user, "Mestrado") - } else if (name.toLowerCase().contains("doutorado")) { - fillOrientationData(node, newOrientation, user, "Doutorado") - } else { - Node children = (Node) (node.children()[0]) - String natureza = (String) children.attribute("NATUREZA") + static private createMasterOrientations(orientations, completedOrientationNode, user){ + def masterOrientations = completedOrientationNode?.getAt("ORIENTACOES-CONCLUIDAS-PARA-MESTRADO") + for(Node orientation: masterOrientations){ + def newOrientation = fillOrientationData(orientation, user, "Mestrado") + def status = checkOrientationStatus(newOrientation, user) - if (isUndergraduateResearch(natureza)) { - fillOrientationData(node, newOrientation, user, "Iniciação Científica") + if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"]} + orientations += ["obj": obj, "status":status] } } + return orientations } - //Only saves if the orientation does not already exist - private static void saveOrientation(Orientation newOrientation) { - if (Orientation.findAll().find { it -> newOrientation.equals(it) } == null) - newOrientation.save(flush: false) - } + static private createThesisOrientations(orientations, completedOrientationNode, user){ + def thesisOrientations = completedOrientationNode?.getAt("ORIENTACOES-CONCLUIDAS-PARA-DOUTORADO") + for(Node orientation: thesisOrientations){ + def newOrientation = fillOrientationData(orientation, user, "Doutorado") + def status = checkOrientationStatus(newOrientation, user) - private static boolean isUndergraduateResearch(String natureza) { - natureza.toLowerCase().contains("iniciacao_cientifica") + if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"]} + orientations += ["obj": obj, "status":status] + } + } + return orientations } - private static boolean isNullOrEmpty(List completedOrientations) { - completedOrientations == null || completedOrientations.size() == 0 + static private createUndergraduateResearch(orientations, completedOrientationNode, user){ + def undergraduateResearch = completedOrientationNode?.getAt("OUTRAS-ORIENTACOES-CONCLUIDAS").findAll{ + it.children().get(0).'@NATUREZA' == "INICIACAO_CIENTIFICA" + } + + for(Node orientation: undergraduateResearch){ + def newOrientation = fillOrientationData(orientation, user, "Iniciação Científica") + def status = checkOrientationStatus(newOrientation, user) + + if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"]} + orientations += ["obj": obj, "status":status] + } + } + return orientations } - private static List findCompletedOrientations(Node xmlFile) { - def orientations = (Node) xmlFile.children()[3] - def completedOrientations = (Node) orientations.children()[0] - List values = completedOrientations.children() - values + static checkOrientationStatus(Orientation orientation, Member user){ + if(!orientation) return null + def status = XMLService.PUB_STATUS_STABLE + def orientationDB = Orientation.findByOrientadorAndTituloTese(user, orientation.tituloTese) + if(orientationDB){ + status = XMLService.PUB_STATUS_DUPLICATED + + def missingPropertiesDB = orientationDB.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} + def missingProperties = orientation.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} + + if(missingPropertiesDB != missingProperties){ + status = XMLService.PUB_STATUS_TO_UPDATE + } + + def detailsDB = orientationDB.properties.findAll{it.key!='id' && it.key != 'members'} - missingPropertiesDB + def details = orientation.properties.findAll{it.key!='id '&& it.key != 'members'} - missingProperties + + if(detailsDB != details){ + status = XMLService.PUB_STATUS_CONFLICTED + } + } + return status } - private static void fillOrientationData(Node node, Orientation newOrientation, Member user, String tipoOrientacao) { + private static fillOrientationData(Node node, Member user, String type) { + Orientation newOrientation = new Orientation(tipo:type) Node basicData = (Node) (node.children()[0]) Node specificData = (Node) (node.children()[1]) - newOrientation.tipo = tipoOrientacao newOrientation.tituloTese = getAttributeValueFromNode(basicData, "TITULO") String ano = getAttributeValueFromNode(basicData, "ANO") newOrientation.anoPublicacao = Integer.parseInt(ano) @@ -391,68 +581,156 @@ class XMLService { newOrientation.instituicao = getAttributeValueFromNode(specificData, "NOME-DA-INSTITUICAO") newOrientation.orientador = user newOrientation.orientando = getAttributeValueFromNode(specificData, "NOME-DO-ORIENTADO") + return newOrientation } + //#end - //#if($Article) - static void createJournals(Node xmlFile) { - Node artigosPublicados = (Node) ((Node) xmlFile.children()[1]).children()[1] - List artigosPublicadosChildren = artigosPublicados.children() + //#if($researchLine) + static createResearchLines(Node xmlFile, String authorName) { + def author = xmlFile.depthFirst().find{it.name() == 'DADOS-GERAIS'}.'@NOME-COMPLETO' + if(author != authorName) return null + + def researchLinesList = [] + def researchAndDevelopment = xmlFile.depthFirst().findAll{ it.name() == 'PESQUISA-E-DESENVOLVIMENTO' } + + for(Node i: researchAndDevelopment){ + def researchLines = i.getAt("LINHA-DE-PESQUISA") + for(Node j:researchLines){ + def newResearchLine = saveResearchLine(j) + def status = checkResearchLineStatus(newResearchLine) + + if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newResearchLine.properties.findAll{it.key in ["name","description"]} + researchLinesList += ["obj": obj, "status":status] + } + } + } + return researchLinesList + } - for (int i = 0; i < artigosPublicadosChildren.size(); ++i) - saveNewJournal(artigosPublicadosChildren, i) + private static saveResearchLine(Node xmlFile) { + ResearchLine newResearchLine = new ResearchLine() + newResearchLine.name = getAttributeValueFromNode(xmlFile, "TITULO-DA-LINHA-DE-PESQUISA") + newResearchLine.description = getAttributeValueFromNode(xmlFile, "OBJETIVOS-LINHA-DE-PESQUISA") + return newResearchLine } - private static void saveNewJournal(List artigosPublicadosChildren, int i) { - List firstArticle = ((Node) artigosPublicadosChildren[i]).children() - Node dadosBasicos = (Node) firstArticle[0] - Node detalhamentoArtigo = (Node) firstArticle[1] - Periodico newJournal = new Periodico() - getJournalTitle(dadosBasicos, newJournal) + static checkResearchLineStatus(ResearchLine researchLine){ + if(!researchLine) return null + def status = XMLService.PUB_STATUS_STABLE + def rlDB = ResearchLine.findByName(researchLine.name) + if(rlDB){ + status = XMLService.PUB_STATUS_DUPLICATED - newJournal = (Periodico) addAuthors(firstArticle, newJournal) + def missingPropertiesDB = rlDB.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} + def missingProperties = researchLine.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} - if (Publication.findByTitle(newJournal.title) == null) { - fillPublicationDate(newJournal, dadosBasicos, "ANO-DO-ARTIGO") - getJournalVolume(detalhamentoArtigo, newJournal) - getJournalNumber(detalhamentoArtigo, newJournal) - getJournalNumberOfPages(detalhamentoArtigo, newJournal) - getPeriodicTitle(detalhamentoArtigo, newJournal) - newJournal.file = 'emptyfile' + i.toString() //files are not available on lattes - newJournal.save(flush: false) + if(missingPropertiesDB != missingProperties){ + status = XMLService.PUB_STATUS_TO_UPDATE + } + + def detailsDB = rlDB.properties.findAll{it.key!='id' && it.key != 'members'} - missingPropertiesDB + def details = researchLine.properties.findAll{it.key!='id '&& it.key != 'members'} - missingProperties + + if(detailsDB != details){ + status = XMLService.PUB_STATUS_CONFLICTED + } } + return status } + //#end + + //#if($researchProject) + static createResearchProjects(Node xmlFile, String authorName) { + def author = xmlFile.depthFirst().find{it.name() == 'DADOS-GERAIS'}.'@NOME-COMPLETO' + if(author != authorName) return null + + def researchProjectsList = [] + def researchProjects = xmlFile.depthFirst().findAll{ it.name() == 'PROJETO-DE-PESQUISA' } - private static void getJournalTitle(Node dadosBasicos, Periodico newJournal) { - newJournal.title = getAttributeValueFromNode(dadosBasicos, "TITULO-DO-ARTIGO") + for(Node project: researchProjects){ + def newProject = saveResearchProject(project) + def status = checkResearchProjectStatus(newProject) + + if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newProject.properties.findAll{ + it.key in ["projectName","description", "status", "responsible", "startYear", "endYear", "members", "responsible"] + } + researchProjectsList += ["obj": obj, "status":status] + } + } + return researchProjectsList } - private static void getPeriodicTitle(Node detalhamentoArtigo, Periodico newJournal) { - newJournal.journal = getAttributeValueFromNode(detalhamentoArtigo, "TITULO-DO-PERIODICO-OU-REVISTA") + static checkResearchProjectStatus(ResearchProject researchProject){ + if(!researchProject) return null + def status = XMLService.PUB_STATUS_STABLE + def researchProjectDB = ResearchProject.findByProjectName(researchProject.projectName) + + if(researchProjectDB){ + status = XMLService.PUB_STATUS_DUPLICATED + + def missingPropertiesDB = researchProjectDB.properties.findAll{it.key != 'id' && !it.value} + def missingProperties = researchProject.properties.findAll{it.key != 'id' && !it.value} + + if(missingPropertiesDB != missingProperties){ + status = XMLService.PUB_STATUS_TO_UPDATE + } + + def detailsDB = researchProjectDB.properties.findAll{it.key!='id'} - missingPropertiesDB + def details = researchProject.properties.findAll{it.key!='id '} - missingProperties + + if(detailsDB != details){ + status = XMLService.PUB_STATUS_CONFLICTED + } + } + + return status } - private static void getJournalNumberOfPages(Node detalhamentoArtigo, Periodico newJournal) { - String tryingToParse = getAttributeValueFromNode(detalhamentoArtigo, "PAGINA-FINAL") - String tryingToParse2 = getAttributeValueFromNode(detalhamentoArtigo, "PAGINA-INICIAL") - if (tryingToParse.isInteger() && tryingToParse2.isInteger()) - newJournal.pages = tryingToParse.toInteger() - tryingToParse2.toInteger() + 1 - else - newJournal.pages = 1 //if not parsed successfully, least value possible + private static saveResearchProject(Node xmlFile) { + ResearchProject newProject = new ResearchProject() + newProject.projectName = getAttributeValueFromNode(xmlFile, "NOME-DO-PROJETO") + newProject.description = getAttributeValueFromNode(xmlFile, "DESCRICAO-DO-PROJETO") + newProject.status = getAttributeValueFromNode(xmlFile, "SITUACAO") + newProject.startYear = getAttributeValueFromNode(xmlFile, "ANO-INICIO").toInteger() + newProject.endYear = getAttributeValueFromNode(xmlFile, "ANO-FIM").equals("") ? 0 : getAttributeValueFromNode(xmlFile, "ANO-FIM").toInteger() + fillProjectMembers(getNodeFromNode(xmlFile, "EQUIPE-DO-PROJETO"), newProject) + /* Por hora essa feature está sendo desconsiderada (16.06.14) + //#if($funder) + fillFunders(getNodeFromNode(xmlFile, "FINANCIADORES-DO-PROJETO"), newProject) + //#end + */ + return newProject } - private static void getJournalNumber(Node detalhamentoArtigo, Periodico newJournal) { - String tryingToParse = getAttributeValueFromNode(detalhamentoArtigo, "FASCICULO") - if (tryingToParse.isInteger()) - newJournal.number = tryingToParse.toInteger() - else - newJournal.number = 1 //if not parsed successfully, least value possible + private static void fillProjectMembers(Node xmlFile, ResearchProject project) { + for (Node node : xmlFile?.children()) { //Para cada integrante presente no projeto + String name = (String) (node.attribute("NOME-COMPLETO")) + Boolean responsavel = ((String) (node.attribute("FLAG-RESPONSAVEL"))).equals("SIM") + if (responsavel) project.responsible = name + else project.addToMembers(name)//.save(validate: true) //não deve mais salvar diretamente (16.06.14) + } } + //#end - private static void getJournalVolume(Node detalhamentoArtigo, Periodico newJournal) { - String tryingToParse = getAttributeValueFromNode(detalhamentoArtigo, "VOLUME") - if (tryingToParse.isInteger()) - newJournal.volume = tryingToParse.toInteger() - else - newJournal.volume = 1 //if not parsed successfully, least value possible + //#if($funder) + private static void fillFunders(Node xmlFile, ResearchProject project) { + for (Node node : xmlFile?.children()) { + String code = getAttributeValueFromNode(node, "CODIGO-INSTITUICAO") + Funder funder = Funder.findByCode(code) + + if (funder) { + project.addToFunders(funder) + } else { + Funder newFunder = new Funder() + newFunder.code = code + newFunder.name = getAttributeValueFromNode(node, "NOME-INSTITUICAO") + newFunder.nature = getAttributeValueFromNode(node, "NATUREZA") + newFunder.save(flush: false) //não deve mais salvar diretamente (16.06.14) + project.addToFunders(newFunder).save(flush: false) + } + } } //#end @@ -475,33 +753,4 @@ class XMLService { newMember.save(flush: false) } - static Node parseReceivedFile(MultipartHttpServletRequest request) { - MultipartHttpServletRequest mpr = (MultipartHttpServletRequest) request; - CommonsMultipartFile f = (CommonsMultipartFile) mpr.getFile("file"); - File file = new File("xmlimported.xml"); - f.transferTo(file) - def records = new XmlParser() - - if (file.length() > 0) - records.parse(file) - } - - static String getAttributeValueFromNode(Node n, String attribute) { - n.attribute attribute - } - - static Node getNodeFromNode(Node n, String nodeName) { - for (Node currentNodeChild : n.children()) { - if ((currentNodeChild.name() + "").equals((nodeName))) - return currentNodeChild - } - } - - static void fillPublicationDate(Publication publication, Node currentNode, String field) { - publication.publicationDate = new Date() - String tryingToParse = getAttributeValueFromNode(currentNode, field) - if (tryingToParse.isInteger()) - publication.publicationDate.set(year: tryingToParse.toInteger()) - } - } diff --git a/test/cucumber/XMLImport.feature b/test/cucumber/XMLImport.feature index cc0dcc6a..c9e5282c 100644 --- a/test/cucumber/XMLImport.feature +++ b/test/cucumber/XMLImport.feature @@ -1,3 +1,4 @@ +#Para executar os testes, alterar o nome do usuário admin para "Paulo Henrique Monteiro Borba" em BootStrap.groovy @i9n Feature: XMLImport As a member of a research group @@ -27,20 +28,17 @@ Feature: XMLImport Then no new publication is stored by the system And the previously stored publications do not change - @ignore Scenario: no file web - Given I am at the "Import XML File" Page - And the system has some publications stored - When I click on "upload" without select a xml file + Given the system has some publications stored + When I click on "upload" at the "Import XML File" Page without select a xml file Then the system outputs an error message And no new publication is stored by the system And the previously stored publications do not change - @ignore Scenario: new publication Given the system has some publications stored - And the system has no journal article entitled "An Abstract Equivalence Notion for Object Models" authored by me - When I upload the file "cv.xml" which contains a journal article entitled "An Abstract Equivalence Notion for Object Models" authored by me + And the system has no journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" authored by me + When I upload the file "cv.xml" which contains a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" authored by me Then the system outputs a list of imported publications which contains the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "stable" And no new publication is stored by the system And the previously stored publications do not change @@ -65,25 +63,22 @@ Feature: XMLImport And no new publication is stored by the system And the previously stored publications do not change - @ignore Scenario: publications with same name and different type - Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" authored by me, among several publications - When I upload the file "cv.xml" which contains a conference article entitled "An Abstract Equivalence Notion for Object Models" authored by me - Then the system outputs a list of imported publications which contains the conference article entitled "An Abstract Equivalence Notion for Object Models" with status "stable" - And no new publication is stored by the system + Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" authored by me, among several publications + When I upload the file "cv.xml" which contains a conference article entitled "An Abstract Equivalence Notion for Object Models" from "Seventh Brazilian Conference on Formal Methods" authored by me + Then no new publication is stored by the system And the previously stored publications do not change + And the system outputs a list of imported publications which contains the conference article entitled "An Abstract Equivalence Notion for Object Models" with status "stable" - @ignore Scenario: duplicated publication with equal details - Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" with some extra information, among several publications - When I upload the file "cv-duplicated.xml" which also contains a journal article entitled "An Abstract Equivalence Notion for Object Models" with the same extra information - Then the system outputs a list of imported publications which does not contain the journal article entitled "An Abstract Equivalence Notion for Object Models" - And the previously stored publications do not change + Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" authored by me, among several publications + When I upload the file "cv.xml" which also contains a journal article entitled "An Abstract Equivalence Notion for Object Models" with the same details information + Then the previously stored publications do not change + And the system outputs a list of imported publications which does not contain the journal article entitled "An Abstract Equivalence Notion for Object Models" - @ignore Scenario: duplicated publications with conflicted details - Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" with year "2003" that is authored by me, among several publications - When I upload the file "cv-duplicatedConflictedDetails.xml" which contains a journal article entitled "An Abstract Equivalence Notion for Object Models" authored by me with year "2004" + Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" and pages "3-21" that is authored by me, among several publications + When I upload the file "cv-duplicatedConflictedDetails.xml" which contains a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" and pages "3-10" authored by me Then the system outputs a list of imported publications which contains the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "conflicted" And no new publication is stored by the system And the previously stored publications do not change @@ -104,13 +99,15 @@ Feature: XMLImport Then the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "conflicted" is removed from the list of imported publications And the previously stored publications do not change + #Esse cenário não faz sentido hoje, porque todos os atributos de uma publicação são obrigatórios, com exceção do filename. + #Daí que não tem como uma publicação importada ter informações extra em relação à uma publicação já existente no sistema. @ignore Scenario: duplicated publications with different details - Given the system has a conference article entitled "An Abstract Equivalence Notion for Object Models" with pages "1-14" that is authored by me - When I upload the file "cv-duplicatedDifferentDetails.xml" which contains a conference article entitled "An Abstract Equivalence Notion for Object Models" with locale "Recife" that is also authored by me - Then the system outputs a list of imported publications which contains the conference article entitled "An Abstract Equivalence Notion for Object Models" with status "to update" - And no new publication is stored by the system + Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" with file name "ArticleExample.pdf" that is authored by me, among several publications + When I upload the file "cv.xml" which contains a journal article entitled "An Abstract Equivalence Notion for Object Models" authored by me + Then no new publication is stored by the system And the previously stored publications do not change + And the system outputs a list of imported publications which contains the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "to update" @ignore Scenario: confirm import of publication with different details @@ -191,14 +188,13 @@ Feature: XMLImport #end #if($ResearchProject) - @ignore Scenario: new research project Given the system has some research projects stored - And the system has no research project named as "Modularização Emergente para Linhas de Produtos de Software" - When I upload the file "cv.xml" which contains a research project named as "Modularização Emergente para Linhas de Produtos de Software" - Then the system outputs a list of imported research projects which contains the one named as "Modularização Emergente para Linhas de Produtos de Software" with status "stable" - And no new research project is stored by the system + And the system has no research project named as "Desenvolvimento Formal de Componentes de Software Reutilizáveis" + When I upload the file "cv.xml" which contains a research project named as "Desenvolvimento Formal de Componentes de Software Reutilizáveis" + Then no new research project is stored by the system And the previously stored research projects do not change + And the system outputs a list of imported research projects which contains the one named as "Desenvolvimento Formal de Componentes de Software Reutilizáveis" with status "stable" @ignore Scenario: confirm import of new research project @@ -220,20 +216,18 @@ Feature: XMLImport And the research project named as "Modularização Emergente para Linhas de Produtos de Software" with status "stable" is removed from the list of imported research projects And the previously stored research projects do not change - @ignore Scenario: duplicated research project - Given the system has a research project named as "Modularização Emergente para Linhas de Produtos de Software" with status "EM_ANDAMENTO", among several research projects - When I upload the file "cv-duplicatedRPE.xml" which contains a research project named as "Modularização Emergente para Linhas de Produtos de Software" with status "EM_ANDAMENTO" - Then the system outputs a list of imported research projects which does not contain the one named as "Modularização Emergente para Linhas de Produtos de Software" - And the previously stored research projects do not change + Given the system has a research project named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas", among several research projects + When I upload the file "cv.xml" which also contains a research project named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" with the same details information + Then the previously stored research projects do not change + And the system outputs a list of imported research projects which does not contain the one named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" - @ignore Scenario: duplicated research project with conflicted details - Given the system has a research project named as "Modularização Emergente para Linhas de Produtos de Software" with status "EM_ANDAMENTO", among several research projects - When I upload the file "cv-duplicatedRPC.xml" which contains a research project named as "Modularização Emergente para Linhas de Produtos de Software" with status "ENCERRADO" - Then the system outputs a list of imported research projects which contains the one named as "Modularidade Emergente" with status "conflicted" - And no new research project is stored by the system + Given the system has a research project named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" with status "CONCLUIDO", among several research projects + When I upload the file "cv-duplicatedRPC.xml" which also contains a research project named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" with status "ENCERRADO" + Then no new research project is stored by the system And the previously stored research projects do not change + And the system outputs a list of imported research projects which contains the one named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" with status "conflicted" @ignore Scenario: confirm import of research project with conflicted details @@ -327,7 +321,7 @@ Feature: XMLImport When I cancel the import of the master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with status "conflicted" And the master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with status "conflicted" is removed from the list of imported orientations And the previously stored orientations do not change -#end + #end # o que acontece quando o arquivo tem publicações já cadastradas? e # publicações com mesmos títulos mas outras partes diferentes? e # se o arquivo nao estiver no formato correto? diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index df70dccb..4982061f 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -1,107 +1,231 @@ -import pages.ArticlePages.ArticlesPage -import pages.BookChapterPage -import pages.Conferencia.ConferenciaPage -import pages.DissertationPage -import pages.LoginPage -import pages.OrientationPages.OrientationsPage -import pages.XMLImportPage -import pages.ferramenta.FerramentaPage +import rgms.authentication.User import rgms.publication.* +import rgms.researchProject.ResearchProject +import steps.ArticleTestDataAndOperations +import steps.ConferenciaTestDataAndOperations +import steps.ResearchProjectTestDadaAndOperations +import steps.XMLImportTestDataAndOperations + +import pages.XMLImportPage + +import javax.servlet.http.HttpServletResponse import static cucumber.api.groovy.EN.* import steps.TestDataAndOperations -import CommonSteps -import org.apache.shiro.util.ThreadContext -import org.apache.shiro.subject.Subject -import org.apache.shiro.SecurityUtils +XMLController xmlController +int publicationsTotal + +//#if($ResearchProject) +int researchProjectsTotal +//#end Given(~'^the system has some publications stored$') { -> + TestDataAndOperations.loginController(this) + XMLImportTestDataAndOperations.initializePublicationDB() + publicationsTotal = 4 + assert Publication.findAll().size() == publicationsTotal +} + +Given(~'^the system has no journal article entitled "([^"]*)" with journal "([^"]*)" authored by me$'){ pubName, journalName -> + assert XMLImportTestDataAndOperations.isANewJournal(pubName, journalName) +} + +When(~'^I upload the file "([^"]*)" which contains a journal article entitled "([^"]*)" with journal "([^"]*)" authored by me$') { filename, pubName, journalName -> + String path = "test" + File.separator + "files" + File.separator + filename + assert XMLImportTestDataAndOperations.fileContainsJournal(path, pubName, journalName) + xmlController = new XMLController() + XMLImportTestDataAndOperations.uploadPublications(xmlController,path) +} + +Then(~'^no new publication is stored by the system$'){ -> + assert Publication.findAll().size() == publicationsTotal +} + +Then(~'^the previously stored publications do not change$'){ -> + // sempre vai conter as publicacoes conf1, conf2, journal1, journal2 + def conf1 = Conferencia.findByTitle(ConferenciaTestDataAndOperations.conferencias[0].title) + assert ConferenciaTestDataAndOperations.conferenciaCompatibleTo(conf1, conf1.title) + + def conf2 = Conferencia.findByTitle(ConferenciaTestDataAndOperations.conferencias[1].title) + assert ConferenciaTestDataAndOperations.conferenciaCompatibleTo(conf2, conf2.title) + + def journal1 = Periodico.findByTitle(ArticleTestDataAndOperations.articles[0].title) + assert ArticleTestDataAndOperations.compatibleTo(journal1, journal1.title) + + def journal2 = Periodico.findByTitle(ArticleTestDataAndOperations.articles[1].title) + assert ArticleTestDataAndOperations.compatibleTo(journal2, journal2.title) + + //caso do sistema já ter um dado periodico + if(publicationsTotal == 5){ + def journal3 = Periodico.findByTitle(ArticleTestDataAndOperations.articles[2].title) + assert ArticleTestDataAndOperations.compatibleTo(journal3, journal3.title) + } +} + +Then(~'^the system outputs a list of imported publications which contains the journal article entitled "([^"]*)" with status "([^"]*)"$'){ pubName, status -> + assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status + assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render + assert xmlController.modelAndView.model.publications.journals.find{it["obj"].title == pubName && it["status"] == status} +} + +Given(~'^the system has a journal article entitled "([^"]*)" with journal "([^"]*)" authored by me, among several publications$'){ pubName, journalName -> + TestDataAndOperations.loginController(this) + XMLImportTestDataAndOperations.initializePublicationDB() + def p = XMLImportTestDataAndOperations.addJournalPublication(pubName, journalName) + publicationsTotal = 5 + assert Publication.findAll().size() == publicationsTotal + + String author = User.findByUsername('admin')?.author?.name + assert p.authors.contains(author) +} + +When(~'^I upload the file "([^"]*)" which contains a conference article entitled "([^"]*)" from "([^"]*)" authored by me$'){ filename, pubName, confName -> + String path = "test" + File.separator + "files" + File.separator + filename + assert XMLImportTestDataAndOperations.fileContainsConference(path, pubName, confName) + + def member = User.findByUsername('admin')?.author + assert !Conferencia.findByBooktitleAndTitle(pubName, confName)?.authors?.contains(member.name) + xmlController = new XMLController() + XMLImportTestDataAndOperations.uploadPublications(xmlController, path) +} +Then(~'^the system outputs a list of imported publications which contains the conference article entitled "([^"]*)" with status "([^"]*)"$'){ pubName, status -> + assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status + assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render + assert xmlController.modelAndView.model.publications.conferences.find{it["obj"].booktitle == pubName && it["status"] == status} +} + +When(~'^I upload the file "([^"]*)" which also contains a journal article entitled "([^"]*)" with the same details information$'){ filename, pubName -> + String path = "test" + File.separator + "files" + File.separator + filename + assert XMLImportTestDataAndOperations.fileContainsJournal(path, pubName, null) + + String author = User.findByUsername('admin')?.author?.name + Periodico dbPub = Periodico.findAllByTitle(pubName).find{ + it.authors.contains(author) + } + assert ArticleTestDataAndOperations.compatibleTo(dbPub, pubName) + xmlController = new XMLController() + XMLImportTestDataAndOperations.uploadPublications(xmlController, path) +} + +Then(~'^the system outputs a list of imported publications which does not contain the journal article entitled "([^"]*)"$'){ pubName -> + assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status + assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render + assert !xmlController.modelAndView.model.publications.journals.find{it["obj"].title == pubName} +} + +Given(~'^the system has a journal article entitled "([^"]*)" with journal "([^"]*)" and pages "([^"]*)" that is authored by me, among several publications$'){ pubName, journalName, pages -> TestDataAndOperations.loginController(this) + XMLImportTestDataAndOperations.initializePublicationDB() + def journal = XMLImportTestDataAndOperations.addJournalPublication(pubName, journalName) + publicationsTotal = 5 + assert Publication.findAll().size() == publicationsTotal + + String author = User.findByUsername('admin')?.author?.name + assert journal.authors.contains(author) + assert journal.pages == pages +} - initialSize = Publication.findAll().size() -} -When(~'^I upload the publications of "([^"]*)"$') { filename -> - String path = "test" + File.separator + "functional" + File.separator + "steps" + File.separator + filename - initialSize = Publication.findAll().size() - TestDataAndOperations.uploadPublications(path) - finalSize = Publication.findAll().size() - assert initialSize < finalSize -} -Then(~'^the system has all the publications of the xml file$') { -> - TestDataAndOperations.logoutController(this) - - //Book Chapters - assert Publication.findByTitle("Refinement of Concurrent Object Oriented Programs") != null - assert Publication.findByTitle("A RUP-Based Software Process Supporting Progressive Implementation") != null - assert Publication.findByTitle("Transformation Laws for Sequential Object-Oriented Programming") != null - assert Publication.findByTitle("Mapping Features to Aspects: A Model-Based Generative Approach") != null - assert Publication.findByTitle("Recommending Mechanisms for Modularizing Mobile Software Variabilities") != null - assert Publication.findByTitle("An Introduction to Software Product Line Refactoring") != null - - //Conferencias - assert Publication.findByTitle("Latin American Conference On Computing (CLEI 1992)") != null - assert Publication.findByTitle("Engineering Distributed Objects Workshop, 21st ACM International Conference on Software Engineering (ICSE 1999)") != null - assert Publication.findByTitle("6th International Conference on Software Reuse (ICSR 2000)") != null - - //Dissertacoes - assert Publication.findByTitle("De especificações formais para protótipos funcionais") != null - assert Publication.findByTitle("Semantics and Refinement for a Concurrent Object Oriented Language") != null - - //Ferramentas - assert Publication.findByTitle("Sherlock") != null - assert Publication.findByTitle("FOOPS Proof Assistant and Simulator") != null - assert Publication.findByTitle("TaRGeT: Test and Requirements Generation Tool") != null - assert Publication.findByTitle("JaTS: Java Transformation System") != null - assert Publication.findByTitle("SGC") != null - assert Publication.findByTitle("FLiP: Ferramenta para Extração de Linhas de Produtos de Software") != null - - //Periodicos - assert Publication.findByTitle("A System For Translating Executable VDM Specifications Into Lazy ML") != null - assert Publication.findByTitle("From VDM Specifications To Functional Prototypes") != null - assert Publication.findByTitle("Basic laws of ROOL: an object-oriented language") != null - assert Publication.findByTitle("Implementing distribution and persistence aspects with AspectJ") != null - assert Publication.findByTitle("Developing adaptive J2ME applications using AspectJ") != null - assert Publication.findByTitle("Algebraic reasoning for object-oriented programming") != null - assert Publication.findByTitle("Refactoring Alloy Specifications") != null - - //Orientacoes - assert Publication.findByTitle("Desenvolvimento de Software Como Um Processo Contínuo e Reversível Usando Bon e Java") != null - assert Publication.findByTitle("Design and Evaluation of an Object-Oriented Formal Specification Language") != null - assert Publication.findByTitle("Um Método para Implementação Orientada a Objetos Usando Java e Banco de Dados Relacional.") != null - assert Publication.findByTitle("Progressive Development of Distributed Object-Oriented Applications") != null - assert Publication.findByTitle("Um Processo de Software com Suporte para Implementação Progressiva") != null -} - -And(~'^I select the upload button at the XML import page$') {-> +When(~'^I upload the file "([^"]*)" which contains a journal article entitled "([^"]*)" with journal "([^"]*)" and pages "([^"]*)" authored by me$'){ + filename, pubName, journalName, pages -> + String path = "test" + File.separator + "files" + File.separator + filename + assert XMLImportTestDataAndOperations.fileContainsJournalWithPages(path, pubName, journalName, pages) + xmlController = new XMLController() + XMLImportTestDataAndOperations.uploadPublications(xmlController, path) +} + +When(~'^I click on "([^"]*)" at the "([^"]*)" Page without select a xml file$'){ option, xmlPage -> + to XMLImportPage at XMLImportPage page.uploadWithoutFile() } -Then(~'^I\'m still on XML import page$') {-> + +Then(~'^the system outputs an error message$') { -> at XMLImportPage + assert page.hasErrorUploadFile() +} + +//#if ($ResearchProject) +Given(~'^the system has some research projects stored$'){ -> + TestDataAndOperations.loginController(this) + XMLImportTestDataAndOperations.initializeResearchProjectDB() + researchProjectsTotal = 2 + assert ResearchProject.findAll().size() == researchProjectsTotal +} + +/*Given(~'^the system has no research project named as "([^"]*)"$'){ projectName -> + assert XMLImportTestDataAndOperations.isANewResearchProject(projectName) +}*/ + +When(~'^I upload the file "([^"]*)" which contains a research project named as "([^"]*)"$'){ filename, projectName -> + String path = "test" + File.separator + "files" + File.separator + filename + assert XMLImportTestDataAndOperations.fileContainsResearchProject(path, projectName) + xmlController = new XMLController() + XMLImportTestDataAndOperations.uploadPublications(xmlController,path) +} + +Then(~'^no new research project is stored by the system$'){ -> + assert ResearchProject.findAll().size() == researchProjectsTotal +} + +Then(~'^the previously stored research projects do not change$'){ -> + def project1 = ResearchProject.findByProjectName(ResearchProjectTestDadaAndOperations.researchProjects[0].projectName) + assert ResearchProjectTestDadaAndOperations.compatibleTo(project1, project1.projectName) + def project2 = ResearchProject.findByProjectName(ResearchProjectTestDadaAndOperations.researchProjects[1].projectName) + assert ResearchProjectTestDadaAndOperations.compatibleTo(project2, project2.projectName) +} + +Then(~'^the system outputs a list of imported research projects which contains the one named as "([^"]*)" with status "([^"]*)"$'){ + projectName, status -> + assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status + assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render + assert xmlController.modelAndView.model.publications.researchProjects.find{it["obj"].projectName == projectName && it["status"] == status} } -And(~'^the publications are not stored by the system$') {-> - to ArticlesPage - at ArticlesPage - page.checkIfArticlesListIsEmpty() - to BookChapterPage - at BookChapterPage - page.checkIfBookChapterListIsEmpty() +Given(~'^the system has a research project named as "([^"]*)", among several research projects$'){ projectName -> + TestDataAndOperations.loginController(this) + XMLImportTestDataAndOperations.initializeResearchProjectDB() + researchProjectsTotal = 2 + assert ResearchProject.findAll().size() == researchProjectsTotal + + def project = ResearchProject.findByProjectName(projectName) + assert project + String author = User.findByUsername('admin')?.author?.name + assert project.members.contains(author) || project.responsible==author +} - to ConferenciaPage - at ConferenciaPage - page.checkIfConferenciaListIsEmpty() +When(~'^I upload the file "([^"]*)" which also contains a research project named as "([^"]*)" with the same details information$'){ + filename, projectName -> + String path = "test" + File.separator + "files" + File.separator + filename + assert XMLImportTestDataAndOperations.fileContainsResearchProject(path, projectName) + xmlController = new XMLController() + XMLImportTestDataAndOperations.uploadPublications(xmlController, path) +} - to DissertationPage - at DissertationPage - page.checkIfDissertationListIsEmpty() +Then(~'^the system outputs a list of imported research projects which does not contain the one named as "([^"]*)"$'){ projectName -> + assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status + assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render + assert !xmlController.modelAndView.model.publications.researchProjects.find{it["obj"].projectName == projectName} +} - to FerramentaPage - at FerramentaPage - page.checkIfFerramentaListIsEmpty() +Given(~'^the system has a research project named as "([^"]*)" with status "([^"]*)", among several research projects$'){ + projectName, projectStatus -> + TestDataAndOperations.loginController(this) + XMLImportTestDataAndOperations.initializeResearchProjectDB() + researchProjectsTotal = 2 + assert ResearchProject.findAll().size() == researchProjectsTotal + + def project = ResearchProject.findByProjectNameAndStatus(projectName, projectStatus) + assert project + String author = User.findByUsername('admin')?.author?.name + assert project.members.contains(author) || project.responsible==author +} - to OrientationsPage - at OrientationsPage - page.checkIfOrientationListIsEmpty() -} \ No newline at end of file +When(~'^I upload the file "([^"]*)" which also contains a research project named as "([^"]*)" with status "([^"]*)"$'){ + filename, projectName, projectStatus -> + String path = "test" + File.separator + "files" + File.separator + filename + assert XMLImportTestDataAndOperations.fileContainsResearchProjectWithStatus(path, projectName, projectStatus) + xmlController = new XMLController() + XMLImportTestDataAndOperations.uploadPublications(xmlController, path) +} +//#end diff --git a/test/files/cv-completo.xml b/test/files/cv-completo.xml new file mode 100644 index 00000000..8be42eea --- /dev/null +++ b/test/files/cv-completo.xml @@ -0,0 +1,3092 @@ + + + + + + + + + + + + + + + + + + + + + + Theorem Proving and Algebra + Algebraic Semantics + + + + + + + + + + + + + + + + + + + + + + + + + + + Introduo a Programao (Orientada a Objetos com + Java) + + Programao Orientada a Objetos (e Java) + Engenharia de Software + Trabalho de Graduao em Engenharia de Software + + + + Paradigmas de Linguagens de Programao + Especificao de Sistemas Distribudos + Trabalho Individual em Engenharia de Software + + Introduo ao RUP--Rational Unified Process + Mtodos Formais + Programao Orientada a Objetos com AspectJ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/files/cv-duplicatedConflictedDetails.xml b/test/files/cv-duplicatedConflictedDetails.xml new file mode 100644 index 00000000..0c384d39 --- /dev/null +++ b/test/files/cv-duplicatedConflictedDetails.xml @@ -0,0 +1,424 @@ + + + + + + + + + + + + + + + + + + + + + + Theorem Proving and Algebra + Algebraic Semantics + + + + + + + + + + + + + + + + + + + + + + + + + + + Introduo a Programao (Orientada a Objetos com + Java) + + Programao Orientada a Objetos (e Java) + Engenharia de Software + Trabalho de Graduao em Engenharia de Software + + + + Paradigmas de Linguagens de Programao + Especificao de Sistemas Distribudos + Trabalho Individual em Engenharia de Software + + Introduo ao RUP--Rational Unified Process + Mtodos Formais + Programao Orientada a Objetos com AspectJ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/files/cv-duplicatedRPC.xml b/test/files/cv-duplicatedRPC.xml new file mode 100644 index 00000000..cc7019ad --- /dev/null +++ b/test/files/cv-duplicatedRPC.xml @@ -0,0 +1,454 @@ + + + + + + + + + + + + + + + + + + + + + + Theorem Proving and Algebra + Algebraic Semantics + + + + + + + + + + + + + + + + + + + + + + + + + + + Introduo a Programao (Orientada a Objetos com + Java) + + Programao Orientada a Objetos (e Java) + Engenharia de Software + Trabalho de Graduao em Engenharia de Software + + + + Paradigmas de Linguagens de Programao + Especificao de Sistemas Distribudos + Trabalho Individual em Engenharia de Software + + Introduo ao RUP--Rational Unified Process + Mtodos Formais + Programao Orientada a Objetos com AspectJ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/files/cv.xml b/test/files/cv.xml new file mode 100644 index 00000000..6e6f3066 --- /dev/null +++ b/test/files/cv.xml @@ -0,0 +1,454 @@ + + + + + + + + + + + + + + + + + + + + + + Theorem Proving and Algebra + Algebraic Semantics + + + + + + + + + + + + + + + + + + + + + + + + + + + Introduo a Programao (Orientada a Objetos com + Java) + + Programao Orientada a Objetos (e Java) + Engenharia de Software + Trabalho de Graduao em Engenharia de Software + + + + Paradigmas de Linguagens de Programao + Especificao de Sistemas Distribudos + Trabalho Individual em Engenharia de Software + + Introduo ao RUP--Rational Unified Process + Mtodos Formais + Programao Orientada a Objetos com AspectJ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/functional/pages/XMLImportPage.groovy b/test/functional/pages/XMLImportPage.groovy index 49ee322d..e8cbcf0d 100644 --- a/test/functional/pages/XMLImportPage.groovy +++ b/test/functional/pages/XMLImportPage.groovy @@ -14,8 +14,8 @@ class XMLImportPage extends Page { //title ==~ /XMLImport Listagem/ GetPageTitle gp = new GetPageTitle() - def currentBookChapter = gp.getMessageServerLocale("xml.label") - def currentTitle = currentBookChapter + " " + gp.getMessageServerLocale("default.button.list.label") + def current = gp.getMessageServerLocale("xml.label") + def currentTitle = current + " " + gp.getMessageServerLocale("default.button.list.label") title ==~ currentTitle } @@ -25,6 +25,11 @@ class XMLImportPage extends Page { } def uploadWithoutFile(){ - $('input.save').click() + $('input[type=submit]').click() + } + + def hasErrorUploadFile() { + GetPageTitle gp = new GetPageTitle() + return gp.msg('default.xml.parserror.message') == $("div", class: "message", role: "status").text() } } diff --git a/test/functional/steps/ArticleTestDataAndOperations.groovy b/test/functional/steps/ArticleTestDataAndOperations.groovy index 1d1427f5..18def74d 100644 --- a/test/functional/steps/ArticleTestDataAndOperations.groovy +++ b/test/functional/steps/ArticleTestDataAndOperations.groovy @@ -18,9 +18,13 @@ class ArticleTestDataAndOperations { [journal: "Theoretical Computer Science", volume: 455, number: 1, pages: "2-30", title: "A theory of software product line refinement", publicationDate: (new Date("12 October 2012"))], - [journal: "Science of Computer Programming", volume: 455, pages: "2-30", + [journal: "Science of Computer Programming", volume: 455, number: 1, pages: "2-30", title: "Algebraic reasoning for object-oriented programming", - publicationDate: (new Date("12 October 2012"))] + publicationDate: (new Date("12 October 2012"))], + [journal: "Eletronic Notes In Theoretical Computer Science", volume: 130, number: 1, pages: "3-21", + title: "An Abstract Equivalence Notion for Object Models", + publicationDate: (new Date("12 October 2005")), + authors: ["Paulo Henrique Monteiro Borba", "Tiago Massoni", "Rohit Gheyi"] as Set] ] static public def findArticleByTitle(String title) { @@ -45,7 +49,7 @@ class ArticleTestDataAndOperations { } else if (testarticle != null && article != null) { compatible = true testarticle.each { key, data -> - compatible = compatible && (article."$key" == data) + if(key != 'publicationDate') compatible = compatible && (article."$key" == data) //para evitar problema de lidar com data } } return compatible diff --git a/test/functional/steps/ConferenciaTestDataAndOperations.groovy b/test/functional/steps/ConferenciaTestDataAndOperations.groovy index 39c4009e..61f345ab 100644 --- a/test/functional/steps/ConferenciaTestDataAndOperations.groovy +++ b/test/functional/steps/ConferenciaTestDataAndOperations.groovy @@ -22,7 +22,8 @@ class ConferenciaTestDataAndOperations { booktitle: "Practices and Patterns", pages: "150-200"], [title: "V Conference on Software Product Lines", publicationDate: (new Date("16 October 2012")), - booktitle: "Practices and Patterns", pages: "50-100"] + booktitle: "Practices and Patterns", pages: "50-100", + authors: ["Paulo Henrique Monteiro Borba", "Tiago Massoni", "Rohit Gheyi"] as Set] ] @@ -43,7 +44,7 @@ class ConferenciaTestDataAndOperations { } else if (testConferencia != null && conferencia != null) { compatible = true testConferencia.each { key, data -> - compatible = compatible && (conferencia."$key" == data) + if(key != 'publicationDate') compatible = compatible && (conferencia."$key" == data) //para evitar problema de lidar com data } } return compatible diff --git a/test/functional/steps/ResearchProjectTestDadaAndOperations.groovy b/test/functional/steps/ResearchProjectTestDadaAndOperations.groovy index e402a19b..c6daa152 100644 --- a/test/functional/steps/ResearchProjectTestDadaAndOperations.groovy +++ b/test/functional/steps/ResearchProjectTestDadaAndOperations.groovy @@ -19,7 +19,7 @@ class ResearchProjectTestDadaAndOperations { startYear: 2000, endYear: 2003, funders: FunderTestDataAndOperations.funder[0], - members: ["Rubens Lopes da Silva"] + members: ["Rubens Lopes da Silva"] as Set ], [projectName:"Implementação Progressiva de Aplicações Orientadas a Aspectos", description:"Neste projeto pretendemso definir e validar um método para a implementação de aplicações orientadas a Aspectos. Em particular, este método deve suportar uma abordagem progressiva para implementação orientada a aspectos, de forma que aspectos de distribuição, concorrência, e persistência não sejam inicialmente considerados pelo processo de implementação, mas sejam gradualmente introduzidos, preservando os requisitos funcionais da aplicação.", @@ -27,11 +27,11 @@ class ResearchProjectTestDadaAndOperations { responsible: "Paulo Henrique Monteiro Borba", startYear: 2001, endYear: 2004, - members: ["Bruno Soares da Silva","Dyego Felipe Oliveira de Penha", "Pedro Henrique Torres Gonçalves"] + members: ["Bruno Soares da Silva","Dyego Felipe Oliveira de Penha", "Pedro Henrique Torres Gonçalves"] as Set ] ] - static private def findResearchProjectByProjectName(String name) { + static def findResearchProjectByProjectName(String name) { researchProjects.find { orientation -> orientation.projectName == name } @@ -67,4 +67,19 @@ class ResearchProjectTestDadaAndOperations { cont.saveReseachProject(records.parse(xml)); cont.response.reset() } + + static public boolean compatibleTo(project, projectName) { + def testProject = findResearchProjectByProjectName(projectName) + testProject.funders = null //desconsiderar provisoriamente + def compatible = false + if (testProject == null && project == null) { + compatible = true + } else if (testProject != null && project != null) { + compatible = true + testProject.each { key, data -> + compatible = compatible && (project."$key" == data) + } + } + return compatible + } } diff --git a/test/functional/steps/TestDataAndOperations.groovy b/test/functional/steps/TestDataAndOperations.groovy index 34006453..83225946 100644 --- a/test/functional/steps/TestDataAndOperations.groovy +++ b/test/functional/steps/TestDataAndOperations.groovy @@ -100,8 +100,9 @@ class TestDataAndOperations { cont.response.reset() } + /* ESSE MÉTODO JÁ FOI DEFINIDO EM ConferenciaTestDataAndOperations.groovy */ static public boolean conferenciaCompatibleTo(conferencia, title) { - def testConferencia = findConferenciaByTitle(title) + def testConferencia = findConferenciaByTitle(title) //o método não existe def compatible = false if (testConferencia == null && conferencia == null) { compatible = true diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy new file mode 100644 index 00000000..a0da275e --- /dev/null +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -0,0 +1,196 @@ +package steps + +import org.springframework.mock.web.MockMultipartFile +import org.springframework.mock.web.MockMultipartHttpServletRequest +import rgms.XMLService +import rgms.authentication.User +import rgms.member.Orientation +import rgms.publication.Conferencia +import rgms.publication.Periodico +import rgms.publication.ResearchLine +import rgms.researchProject.ResearchProject + +/** + * Created by Thaís Burity on 02/06/2014. + */ +class XMLImportTestDataAndOperations { + + static addJournalPublication(pubName, journalName){ + Periodico p = new Periodico(ArticleTestDataAndOperations.findArticleByTitle(pubName)) + p.journal = journalName + p.save(flush: true) + return p + } + + static public void uploadPublications(controller, filename) { + def mockRequest = new MockMultipartHttpServletRequest() + controller.metaClass.request = mockRequest + def uploadedFile = new File(filename) + mockRequest.addFile(new MockMultipartFile("file", uploadedFile.bytes)) + controller.upload() + } + + private static getRootNode(filename){ + def xml = new File(filename) + def cv = new XmlParser().parse(xml) + } + + static extractJournalsFromFile(filename, name){ + def xmlFile = getRootNode(filename) + def publishedArticles = xmlFile.depthFirst().findAll{ it.name() == 'ARTIGO-PUBLICADO' } + def journalArticles = [] + + for (int i = 0; i < publishedArticles?.size(); ++i) { + def newJournal = XMLService.saveNewJournal(publishedArticles, i, name) + if(newJournal) journalArticles += newJournal + } + + return journalArticles + } + + static extractConferencesFromFile(String filename, String name){ + def xmlFile = getRootNode(filename) + def conferencePublications = xmlFile.depthFirst().findAll{ it.name() == 'TRABALHO-EM-EVENTOS' } + def conferences = [] + + for (Node currentNode : conferencePublications) { + def newConference = XMLService.saveNewConferencia(currentNode, name); + if(newConference) conferences += newConference + } + + return conferences + } + + static boolean checkJournal(journalArticles, pubName, journalName) { + def result = false + if(journalArticles){ + def searched + if(journalName){ + searched = journalArticles.findAll{ it.title == pubName && it.journal == journalName } + } + else{ + searched = journalArticles.findAll{ it.title == pubName } + } + if (searched) result = true + } + return result + } + + static boolean checkJournalWithPages(journalArticles, pubName, journalName, pages) { + def result = false + if(journalArticles){ + def searched = journalArticles.findAll{ + it.title==pubName && it.journal == journalName && it.pages==pages + } + if (searched) result = true + } + return result + } + + static boolean checkConference(conferences, pubName, confName) { + def result = false + if(conferences) { + def searched = conferences.findAll { it.booktitle==pubName && it.title==confName } + if (searched) result = true + } + return result + } + + static isANewJournal(title, journal){ + def member = User.findByUsername('admin')?.author + def periodic = Periodico.findByTitleAndJournal(title, journal) + !periodic || !(periodic?.authors?.contains(member.name)) + } + + static fileContainsJournal(path, pubName, journalName){ + def name = User.findByUsername('admin')?.author?.name + def journalArticles = extractJournalsFromFile(path, name) + checkJournal(journalArticles, pubName, journalName) + } + + static fileContainsJournalWithPages(path, pubName, journal, pages){ + def name = User.findByUsername('admin')?.author?.name + def journalArticles = extractJournalsFromFile(path, name) + checkJournalWithPages(journalArticles, pubName, journal, pages) + } + + static fileContainsConference(path, pubName, confName){ + def name = User.findByUsername('admin')?.author?.name + def conferences = extractConferencesFromFile(path, name) + checkConference(conferences, pubName, confName) + } + + static void initializePublicationDB(){ + //salvar 2 artigos de conferencia + Conferencia conf = new Conferencia(ConferenciaTestDataAndOperations.conferencias[0]) + conf.save(flush: true) + conf = new Conferencia(ConferenciaTestDataAndOperations.conferencias[1]) + conf.save(flush: true) + + //salvar 2 artigos de periodico + Periodico journal = new Periodico(ArticleTestDataAndOperations.articles[0]) + journal.save(flush: true) + journal = new Periodico(ArticleTestDataAndOperations.articles[1]) + journal.save(flush: true) + } + + //#if($ResearchLine) + static void initializeResearchLineDB(){ + ResearchLine rl = new ResearchLine(ResearchLineTestDataAndOperations.researchlines[0]) + rl.save(flush: true) + rl = new ResearchLine(ResearchLineTestDataAndOperations.researchlines[1]) + rl.save(flush: true) + } + //#end + + //#if($ResearchProject) + static void initializeResearchProjectDB() { + ResearchProject rp = new ResearchProject(ResearchProjectTestDadaAndOperations.researchProjects[0]) + rp.funders = null + rp.save(flush: true) + rp = new ResearchProject(ResearchProjectTestDadaAndOperations.researchProjects[1]) + rp.save(flush: true) + } + + static isANewResearchProject(projectName){ + def project = ResearchProject.findByProjectName(projectName) + } + + static fileContainsResearchProject(path, projectName){ + def name = User.findByUsername('admin')?.author?.name + def projects = extractResearchProjectsFromFile(path)?.find{ + it.projectName == projectName + } + return projects + } + + static fileContainsResearchProjectWithStatus(path, projectName, status){ + def name = User.findByUsername('admin')?.author?.name + def projects = extractResearchProjectsFromFile(path)?.find{ + it.projectName==projectName && it.status==status + } + return projects + } + + static extractResearchProjectsFromFile(filename){ + def xmlFile = getRootNode(filename) + def projects = xmlFile.depthFirst().findAll{ it.name() == 'PROJETO-DE-PESQUISA' } + def projectsList = [] + + for (Node project: projects) { + def newProject = XMLService.saveResearchProject(project) + if(newProject){ + projectsList += newProject + } + } + return projectsList + } + //#end + + //#if($Orientation) + static void initializeOrientationDB() { + Orientation o = new Orientation(OrientationTestDataAndOperations.findOrientationByTitle(OrientationTestDataAndOperations.orientations[0].tituloTese)) + o.save(flush: true) + } + //#end +} From 54c7368de17cff8c00955a94133444fa2bce7aa1 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sun, 29 Jun 2014 11:50:26 -0300 Subject: [PATCH 02/54] =?UTF-8?q?Exclus=C3=A3o=20de=20m=C3=A9todos=20desne?= =?UTF-8?q?cess=C3=A1rios,=20que=20foram=20indevidamente=20inclu=C3=ADdos.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/XMLImportSteps.groovy | 4 ---- test/functional/steps/XMLImportTestDataAndOperations.groovy | 4 ---- 2 files changed, 8 deletions(-) diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index 4982061f..2da23bf6 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -153,10 +153,6 @@ Given(~'^the system has some research projects stored$'){ -> assert ResearchProject.findAll().size() == researchProjectsTotal } -/*Given(~'^the system has no research project named as "([^"]*)"$'){ projectName -> - assert XMLImportTestDataAndOperations.isANewResearchProject(projectName) -}*/ - When(~'^I upload the file "([^"]*)" which contains a research project named as "([^"]*)"$'){ filename, projectName -> String path = "test" + File.separator + "files" + File.separator + filename assert XMLImportTestDataAndOperations.fileContainsResearchProject(path, projectName) diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index a0da275e..8c7ba310 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -152,10 +152,6 @@ class XMLImportTestDataAndOperations { rp.save(flush: true) } - static isANewResearchProject(projectName){ - def project = ResearchProject.findByProjectName(projectName) - } - static fileContainsResearchProject(path, projectName){ def name = User.findByUsername('admin')?.author?.name def projects = extractResearchProjectsFromFile(path)?.find{ From 9601eb2a87c07e9dff9c251073d41d795563f8b0 Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Tue, 1 Jul 2014 12:06:07 -0300 Subject: [PATCH 03/54] Atividade 4 --- ProductGeneration/featureModel.xml | 15 +- test/cucumber/steps/XMLImportSteps.groovy | 156 +++++++++++++++++++++ test/functional/pages/XMLImportPage.groovy | 15 ++ 3 files changed, 184 insertions(+), 2 deletions(-) diff --git a/ProductGeneration/featureModel.xml b/ProductGeneration/featureModel.xml index 95ffe747..fd1a8a9e 100644 --- a/ProductGeneration/featureModel.xml +++ b/ProductGeneration/featureModel.xml @@ -39,10 +39,21 @@ - + - + + + + + + + + + + + + diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index 2da23bf6..1ea94a3f 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -1,9 +1,17 @@ +import pages.ArticlePages.ArticlesPage +import pages.BookChapterPage +import pages.Conferencia.ConferenciaPage +import pages.DissertationPage +import pages.LoginPage +import pages.OrientationPages.OrientationsPage +import pages.ResearchLinePages.ResearchLinePage import rgms.authentication.User import rgms.publication.* import rgms.researchProject.ResearchProject import steps.ArticleTestDataAndOperations import steps.ConferenciaTestDataAndOperations import steps.ResearchProjectTestDadaAndOperations +import steps.TestDataAndOperationsResearchLine import steps.XMLImportTestDataAndOperations import pages.XMLImportPage @@ -225,3 +233,151 @@ When(~'^I upload the file "([^"]*)" which also contains a research project named XMLImportTestDataAndOperations.uploadPublications(xmlController, path) } //#end + +/** + * @author Kamilla Cardoso + * #if($ResearchLine) + * Scenario: new research line + */ +Given(~'^ The system has some research lines stored$'){ -> + TestDataAndOperations.loginController(this) + initialSize = ResearchLine.findAll().size() +} + +Given(~'^ The system has no research line named as "([^"]*)" associated with me $'){ String nameResearch -> + //Se existe uma linha de pesquisa de nome (nameResearch), mas ela não é existe na lista de pesquisas do usuário atual, + // o usuário logado não à possui + assert ResearchLine.findByName(nameResearch) != PublicationController.getLoggedMember().researchLines.contains(nameResearch) +} + +When(~'^ I upload the file "([^"]*)" which contains a research line named as "([^"]*)" $'){ file, researchLineName -> + TestDataAndOperations.uploadPublications(file) + TestDataAndOperationsResearchLine.findResearchLineByName(researchLineName) +} + +Then(~'^ The system outputs a list of imported research lines which contains the one named as "([^"]*)" with status "([^"]*)" $'){ research_Line, status -> + //lista a quantidades de linha de pesquisas armazenadas para o nome especifico + assert Publication.findAllByResearchLineInListAndTitle(ResearchLine.findAll(), research_Line).size() > 1 + //deve conter apenas uma linha de pesquisa, entao a linha de pesquisa armazenada recentemente é removida + TestDataAndOperationsResearchLine.deleteResearchLine(ResearchLine.findByName(research_Line).getMembers()) + //status definido na descrição deve ser "stable" para a linha de pesquisa que se manteve armazenada + assert ResearchLine.findByName(research_Line).getDescription() == status +} + +Then(~'^ No new research line is stored by the system$'){ -> + finalSize = ResearchLine.findAll().size() +} + +Then(~'^ The previously stored research lines do not change$'){ + assert initialSize == finalSize +} +//#end + +/** + * @author Kamilla Cardoso + * #if($ResearchLine) + * Scenario: import xml file that contains a research line + */ +Given(~'^ The system has no research line named as "([^"]*)" $'){ nameResearch -> + assert ResearchLine.findByName(nameResearch) == null + inicialSize = ResearchLineController.findAll().size() +} + +When(~'^ I upload the file "([^"]*)" which contains a research line named as "([^"]*)" $') { file, research_name -> + TestDataAndOperations.uploadPublications(file) + assert ResearchLine.findByName(research_name) == research_name + finalSize = ResearchLineController.findAll().size() + assert inicialSize < finalSize +} + +Then(~'^ the research line named as "([^"]*)" is stored &'){ research-> + assert ResearchLine.findByName(research) != null +} +//#end + +/** + * @author Kamilla Cardoso + * Scenario: import invalid file + */ +Given(~'^The system has some publications stored $'){ -> + inicial = Publication.findAll().size() +} + +When(~'^ I upload the file "([^"]*)" $') { String typeFile -> + TestDataAndOperations.uploadPublications(typeFile) + currentTypeFile = Publication.findByFile(typeFile).getFile().hasProperty(".xml") + assert currentTypeFile == false + + //Verifica se o arquivo possuia tipo invalido, caso seja e removido do sistema + //Como este arquivo pode ter persistido informacoes entao deve-se deletar todas as informacoes juntamente com o arquivo + + Publication.findAllByFile(typeFile).remove(this) +} + +Then(~'^ no publication is stored by the system $') { -> + finalS = Publication.findAll().size() +} + +Then(~'^ And previusly stored publications do not change $'){-> + assert inicial == finalS + TestDataAndOperations.logoutController(this) +} + +/** + * @author Kamilla Cardoso + * Scenario: invalid file web + */ +Given(~'^I am at the "Import XML File" page$'){ -> + to LoginPage + at LoginPage + page.add("admin", "adminadmin") + at XMLImportPage +} + +When(~'^I select the "([^"]*)" button$'){ String uploadButton -> + at XMLImportPage + page.selectButton(uploadButton) +} + +When(~' I upload the file "([^"]*)"$'){ String file -> + at XMLImportPage + page.uploadFile(file) +} + +Then(~'^ the system outputs an error message$'){ -> + at XMLImportPage + assert page.invalidXML() +} + +And(~'^ no new publication is stored by the system$'){ -> + to ArticlesPage + at ArticlesPage + page.checkIfArticlesListIsEmpty() + + to BookChapterPage + at BookChapterPage + page.checkIfBookChapterListIsEmpty() + + to ConferenciaPage + at ConferenciaPage + page.checkIfConferenciaListIsEmpty() + + to DissertationPage + at DissertationPage + page.checkIfDissertationListIsEmpty() + + to ResearchLinePage + at ResearchLinePage + page.checkIfFerramentaListIsEmpty() + + to OrientationsPage + at OrientationsPage + page.checkIfOrientationListIsEmpty() +} + +And(~'^ the previously stored publications do not change$'){ + to XMLImportPage + at XMLImportPage +} + + diff --git a/test/functional/pages/XMLImportPage.groovy b/test/functional/pages/XMLImportPage.groovy index e8cbcf0d..a10bc77c 100644 --- a/test/functional/pages/XMLImportPage.groovy +++ b/test/functional/pages/XMLImportPage.groovy @@ -32,4 +32,19 @@ class XMLImportPage extends Page { GetPageTitle gp = new GetPageTitle() return gp.msg('default.xml.parserror.message') == $("div", class: "message", role: "status").text() } + + def uploadFile(File file){ + GetPageTitle gp = new GetPageTitle() + if((file.canRead())&&(file.hasProperty(".xml"))){ + $('input.save').click() + }else{ + gp.getMessage("não é possível transferir arquivos deste tipo") + } + } + + def invalidXML(){ + GetPageTitle gp = new GetPageTitle() + return gp.msg('default.xml.parserror.message') == $("div", class: "message", role: "status").text() + + } } From ed1e75a83cefed2b2fe6c084a3df52fc99f18a27 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sat, 5 Jul 2014 12:20:22 -0300 Subject: [PATCH 04/54] =?UTF-8?q?Corre=C3=A7=C3=B5es=20da=20atividade=204:?= =?UTF-8?q?=20Ajuste=20na=20escrita=20dos=20cen=C3=A1rios,=20defini=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20m=C3=A9todos=20para=20melhorar=20a=20leitura=20d?= =?UTF-8?q?o=20c=C3=B3digo=20em=20XMLImportSteps,=20ajuste=20de=20c=C3=B3d?= =?UTF-8?q?igo=20repetido.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rgms/publication/XMLController.groovy | 2 +- test/cucumber/XMLImport.feature | 68 +++++++------- test/cucumber/steps/XMLImportSteps.groovy | 88 +++++++++---------- .../steps/ArticleTestDataAndOperations.groovy | 6 ++ .../XMLImportTestDataAndOperations.groovy | 40 +++++---- 5 files changed, 103 insertions(+), 101 deletions(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index 7cc63ba8..14de2d67 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -154,7 +154,7 @@ class XMLController { } private Closure returnWithMessage = { - String msg, String controller -> + String msg, String controller, publications -> redirectToList(controller) flash.message = message(code: msg) } diff --git a/test/cucumber/XMLImport.feature b/test/cucumber/XMLImport.feature index c9e5282c..7f36a71b 100644 --- a/test/cucumber/XMLImport.feature +++ b/test/cucumber/XMLImport.feature @@ -30,7 +30,7 @@ Feature: XMLImport Scenario: no file web Given the system has some publications stored - When I click on "upload" at the "Import XML File" Page without select a xml file + When I click on "upload" at the "Import XML File" Page without selecting a xml file Then the system outputs an error message And no new publication is stored by the system And the previously stored publications do not change @@ -38,8 +38,8 @@ Feature: XMLImport Scenario: new publication Given the system has some publications stored And the system has no journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" authored by me - When I upload the file "cv.xml" which contains a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" authored by me - Then the system outputs a list of imported publications which contains the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "stable" + When I upload the file "cv.xml" that contains a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" authored by me + Then the system outputs a list of imported publications that contains the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "stable" And no new publication is stored by the system And the previously stored publications do not change @@ -65,21 +65,21 @@ Feature: XMLImport Scenario: publications with same name and different type Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" authored by me, among several publications - When I upload the file "cv.xml" which contains a conference article entitled "An Abstract Equivalence Notion for Object Models" from "Seventh Brazilian Conference on Formal Methods" authored by me + When I upload the file "cv.xml" that contains a conference article entitled "An Abstract Equivalence Notion for Object Models" from "Seventh Brazilian Conference on Formal Methods" authored by me Then no new publication is stored by the system And the previously stored publications do not change - And the system outputs a list of imported publications which contains the conference article entitled "An Abstract Equivalence Notion for Object Models" with status "stable" + And the system outputs a list of imported publications that contains the conference article entitled "An Abstract Equivalence Notion for Object Models" with status "stable" Scenario: duplicated publication with equal details Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" authored by me, among several publications - When I upload the file "cv.xml" which also contains a journal article entitled "An Abstract Equivalence Notion for Object Models" with the same details information + When I upload the file "cv.xml" that also contains a journal article entitled "An Abstract Equivalence Notion for Object Models" with the same details information Then the previously stored publications do not change - And the system outputs a list of imported publications which does not contain the journal article entitled "An Abstract Equivalence Notion for Object Models" + And the system outputs a list of imported publications that does not contain the journal article entitled "An Abstract Equivalence Notion for Object Models" Scenario: duplicated publications with conflicted details Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" and pages "3-21" that is authored by me, among several publications - When I upload the file "cv-duplicatedConflictedDetails.xml" which contains a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" and pages "3-10" authored by me - Then the system outputs a list of imported publications which contains the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "conflicted" + When I upload the file "cv-duplicatedConflictedDetails.xml" that contains a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" and pages "3-10" authored by me + Then the system outputs a list of imported publications that contains the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "conflicted" And no new publication is stored by the system And the previously stored publications do not change @@ -104,15 +104,15 @@ Feature: XMLImport @ignore Scenario: duplicated publications with different details Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" with file name "ArticleExample.pdf" that is authored by me, among several publications - When I upload the file "cv.xml" which contains a journal article entitled "An Abstract Equivalence Notion for Object Models" authored by me + When I upload the file "cv.xml" that contains a journal article entitled "An Abstract Equivalence Notion for Object Models" authored by me Then no new publication is stored by the system And the previously stored publications do not change - And the system outputs a list of imported publications which contains the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "to update" + And the system outputs a list of imported publications that contains the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "to update" @ignore Scenario: confirm import of publication with different details Given the system has a conference article entitled "An Abstract Equivalence Notion for Object Models" with pages "1-14" that is authored by me, among several publications - And the file "cv-duplicatedDifferentDetails.xml", which contains a conference article entitled "An Abstract Equivalence Notion for Object Models" authored by me with locale "Recife", is uploaded + And the file "cv-duplicatedDifferentDetails.xml", that contains a conference article entitled "An Abstract Equivalence Notion for Object Models" authored by me with locale "Recife", is uploaded When I confirm the import of the conference article entitled "An Abstract Equivalence Notion for Object Models" with status "to update" Then the system updates the previously stored journal article entitled "An Abstract Equivalence Notion for Object Models" to include the locale "Recife" And the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "to update" is removed from the list of imported publications @@ -120,7 +120,7 @@ Feature: XMLImport @ignore Scenario: cancel import of publication with different details Given the system has a conference article entitled "An Abstract Equivalence Notion for Object Models" with pages "1-14" that is authored by me, among several publications - And the file "cv-duplicatedDifferentDetails.xml", which contains a conference article entitled "An Abstract Equivalence Notion for Object Models" authored by me with locale "Recife", is uploaded + And the file "cv-duplicatedDifferentDetails.xml", that contains a conference article entitled "An Abstract Equivalence Notion for Object Models" authored by me with locale "Recife", is uploaded When I cancel the import of the conference article entitled "An Abstract Equivalence Notion for Object Models" with status "to update" And the conference article entitled "An Abstract Equivalence Notion for Object Models" with status "conflicted" is removed from the list of imported publications And the previously stored publications do not change @@ -130,8 +130,8 @@ Feature: XMLImport Scenario: new research line Given the system has some research lines stored And the system has no research line named as "Modularidade Emergente" associated with me - When I upload the file "cv.xml" which contains a research line named as "Modularidade Emergente" - Then the system outputs a list of imported research lines which contains the one named as "Modularidade Emergente" with status "stable" + When I upload the file "cv.xml" that contains a research line named as "Modularidade Emergente" + Then the system outputs a list of imported research lines that contains the one named as "Modularidade Emergente" with status "stable" And no new research line is stored by the system And the previously stored research lines do not change @@ -158,15 +158,15 @@ Feature: XMLImport @ignore Scenario: duplicated research line Given the system has a research line named as "Modularidade Emergente" associated with me, with description "Investigar formas alternativas de modularidade.", among others research lines - When I upload the file "cv-duplicatedRLE.xml" which contains a research line named as "Modularidade Emergente" with description "Investigar formas alternativas de modularidade." - Then the system outputs a list of imported research lines which does not contain the one named as "Modularidade Emergente" + When I upload the file "cv-duplicatedRLE.xml" that contains a research line named as "Modularidade Emergente" with description "Investigar formas alternativas de modularidade." + Then the system outputs a list of imported research lines that does not contain the one named as "Modularidade Emergente" And the previously stored research lines do not change @ignore Scenario: duplicated research line with conflicted details Given the system has a research line named as "Modularidade Emergente" associated with me, with description "Investigar formas alternativas de modularidade.", among others research lines - When I upload the file "cv-duplicatedRLC.xml" which contains a research line named as "Modularidade Emergente" with description " Investigar formas alternativas de modularidade com o intuito de promover a produtividade deste processo e a qualidade dos seus produtos." - Then the system outputs a list of imported research lines which contains the one named as "Modularidade Emergente" with status "conflicted" + When I upload the file "cv-duplicatedRLC.xml" that contains a research line named as "Modularidade Emergente" with description " Investigar formas alternativas de modularidade com o intuito de promover a produtividade deste processo e a qualidade dos seus produtos." + Then the system outputs a list of imported research lines that contains the one named as "Modularidade Emergente" with status "conflicted" And no new research line is stored by the system And the previously stored research lines do not change @@ -191,10 +191,10 @@ Feature: XMLImport Scenario: new research project Given the system has some research projects stored And the system has no research project named as "Desenvolvimento Formal de Componentes de Software Reutilizáveis" - When I upload the file "cv.xml" which contains a research project named as "Desenvolvimento Formal de Componentes de Software Reutilizáveis" + When I upload the file "cv.xml" that contains a research project named as "Desenvolvimento Formal de Componentes de Software Reutilizáveis" Then no new research project is stored by the system And the previously stored research projects do not change - And the system outputs a list of imported research projects which contains the one named as "Desenvolvimento Formal de Componentes de Software Reutilizáveis" with status "stable" + And the system outputs a list of imported research projects that contains the one named as "Desenvolvimento Formal de Componentes de Software Reutilizáveis" with status "stable" @ignore Scenario: confirm import of new research project @@ -218,16 +218,16 @@ Feature: XMLImport Scenario: duplicated research project Given the system has a research project named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas", among several research projects - When I upload the file "cv.xml" which also contains a research project named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" with the same details information + When I upload the file "cv.xml" that also contains a research project named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" with the same details information Then the previously stored research projects do not change - And the system outputs a list of imported research projects which does not contain the one named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" + And the system outputs a list of imported research projects that does not contain the one named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" Scenario: duplicated research project with conflicted details Given the system has a research project named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" with status "CONCLUIDO", among several research projects - When I upload the file "cv-duplicatedRPC.xml" which also contains a research project named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" with status "ENCERRADO" + When I upload the file "cv-duplicatedRPC.xml" that also contains a research project named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" with status "ENCERRADO" Then no new research project is stored by the system And the previously stored research projects do not change - And the system outputs a list of imported research projects which contains the one named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" with status "conflicted" + And the system outputs a list of imported research projects that contains the one named as "Implementação Progressiva de Aplicações Orientadas a Objetos Complexas" with status "conflicted" @ignore Scenario: confirm import of research project with conflicted details @@ -251,8 +251,8 @@ Feature: XMLImport Scenario: new orientation Given the system has some orientations stored And the system has no master's orientation entitled "Structuring Adaptive Aplications using AspectJ" - When I upload the file "cv.xml" which contains a master's orientation entitled "Structuring Adaptive Aplications using AspectJ" - Then the system outputs a list of imported orientations which contains the one entitled "Structuring Adaptive Aplications using AspectJ" with status "stable" + When I upload the file "cv.xml" that contains a master's orientation entitled "Structuring Adaptive Aplications using AspectJ" + Then the system outputs a list of imported orientations that contains the one entitled "Structuring Adaptive Aplications using AspectJ" with status "stable" And no new orientation is stored by the system And the previously stored orientations do not change @@ -279,30 +279,30 @@ Feature: XMLImport @ignore Scenario: orientations with same name and different type Given the system has a master's orientation entitled "Structuring Adaptive Aplications using AspectJ" - When I upload the file "cv-orientation.xml" which contains a doctorate's orientation entitled "Structuring Adaptive Aplications using AspectJ" + When I upload the file "cv-orientation.xml" that contains a doctorate's orientation entitled "Structuring Adaptive Aplications using AspectJ" Then the doctorate's orientation entitled "Structuring Adaptive Aplications using AspectJ" is stored by the system And the previously stored master's orientation does not change @ignore Scenario: orientations with same name and different type Given the system has a master's orientation entitled "Structuring Adaptive Aplications using AspectJ", among several orientations - When I upload the file "cv-orientation.xml" which contains a doctorate's orientation entitled "Structuring Adaptive Aplications using AspectJ" - Then the system outputs a list of imported orientations which contains the doctorate's orientation entitled "Structuring Adaptive Aplications using AspectJ" with status "stable" + When I upload the file "cv-orientation.xml" that contains a doctorate's orientation entitled "Structuring Adaptive Aplications using AspectJ" + Then the system outputs a list of imported orientations that contains the doctorate's orientation entitled "Structuring Adaptive Aplications using AspectJ" with status "stable" And no new orientation is stored by the system And the previously stored orientations do not change @ignore Scenario: duplicated orientation with equal details Given the system has a master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with year "2004", among several orientations - When I upload the file "cv-duplicatedOrientationE.xml" which contains a master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with year "2004" - Then the system outputs a list of imported orientations which does not contain the master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with year "2004" + When I upload the file "cv-duplicatedOrientationE.xml" that contains a master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with year "2004" + Then the system outputs a list of imported orientations that does not contain the master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with year "2004" And the previously stored orientations do not change @ignore Scenario: duplicated orientation with conflicted details Given the system has a master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with year "2004", among several orientations - When I upload the file "cv-duplicatedOrientationC.xml" which contains a master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with year "2003" - Then the system outputs a list of imported orientations which contains the master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with status "conflicted" + When I upload the file "cv-duplicatedOrientationC.xml" that contains a master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with year "2003" + Then the system outputs a list of imported orientations that contains the master's orientation entitled "Structuring Adaptive Aplications using AspectJ" with status "conflicted" And no new orientation is stored by the system And the previously stored orientations do not change diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index 1ea94a3f..1d7a8be9 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -22,6 +22,7 @@ import steps.TestDataAndOperations XMLController xmlController int publicationsTotal +String authorName = User.findByUsername('admin')?.author?.name //#if($ResearchProject) int researchProjectsTotal @@ -35,12 +36,12 @@ Given(~'^the system has some publications stored$') { -> } Given(~'^the system has no journal article entitled "([^"]*)" with journal "([^"]*)" authored by me$'){ pubName, journalName -> - assert XMLImportTestDataAndOperations.isANewJournal(pubName, journalName) + assert XMLImportTestDataAndOperations.isANewJournal(authorName, pubName, journalName) } -When(~'^I upload the file "([^"]*)" which contains a journal article entitled "([^"]*)" with journal "([^"]*)" authored by me$') { filename, pubName, journalName -> - String path = "test" + File.separator + "files" + File.separator + filename - assert XMLImportTestDataAndOperations.fileContainsJournal(path, pubName, journalName) +When(~'^I upload the file "([^"]*)" that contains a journal article entitled "([^"]*)" with journal "([^"]*)" authored by me$') { filename, pubName, journalName -> + String path = XMLImportTestDataAndOperations.configureFileName(filename) + assert XMLImportTestDataAndOperations.fileContainsJournal(path, authorName, pubName, journalName) xmlController = new XMLController() XMLImportTestDataAndOperations.uploadPublications(xmlController,path) } @@ -70,7 +71,7 @@ Then(~'^the previously stored publications do not change$'){ -> } } -Then(~'^the system outputs a list of imported publications which contains the journal article entitled "([^"]*)" with status "([^"]*)"$'){ pubName, status -> +Then(~'^the system outputs a list of imported publications that contains the journal article entitled "([^"]*)" with status "([^"]*)"$'){ pubName, status -> assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render assert xmlController.modelAndView.model.publications.journals.find{it["obj"].title == pubName && it["status"] == status} @@ -83,40 +84,36 @@ Given(~'^the system has a journal article entitled "([^"]*)" with journal "([^"] publicationsTotal = 5 assert Publication.findAll().size() == publicationsTotal - String author = User.findByUsername('admin')?.author?.name - assert p.authors.contains(author) + assert p.authors.contains(authorName) } -When(~'^I upload the file "([^"]*)" which contains a conference article entitled "([^"]*)" from "([^"]*)" authored by me$'){ filename, pubName, confName -> - String path = "test" + File.separator + "files" + File.separator + filename - assert XMLImportTestDataAndOperations.fileContainsConference(path, pubName, confName) +When(~'^I upload the file "([^"]*)" that contains a conference article entitled "([^"]*)" from "([^"]*)" authored by me$'){ filename, pubName, confName -> + String path = XMLImportTestDataAndOperations.configureFileName(filename) + assert XMLImportTestDataAndOperations.fileContainsConference(path, authorName, pubName, confName) - def member = User.findByUsername('admin')?.author - assert !Conferencia.findByBooktitleAndTitle(pubName, confName)?.authors?.contains(member.name) + assert !Conferencia.findByBooktitleAndTitle(pubName, confName)?.authors?.contains(authorName) xmlController = new XMLController() XMLImportTestDataAndOperations.uploadPublications(xmlController, path) } -Then(~'^the system outputs a list of imported publications which contains the conference article entitled "([^"]*)" with status "([^"]*)"$'){ pubName, status -> +Then(~'^the system outputs a list of imported publications that contains the conference article entitled "([^"]*)" with status "([^"]*)"$'){ pubName, status -> assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render assert xmlController.modelAndView.model.publications.conferences.find{it["obj"].booktitle == pubName && it["status"] == status} } -When(~'^I upload the file "([^"]*)" which also contains a journal article entitled "([^"]*)" with the same details information$'){ filename, pubName -> - String path = "test" + File.separator + "files" + File.separator + filename - assert XMLImportTestDataAndOperations.fileContainsJournal(path, pubName, null) +When(~'^I upload the file "([^"]*)" that also contains a journal article entitled "([^"]*)" with the same details information$'){ filename, pubName -> + String path = XMLImportTestDataAndOperations.configureFileName(filename) + assert XMLImportTestDataAndOperations.fileContainsJournal(path, authorName, pubName, null) - String author = User.findByUsername('admin')?.author?.name - Periodico dbPub = Periodico.findAllByTitle(pubName).find{ - it.authors.contains(author) - } + Periodico dbPub = ArticleTestDataAndOperations.findArticleByTitleAndAuthor(pubName, authorName) assert ArticleTestDataAndOperations.compatibleTo(dbPub, pubName) + xmlController = new XMLController() XMLImportTestDataAndOperations.uploadPublications(xmlController, path) } -Then(~'^the system outputs a list of imported publications which does not contain the journal article entitled "([^"]*)"$'){ pubName -> +Then(~'^the system outputs a list of imported publications that does not contain the journal article entitled "([^"]*)"$'){ pubName -> assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render assert !xmlController.modelAndView.model.publications.journals.find{it["obj"].title == pubName} @@ -129,20 +126,19 @@ Given(~'^the system has a journal article entitled "([^"]*)" with journal "([^"] publicationsTotal = 5 assert Publication.findAll().size() == publicationsTotal - String author = User.findByUsername('admin')?.author?.name - assert journal.authors.contains(author) + assert journal.authors.contains(authorName) assert journal.pages == pages } -When(~'^I upload the file "([^"]*)" which contains a journal article entitled "([^"]*)" with journal "([^"]*)" and pages "([^"]*)" authored by me$'){ +When(~'^I upload the file "([^"]*)" that contains a journal article entitled "([^"]*)" with journal "([^"]*)" and pages "([^"]*)" authored by me$'){ filename, pubName, journalName, pages -> - String path = "test" + File.separator + "files" + File.separator + filename - assert XMLImportTestDataAndOperations.fileContainsJournalWithPages(path, pubName, journalName, pages) + String path = XMLImportTestDataAndOperations.configureFileName(filename) + assert XMLImportTestDataAndOperations.fileContainsJournalWithPages(path, authorName, pubName, journalName, pages) xmlController = new XMLController() XMLImportTestDataAndOperations.uploadPublications(xmlController, path) } -When(~'^I click on "([^"]*)" at the "([^"]*)" Page without select a xml file$'){ option, xmlPage -> +When(~'^I click on "([^"]*)" at the "([^"]*)" Page without selecting a xml file$'){ option, xmlPage -> to XMLImportPage at XMLImportPage page.uploadWithoutFile() @@ -161,9 +157,9 @@ Given(~'^the system has some research projects stored$'){ -> assert ResearchProject.findAll().size() == researchProjectsTotal } -When(~'^I upload the file "([^"]*)" which contains a research project named as "([^"]*)"$'){ filename, projectName -> - String path = "test" + File.separator + "files" + File.separator + filename - assert XMLImportTestDataAndOperations.fileContainsResearchProject(path, projectName) +When(~'^I upload the file "([^"]*)" that contains a research project named as "([^"]*)"$'){ filename, projectName -> + String path = XMLImportTestDataAndOperations.configureFileName(filename) + assert XMLImportTestDataAndOperations.fileContainsResearchProject(path, projectName, authorName) xmlController = new XMLController() XMLImportTestDataAndOperations.uploadPublications(xmlController,path) } @@ -179,7 +175,7 @@ Then(~'^the previously stored research projects do not change$'){ -> assert ResearchProjectTestDadaAndOperations.compatibleTo(project2, project2.projectName) } -Then(~'^the system outputs a list of imported research projects which contains the one named as "([^"]*)" with status "([^"]*)"$'){ +Then(~'^the system outputs a list of imported research projects that contains the one named as "([^"]*)" with status "([^"]*)"$'){ projectName, status -> assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render @@ -194,19 +190,18 @@ Given(~'^the system has a research project named as "([^"]*)", among several res def project = ResearchProject.findByProjectName(projectName) assert project - String author = User.findByUsername('admin')?.author?.name - assert project.members.contains(author) || project.responsible==author + assert project.members.contains(authorName) || project.responsible==authorName } -When(~'^I upload the file "([^"]*)" which also contains a research project named as "([^"]*)" with the same details information$'){ +When(~'^I upload the file "([^"]*)" that also contains a research project named as "([^"]*)" with the same details information$'){ filename, projectName -> - String path = "test" + File.separator + "files" + File.separator + filename - assert XMLImportTestDataAndOperations.fileContainsResearchProject(path, projectName) + String path = XMLImportTestDataAndOperations.configureFileName(filename) + assert XMLImportTestDataAndOperations.fileContainsResearchProject(path, projectName, authorName) xmlController = new XMLController() XMLImportTestDataAndOperations.uploadPublications(xmlController, path) } -Then(~'^the system outputs a list of imported research projects which does not contain the one named as "([^"]*)"$'){ projectName -> +Then(~'^the system outputs a list of imported research projects that does not contain the one named as "([^"]*)"$'){ projectName -> assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render assert !xmlController.modelAndView.model.publications.researchProjects.find{it["obj"].projectName == projectName} @@ -221,14 +216,13 @@ Given(~'^the system has a research project named as "([^"]*)" with status "([^"] def project = ResearchProject.findByProjectNameAndStatus(projectName, projectStatus) assert project - String author = User.findByUsername('admin')?.author?.name - assert project.members.contains(author) || project.responsible==author + assert project.members.contains(authorName) || project.responsible==authorName } -When(~'^I upload the file "([^"]*)" which also contains a research project named as "([^"]*)" with status "([^"]*)"$'){ +When(~'^I upload the file "([^"]*)" that also contains a research project named as "([^"]*)" with status "([^"]*)"$'){ filename, projectName, projectStatus -> - String path = "test" + File.separator + "files" + File.separator + filename - assert XMLImportTestDataAndOperations.fileContainsResearchProjectWithStatus(path, projectName, projectStatus) + String path = XMLImportTestDataAndOperations.configureFileName(filename) + assert XMLImportTestDataAndOperations.fileContainsResearchProjectWithStatus(path, projectName, authorName, projectStatus) xmlController = new XMLController() XMLImportTestDataAndOperations.uploadPublications(xmlController, path) } @@ -238,7 +232,7 @@ When(~'^I upload the file "([^"]*)" which also contains a research project named * @author Kamilla Cardoso * #if($ResearchLine) * Scenario: new research line - */ + * Given(~'^ The system has some research lines stored$'){ -> TestDataAndOperations.loginController(this) initialSize = ResearchLine.findAll().size() @@ -277,7 +271,7 @@ Then(~'^ The previously stored research lines do not change$'){ * @author Kamilla Cardoso * #if($ResearchLine) * Scenario: import xml file that contains a research line - */ + * Given(~'^ The system has no research line named as "([^"]*)" $'){ nameResearch -> assert ResearchLine.findByName(nameResearch) == null inicialSize = ResearchLineController.findAll().size() @@ -298,7 +292,7 @@ Then(~'^ the research line named as "([^"]*)" is stored &'){ research-> /** * @author Kamilla Cardoso * Scenario: import invalid file - */ + * Given(~'^The system has some publications stored $'){ -> inicial = Publication.findAll().size() } @@ -326,7 +320,7 @@ Then(~'^ And previusly stored publications do not change $'){-> /** * @author Kamilla Cardoso * Scenario: invalid file web - */ + * Given(~'^I am at the "Import XML File" page$'){ -> to LoginPage at LoginPage @@ -379,5 +373,5 @@ And(~'^ the previously stored publications do not change$'){ to XMLImportPage at XMLImportPage } - +*/ diff --git a/test/functional/steps/ArticleTestDataAndOperations.groovy b/test/functional/steps/ArticleTestDataAndOperations.groovy index 18def74d..77b1cea4 100644 --- a/test/functional/steps/ArticleTestDataAndOperations.groovy +++ b/test/functional/steps/ArticleTestDataAndOperations.groovy @@ -97,6 +97,12 @@ class ArticleTestDataAndOperations { static public def path() { return new File(".").getCanonicalPath() + File.separator + "test" + File.separator + "files" + File.separator } + + static findArticleByTitleAndAuthor(title, author){ + Periodico dbPub = Periodico.findAllByTitle(title).find{ + it.authors.contains(author) + } + } } //#end diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index 8c7ba310..c581e60c 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -15,6 +15,10 @@ import rgms.researchProject.ResearchProject */ class XMLImportTestDataAndOperations { + static configureFileName(filename){ + String path = "test" + File.separator + "files" + File.separator + filename + } + static addJournalPublication(pubName, journalName){ Periodico p = new Periodico(ArticleTestDataAndOperations.findArticleByTitle(pubName)) p.journal = journalName @@ -96,27 +100,23 @@ class XMLImportTestDataAndOperations { return result } - static isANewJournal(title, journal){ - def member = User.findByUsername('admin')?.author + static isANewJournal(authorName, title, journal){ def periodic = Periodico.findByTitleAndJournal(title, journal) - !periodic || !(periodic?.authors?.contains(member.name)) + !periodic || !(periodic?.authors?.contains(authorName)) } - static fileContainsJournal(path, pubName, journalName){ - def name = User.findByUsername('admin')?.author?.name - def journalArticles = extractJournalsFromFile(path, name) + static fileContainsJournal(path, authorName, pubName, journalName){ + def journalArticles = extractJournalsFromFile(path, authorName) checkJournal(journalArticles, pubName, journalName) } - static fileContainsJournalWithPages(path, pubName, journal, pages){ - def name = User.findByUsername('admin')?.author?.name - def journalArticles = extractJournalsFromFile(path, name) + static fileContainsJournalWithPages(path, authorName, pubName, journal, pages){ + def journalArticles = extractJournalsFromFile(path, authorName) checkJournalWithPages(journalArticles, pubName, journal, pages) } - static fileContainsConference(path, pubName, confName){ - def name = User.findByUsername('admin')?.author?.name - def conferences = extractConferencesFromFile(path, name) + static fileContainsConference(path, authorName, pubName, confName){ + def conferences = extractConferencesFromFile(path, authorName) checkConference(conferences, pubName, confName) } @@ -152,24 +152,26 @@ class XMLImportTestDataAndOperations { rp.save(flush: true) } - static fileContainsResearchProject(path, projectName){ - def name = User.findByUsername('admin')?.author?.name - def projects = extractResearchProjectsFromFile(path)?.find{ + static fileContainsResearchProject(path, projectName, authorName){ + def projects = extractResearchProjectsFromFile(path, authorName)?.find{ it.projectName == projectName } return projects } - static fileContainsResearchProjectWithStatus(path, projectName, status){ - def name = User.findByUsername('admin')?.author?.name - def projects = extractResearchProjectsFromFile(path)?.find{ + static fileContainsResearchProjectWithStatus(path, projectName, authorName, status){ + def projects = extractResearchProjectsFromFile(path, authorName)?.find{ it.projectName==projectName && it.status==status } return projects } - static extractResearchProjectsFromFile(filename){ + static extractResearchProjectsFromFile(filename, authorName){ def xmlFile = getRootNode(filename) + + def author = xmlFile.depthFirst().find{it.name() == 'DADOS-GERAIS'}.'@NOME-COMPLETO' + if(author != authorName) return null + def projects = xmlFile.depthFirst().findAll{ it.name() == 'PROJETO-DE-PESQUISA' } def projectsList = [] From 59e653bf3ca3d69cde33447096b0c19d984e906e Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sat, 5 Jul 2014 12:47:31 -0300 Subject: [PATCH 05/54] =?UTF-8?q?Atividade=205:=20Corre=C3=A7=C3=A3o=20do?= =?UTF-8?q?=20c=C3=B3digo=20para=20os=20testes=20implementados=20por=20Tha?= =?UTF-8?q?=C3=ADs=20na=20atividade=204=20passarem.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rgms/publication/XMLController.groovy | 18 ++++++++---------- grails-app/views/XML/home.gsp | 3 +++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index 14de2d67..39191be9 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -153,17 +153,15 @@ class XMLController { XMLService.createMember(xmlFile, newMember) } - private Closure returnWithMessage = { - String msg, String controller, publications -> - redirectToList(controller) + private returnWithMessage = { String msg, String controller, publications -> + if (controller == "Publication"){ //importacao via opcao XMLImport no menu da tela inicial do sistema + request.message = message(code: msg) + render(view:"home", model:[publications:publications]) + } + else{ //importacao via outras telas (ainda precisa corrigir) flash.message = message(code: msg) - } - - private def redirectToList(String controllerUsed) { - if (controllerUsed == "Publication") - redirect(uri: '/') - else - redirect(controller: controllerUsed, action: "list", params: params) + redirect(controller: controller, action: "list", params: params) + } } private def getCurrentUser() { diff --git a/grails-app/views/XML/home.gsp b/grails-app/views/XML/home.gsp index d6edee2a..0ccbba4b 100644 --- a/grails-app/views/XML/home.gsp +++ b/grails-app/views/XML/home.gsp @@ -16,6 +16,9 @@
+ +
${request.message}
+
From 8d3dbfbadc4069346c8409a2dbf2c3bd38be587e Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sat, 5 Jul 2014 14:13:41 -0300 Subject: [PATCH 06/54] =?UTF-8?q?Ajuste=20em=20c=C3=B3digo=20referente=20?= =?UTF-8?q?=C3=A0=20feature=20Funder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/services/rgms/XMLService.groovy | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index 256cca76..cecdc5e9 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -696,26 +696,24 @@ class XMLService { newProject.startYear = getAttributeValueFromNode(xmlFile, "ANO-INICIO").toInteger() newProject.endYear = getAttributeValueFromNode(xmlFile, "ANO-FIM").equals("") ? 0 : getAttributeValueFromNode(xmlFile, "ANO-FIM").toInteger() fillProjectMembers(getNodeFromNode(xmlFile, "EQUIPE-DO-PROJETO"), newProject) - /* Por hora essa feature está sendo desconsiderada (16.06.14) - //#if($funder) - fillFunders(getNodeFromNode(xmlFile, "FINANCIADORES-DO-PROJETO"), newProject) - //#end - */ + //#if($funder) + fillFunders(getNodeFromNode(xmlFile, "FINANCIADORES-DO-PROJETO"), newProject) + //#end return newProject } private static void fillProjectMembers(Node xmlFile, ResearchProject project) { - for (Node node : xmlFile?.children()) { //Para cada integrante presente no projeto + for (Node node : xmlFile?.children()) { String name = (String) (node.attribute("NOME-COMPLETO")) Boolean responsavel = ((String) (node.attribute("FLAG-RESPONSAVEL"))).equals("SIM") if (responsavel) project.responsible = name - else project.addToMembers(name)//.save(validate: true) //não deve mais salvar diretamente (16.06.14) + else project.addToMembers(name) } } //#end //#if($funder) - private static void fillFunders(Node xmlFile, ResearchProject project) { + private static fillFunders(Node xmlFile, ResearchProject project) { for (Node node : xmlFile?.children()) { String code = getAttributeValueFromNode(node, "CODIGO-INSTITUICAO") Funder funder = Funder.findByCode(code) @@ -727,10 +725,10 @@ class XMLService { newFunder.code = code newFunder.name = getAttributeValueFromNode(node, "NOME-INSTITUICAO") newFunder.nature = getAttributeValueFromNode(node, "NATUREZA") - newFunder.save(flush: false) //não deve mais salvar diretamente (16.06.14) - project.addToFunders(newFunder).save(flush: false) + project.addToFunders(newFunder) } } + return project } //#end From d347ccab16d04005019760d6d4b048ba5cc1e7a6 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sat, 5 Jul 2014 18:30:32 -0300 Subject: [PATCH 07/54] =?UTF-8?q?Ajuste=20de=20testes=20de=20XMLImport:=20?= =?UTF-8?q?inclus=C3=A3o=20de=20assert=20ap=C3=B3s=20upload=20de=20arquivo?= =?UTF-8?q?,=20reorganiza=C3=A7=C3=A3o=20de=20asserts=20em=20alguns=20step?= =?UTF-8?q?s,=20corre=C3=A7=C3=A3o=20de=20bug=20referente=20ao=20acesso=20?= =?UTF-8?q?ao=20atributo=20publicationDate=20das=20publica=C3=A7=C3=B5es?= =?UTF-8?q?=20importadas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/services/rgms/XMLService.groovy | 46 +++++------- test/cucumber/XMLImport.feature | 4 +- test/cucumber/steps/XMLImportSteps.groovy | 74 ++++++++++++------- .../steps/ArticleTestDataAndOperations.groovy | 2 +- .../ConferenciaTestDataAndOperations.groovy | 2 +- .../XMLImportTestDataAndOperations.groovy | 1 - 6 files changed, 70 insertions(+), 59 deletions(-) diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index cecdc5e9..e74128da 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -427,37 +427,27 @@ class XMLService { } //#end - private static prepareDate(Date date){ - def year = date.toCalendar().get(Calendar.YEAR) - def newDate = new Date() - newDate.clearTime() - newDate.set(year: year) - return newDate - } - private static checkPublicationStatus(Publication pubDB, Publication pub){ def status = XMLService.PUB_STATUS_DUPLICATED - //necessário para analisar apenas diferença de ano - if(pubDB.publicationDate && pub.publicationDate) { - pubDB.publicationDate = prepareDate(pubDB.publicationDate) - pub.publicationDate = prepareDate(pub.publicationDate) - } - - def missingPropertiesDB = pubDB.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} - def missingProperties = pub.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} - + def missingPropertiesDB = pubDB.properties.findAll{it.key != 'id' && !it.value} + def missingProperties = pub.properties.findAll{it.key != 'id' && !it.value} if(missingPropertiesDB != missingProperties){ status = XMLService.PUB_STATUS_TO_UPDATE } - def detailsDB = pubDB.properties.findAll{it.key!='id' && it.key != 'members'} - missingPropertiesDB - def details = pub.properties.findAll{it.key!='id '&& it.key != 'members'} - missingProperties - + def detailsDB = pubDB.properties.findAll{it.key!='id' && it.key!='publicationDate'} - missingPropertiesDB + def details = pub.properties.findAll{it.key!='id' && it.key!='publicationDate'} - missingProperties if(detailsDB != details){ status = XMLService.PUB_STATUS_CONFLICTED } + def date1 = pubDB.publicationDate?.toCalendar().get(Calendar.YEAR) + def date2 = pub.publicationDate?.toCalendar().get(Calendar.YEAR) + if(date1 && date2 && date1!=date2){ + status = XMLService.PUB_STATUS_CONFLICTED + } + return status } @@ -553,15 +543,15 @@ class XMLService { if(orientationDB){ status = XMLService.PUB_STATUS_DUPLICATED - def missingPropertiesDB = orientationDB.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} - def missingProperties = orientation.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} + def missingPropertiesDB = orientationDB.properties.findAll{it.key != 'id' && !it.value} + def missingProperties = orientation.properties.findAll{it.key != 'id' && !it.value} if(missingPropertiesDB != missingProperties){ status = XMLService.PUB_STATUS_TO_UPDATE } - def detailsDB = orientationDB.properties.findAll{it.key!='id' && it.key != 'members'} - missingPropertiesDB - def details = orientation.properties.findAll{it.key!='id '&& it.key != 'members'} - missingProperties + def detailsDB = orientationDB.properties.findAll{it.key!='id'} - missingPropertiesDB + def details = orientation.properties.findAll{it.key!='id'} - missingProperties if(detailsDB != details){ status = XMLService.PUB_STATUS_CONFLICTED @@ -622,15 +612,15 @@ class XMLService { if(rlDB){ status = XMLService.PUB_STATUS_DUPLICATED - def missingPropertiesDB = rlDB.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} - def missingProperties = researchLine.properties.findAll{it.key != 'id' && it.key != 'members' && !it.value} + def missingPropertiesDB = rlDB.properties.findAll{it.key != 'id' && !it.value} + def missingProperties = researchLine.properties.findAll{it.key != 'id' && !it.value} if(missingPropertiesDB != missingProperties){ status = XMLService.PUB_STATUS_TO_UPDATE } - def detailsDB = rlDB.properties.findAll{it.key!='id' && it.key != 'members'} - missingPropertiesDB - def details = researchLine.properties.findAll{it.key!='id '&& it.key != 'members'} - missingProperties + def detailsDB = rlDB.properties.findAll{it.key!='id'} - missingPropertiesDB + def details = researchLine.properties.findAll{it.key!='id'} - missingProperties if(detailsDB != details){ status = XMLService.PUB_STATUS_CONFLICTED diff --git a/test/cucumber/XMLImport.feature b/test/cucumber/XMLImport.feature index 7f36a71b..e94fecb2 100644 --- a/test/cucumber/XMLImport.feature +++ b/test/cucumber/XMLImport.feature @@ -79,9 +79,9 @@ Feature: XMLImport Scenario: duplicated publications with conflicted details Given the system has a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" and pages "3-21" that is authored by me, among several publications When I upload the file "cv-duplicatedConflictedDetails.xml" that contains a journal article entitled "An Abstract Equivalence Notion for Object Models" with journal "Eletronic Notes In Theoretical Computer Science" and pages "3-10" authored by me - Then the system outputs a list of imported publications that contains the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "conflicted" - And no new publication is stored by the system + Then no new publication is stored by the system And the previously stored publications do not change + And the system outputs a list of imported publications that contains the journal article entitled "An Abstract Equivalence Notion for Object Models" with status "conflicted" @ignore Scenario: confirm import of publication with conflicted details diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index 1d7a8be9..8fe6408e 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -42,8 +42,13 @@ Given(~'^the system has no journal article entitled "([^"]*)" with journal "([^" When(~'^I upload the file "([^"]*)" that contains a journal article entitled "([^"]*)" with journal "([^"]*)" authored by me$') { filename, pubName, journalName -> String path = XMLImportTestDataAndOperations.configureFileName(filename) assert XMLImportTestDataAndOperations.fileContainsJournal(path, authorName, pubName, journalName) + + File importedFile = new File("xmlimported.xml") + importedFile.setText("") + assert importedFile.length()==0 xmlController = new XMLController() XMLImportTestDataAndOperations.uploadPublications(xmlController,path) + assert importedFile.length()>0 } Then(~'^no new publication is stored by the system$'){ -> @@ -80,20 +85,22 @@ Then(~'^the system outputs a list of imported publications that contains the jou Given(~'^the system has a journal article entitled "([^"]*)" with journal "([^"]*)" authored by me, among several publications$'){ pubName, journalName -> TestDataAndOperations.loginController(this) XMLImportTestDataAndOperations.initializePublicationDB() - def p = XMLImportTestDataAndOperations.addJournalPublication(pubName, journalName) + XMLImportTestDataAndOperations.addJournalPublication(pubName, journalName) publicationsTotal = 5 - assert Publication.findAll().size() == publicationsTotal - - assert p.authors.contains(authorName) + assert Periodico.findByTitleAndJournal(pubName, journalName).authors.contains(authorName) } When(~'^I upload the file "([^"]*)" that contains a conference article entitled "([^"]*)" from "([^"]*)" authored by me$'){ filename, pubName, confName -> String path = XMLImportTestDataAndOperations.configureFileName(filename) assert XMLImportTestDataAndOperations.fileContainsConference(path, authorName, pubName, confName) - assert !Conferencia.findByBooktitleAndTitle(pubName, confName)?.authors?.contains(authorName) + + File importedFile = new File("xmlimported.xml") + importedFile.setText("") + assert importedFile.length()==0 xmlController = new XMLController() - XMLImportTestDataAndOperations.uploadPublications(xmlController, path) + XMLImportTestDataAndOperations.uploadPublications(xmlController,path) + assert importedFile.length()>0 } Then(~'^the system outputs a list of imported publications that contains the conference article entitled "([^"]*)" with status "([^"]*)"$'){ pubName, status -> @@ -109,8 +116,12 @@ When(~'^I upload the file "([^"]*)" that also contains a journal article entitle Periodico dbPub = ArticleTestDataAndOperations.findArticleByTitleAndAuthor(pubName, authorName) assert ArticleTestDataAndOperations.compatibleTo(dbPub, pubName) + File importedFile = new File("xmlimported.xml") + importedFile.setText("") + assert importedFile.length()==0 xmlController = new XMLController() XMLImportTestDataAndOperations.uploadPublications(xmlController, path) + assert importedFile.length()>0 } Then(~'^the system outputs a list of imported publications that does not contain the journal article entitled "([^"]*)"$'){ pubName -> @@ -122,20 +133,24 @@ Then(~'^the system outputs a list of imported publications that does not contain Given(~'^the system has a journal article entitled "([^"]*)" with journal "([^"]*)" and pages "([^"]*)" that is authored by me, among several publications$'){ pubName, journalName, pages -> TestDataAndOperations.loginController(this) XMLImportTestDataAndOperations.initializePublicationDB() - def journal = XMLImportTestDataAndOperations.addJournalPublication(pubName, journalName) + XMLImportTestDataAndOperations.addJournalPublication(pubName, journalName) publicationsTotal = 5 - assert Publication.findAll().size() == publicationsTotal - - assert journal.authors.contains(authorName) + def journal = Periodico.findByTitleAndJournal(pubName, journalName) assert journal.pages == pages + assert journal.authors.contains(authorName) } When(~'^I upload the file "([^"]*)" that contains a journal article entitled "([^"]*)" with journal "([^"]*)" and pages "([^"]*)" authored by me$'){ filename, pubName, journalName, pages -> String path = XMLImportTestDataAndOperations.configureFileName(filename) assert XMLImportTestDataAndOperations.fileContainsJournalWithPages(path, authorName, pubName, journalName, pages) + + File importedFile = new File("xmlimported.xml") + importedFile.setText("") + assert importedFile.length()==0 xmlController = new XMLController() - XMLImportTestDataAndOperations.uploadPublications(xmlController, path) + XMLImportTestDataAndOperations.uploadPublications(xmlController,path) + assert importedFile.length()>0 } When(~'^I click on "([^"]*)" at the "([^"]*)" Page without selecting a xml file$'){ option, xmlPage -> @@ -160,8 +175,13 @@ Given(~'^the system has some research projects stored$'){ -> When(~'^I upload the file "([^"]*)" that contains a research project named as "([^"]*)"$'){ filename, projectName -> String path = XMLImportTestDataAndOperations.configureFileName(filename) assert XMLImportTestDataAndOperations.fileContainsResearchProject(path, projectName, authorName) + + File importedFile = new File("xmlimported.xml") + importedFile.setText("") + assert importedFile.length()==0 xmlController = new XMLController() XMLImportTestDataAndOperations.uploadPublications(xmlController,path) + assert importedFile.length()>0 } Then(~'^no new research project is stored by the system$'){ -> @@ -186,10 +206,7 @@ Given(~'^the system has a research project named as "([^"]*)", among several res TestDataAndOperations.loginController(this) XMLImportTestDataAndOperations.initializeResearchProjectDB() researchProjectsTotal = 2 - assert ResearchProject.findAll().size() == researchProjectsTotal - def project = ResearchProject.findByProjectName(projectName) - assert project assert project.members.contains(authorName) || project.responsible==authorName } @@ -197,8 +214,13 @@ When(~'^I upload the file "([^"]*)" that also contains a research project named filename, projectName -> String path = XMLImportTestDataAndOperations.configureFileName(filename) assert XMLImportTestDataAndOperations.fileContainsResearchProject(path, projectName, authorName) + + File importedFile = new File("xmlimported.xml") + importedFile.setText("") + assert importedFile.length()==0 xmlController = new XMLController() - XMLImportTestDataAndOperations.uploadPublications(xmlController, path) + XMLImportTestDataAndOperations.uploadPublications(xmlController,path) + assert importedFile.length()>0 } Then(~'^the system outputs a list of imported research projects that does not contain the one named as "([^"]*)"$'){ projectName -> @@ -212,10 +234,7 @@ Given(~'^the system has a research project named as "([^"]*)" with status "([^"] TestDataAndOperations.loginController(this) XMLImportTestDataAndOperations.initializeResearchProjectDB() researchProjectsTotal = 2 - assert ResearchProject.findAll().size() == researchProjectsTotal - def project = ResearchProject.findByProjectNameAndStatus(projectName, projectStatus) - assert project assert project.members.contains(authorName) || project.responsible==authorName } @@ -223,8 +242,13 @@ When(~'^I upload the file "([^"]*)" that also contains a research project named filename, projectName, projectStatus -> String path = XMLImportTestDataAndOperations.configureFileName(filename) assert XMLImportTestDataAndOperations.fileContainsResearchProjectWithStatus(path, projectName, authorName, projectStatus) + + File importedFile = new File("xmlimported.xml") + importedFile.setText("") + assert importedFile.length()==0 xmlController = new XMLController() - XMLImportTestDataAndOperations.uploadPublications(xmlController, path) + XMLImportTestDataAndOperations.uploadPublications(xmlController,path) + assert importedFile.length()>0 } //#end @@ -232,7 +256,7 @@ When(~'^I upload the file "([^"]*)" that also contains a research project named * @author Kamilla Cardoso * #if($ResearchLine) * Scenario: new research line - * + */ Given(~'^ The system has some research lines stored$'){ -> TestDataAndOperations.loginController(this) initialSize = ResearchLine.findAll().size() @@ -271,7 +295,7 @@ Then(~'^ The previously stored research lines do not change$'){ * @author Kamilla Cardoso * #if($ResearchLine) * Scenario: import xml file that contains a research line - * + */ Given(~'^ The system has no research line named as "([^"]*)" $'){ nameResearch -> assert ResearchLine.findByName(nameResearch) == null inicialSize = ResearchLineController.findAll().size() @@ -292,7 +316,7 @@ Then(~'^ the research line named as "([^"]*)" is stored &'){ research-> /** * @author Kamilla Cardoso * Scenario: import invalid file - * + */ Given(~'^The system has some publications stored $'){ -> inicial = Publication.findAll().size() } @@ -320,7 +344,7 @@ Then(~'^ And previusly stored publications do not change $'){-> /** * @author Kamilla Cardoso * Scenario: invalid file web - * + */ Given(~'^I am at the "Import XML File" page$'){ -> to LoginPage at LoginPage @@ -372,6 +396,4 @@ And(~'^ no new publication is stored by the system$'){ -> And(~'^ the previously stored publications do not change$'){ to XMLImportPage at XMLImportPage -} -*/ - +} \ No newline at end of file diff --git a/test/functional/steps/ArticleTestDataAndOperations.groovy b/test/functional/steps/ArticleTestDataAndOperations.groovy index 77b1cea4..93a37fb4 100644 --- a/test/functional/steps/ArticleTestDataAndOperations.groovy +++ b/test/functional/steps/ArticleTestDataAndOperations.groovy @@ -49,7 +49,7 @@ class ArticleTestDataAndOperations { } else if (testarticle != null && article != null) { compatible = true testarticle.each { key, data -> - if(key != 'publicationDate') compatible = compatible && (article."$key" == data) //para evitar problema de lidar com data + compatible = compatible && (article."$key" == data) } } return compatible diff --git a/test/functional/steps/ConferenciaTestDataAndOperations.groovy b/test/functional/steps/ConferenciaTestDataAndOperations.groovy index 61f345ab..d2d0da27 100644 --- a/test/functional/steps/ConferenciaTestDataAndOperations.groovy +++ b/test/functional/steps/ConferenciaTestDataAndOperations.groovy @@ -44,7 +44,7 @@ class ConferenciaTestDataAndOperations { } else if (testConferencia != null && conferencia != null) { compatible = true testConferencia.each { key, data -> - if(key != 'publicationDate') compatible = compatible && (conferencia."$key" == data) //para evitar problema de lidar com data + compatible = compatible && (conferencia."$key" == data) } } return compatible diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index c581e60c..cd32786a 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -23,7 +23,6 @@ class XMLImportTestDataAndOperations { Periodico p = new Periodico(ArticleTestDataAndOperations.findArticleByTitle(pubName)) p.journal = journalName p.save(flush: true) - return p } static public void uploadPublications(controller, filename) { From 96d93d6ddd14c8e375863b3fbcfd23c001d2a959 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sun, 6 Jul 2014 10:04:36 -0300 Subject: [PATCH 08/54] =?UTF-8?q?Corre=C3=A7=C3=A3o=20no=20tratamento=20ao?= =?UTF-8?q?=20atributo=20"members"=20das=20publica=C3=A7=C3=B5es=20importa?= =?UTF-8?q?das.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/services/rgms/XMLService.groovy | 12 +++++++++--- .../steps/ArticleTestDataAndOperations.groovy | 6 +++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index e74128da..8951050f 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -117,6 +117,7 @@ class XMLService { Node additionalInfo = getNodeFromNode(currentNode, "INFORMACOES-ADICIONAIS") Ferramenta newTool = new Ferramenta() + newTool.members = [] newTool = (Ferramenta) addAuthors(currentNode, newTool) if(!newTool.authors.contains(authorName)) return null //the user is not author @@ -160,8 +161,9 @@ class XMLService { List book = currentNode.children() Node basicData = (Node) book[0] Node bookDetails = (Node) book[1] - Book newBook = new Book() + Book newBook = new Book() + newBook.members = [] newBook = (Book) addAuthors(currentNode, newBook) if(!newBook.authors.contains(authorName)) return null //the user is not author @@ -204,6 +206,7 @@ class XMLService { Node chapterDetails = (Node) bookChapter[1] BookChapter newBookChapter = new BookChapter() + newBookChapter.members = [] newBookChapter = (BookChapter) addAuthors(bookChapter, newBookChapter) if(!newBookChapter.authors.contains(authorName)) return null @@ -252,6 +255,7 @@ class XMLService { if(author != authorName) return null Dissertacao newDissertation = new Dissertacao() + newDissertation.members = [] newDissertation = getDissertationOrThesisDetails(mestrado, newDissertation) newDissertation.addToAuthors(author) @@ -287,6 +291,7 @@ class XMLService { if(author != authorName) return null Tese newThesis = new Tese() + newThesis.members = [] newThesis = getDissertationOrThesisDetails(doutorado, newThesis) newThesis.addToAuthors(author) @@ -327,7 +332,7 @@ class XMLService { if (eventName.contains("onferenc")) { newConference = new Conferencia() - + newConference.members = [] def authorsNode = conferenceNode.depthFirst().findAll{ it.name() == 'AUTORES'} newConference = (Conferencia) addAuthors(authorsNode, newConference) if(!newConference.authors.contains(authorName)) return null @@ -375,7 +380,7 @@ class XMLService { Node articleDetails = (Node) firstArticle[1] Periodico newJournal = new Periodico() getJournalTitle(basicData, newJournal) - + newJournal.members = [] newJournal = (Periodico) addAuthors(firstArticle, newJournal) if(!newJournal.authors.contains(authorName)) return null //the user is not author @@ -600,6 +605,7 @@ class XMLService { private static saveResearchLine(Node xmlFile) { ResearchLine newResearchLine = new ResearchLine() + newResearchLine.members = [] newResearchLine.name = getAttributeValueFromNode(xmlFile, "TITULO-DA-LINHA-DE-PESQUISA") newResearchLine.description = getAttributeValueFromNode(xmlFile, "OBJETIVOS-LINHA-DE-PESQUISA") return newResearchLine diff --git a/test/functional/steps/ArticleTestDataAndOperations.groovy b/test/functional/steps/ArticleTestDataAndOperations.groovy index 93a37fb4..1ec309a0 100644 --- a/test/functional/steps/ArticleTestDataAndOperations.groovy +++ b/test/functional/steps/ArticleTestDataAndOperations.groovy @@ -17,13 +17,13 @@ class ArticleTestDataAndOperations { static articles = [ [journal: "Theoretical Computer Science", volume: 455, number: 1, pages: "2-30", title: "A theory of software product line refinement", - publicationDate: (new Date("12 October 2012"))], + publicationDate: (new Date("12 October 2012")), members:[] as Set], [journal: "Science of Computer Programming", volume: 455, number: 1, pages: "2-30", title: "Algebraic reasoning for object-oriented programming", - publicationDate: (new Date("12 October 2012"))], + publicationDate: (new Date("12 October 2012")), members:[] as Set], [journal: "Eletronic Notes In Theoretical Computer Science", volume: 130, number: 1, pages: "3-21", title: "An Abstract Equivalence Notion for Object Models", - publicationDate: (new Date("12 October 2005")), + publicationDate: (new Date("12 October 2005")), members:[] as Set, authors: ["Paulo Henrique Monteiro Borba", "Tiago Massoni", "Rohit Gheyi"] as Set] ] From a7cbf4ad6a166d681d5aee94535bd90b28ffa015 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sun, 6 Jul 2014 10:46:49 -0300 Subject: [PATCH 09/54] =?UTF-8?q?Corre=C3=A7=C3=A3o=20em=20mensagem=20usad?= =?UTF-8?q?a=20no=20t=C3=ADtulo=20da=20tela.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/views/XML/home.gsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grails-app/views/XML/home.gsp b/grails-app/views/XML/home.gsp index 0ccbba4b..b417f8ed 100644 --- a/grails-app/views/XML/home.gsp +++ b/grails-app/views/XML/home.gsp @@ -4,7 +4,7 @@ - + <g:message code="default.list.label" args="[entityName]" /> From 58498e1ce5878e238372347e0b8217e5a7ff76af Mon Sep 17 00:00:00 2001 From: thaisabr Date: Fri, 11 Jul 2014 18:49:18 -0300 Subject: [PATCH 10/54] =?UTF-8?q?Mudan=C3=A7a=20para=20tornar=20opcional?= =?UTF-8?q?=20atributos=20obrigat=C3=B3rios=20que=20n=C3=A3o=20existem=20n?= =?UTF-8?q?o=20lattes=20e,=20portanto,=20n=C3=A3o=20podem=20ser=20preenchi?= =?UTF-8?q?dos=20quando=20da=20importa=C3=A7=C3=A3o=20de=20curr=C3=ADculo?= =?UTF-8?q?=20via=20XML.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/domain/rgms/publication/BookChapter.groovy | 2 +- grails-app/domain/rgms/publication/Ferramenta.groovy | 2 +- grails-app/domain/rgms/publication/ResearchLine.groovy | 2 +- grails-app/domain/rgms/publication/TeseOrDissertacao.groovy | 2 +- grails-app/domain/rgms/researchProject/Funder.groovy | 2 +- grails-app/domain/rgms/researchProject/ResearchProject.groovy | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/grails-app/domain/rgms/publication/BookChapter.groovy b/grails-app/domain/rgms/publication/BookChapter.groovy index 6fa832ff..ef9bd06d 100644 --- a/grails-app/domain/rgms/publication/BookChapter.groovy +++ b/grails-app/domain/rgms/publication/BookChapter.groovy @@ -8,7 +8,7 @@ class BookChapter extends Publication { static constraints = { publisher nullable: false, blank: false, unique: ['title', 'file', 'chapter'] - chapter nullable: false, blank: false, min: 1 + chapter nullable: false, blank: false, defaultValue:1 } //#if($Bibtex) diff --git a/grails-app/domain/rgms/publication/Ferramenta.groovy b/grails-app/domain/rgms/publication/Ferramenta.groovy index 7c844e2e..39f33018 100644 --- a/grails-app/domain/rgms/publication/Ferramenta.groovy +++ b/grails-app/domain/rgms/publication/Ferramenta.groovy @@ -7,7 +7,7 @@ class Ferramenta extends Publication { String description static constraints = { - website nullable: false, blank: false + website nullable: true, blank: true description nullable: false, blank: false } diff --git a/grails-app/domain/rgms/publication/ResearchLine.groovy b/grails-app/domain/rgms/publication/ResearchLine.groovy index d181e2de..8b9bce0b 100644 --- a/grails-app/domain/rgms/publication/ResearchLine.groovy +++ b/grails-app/domain/rgms/publication/ResearchLine.groovy @@ -12,7 +12,7 @@ class ResearchLine { static constraints = { name(blank: false, unique: true) - description(maxSize: 1000, blank: false) + description(maxSize: 1000, blank: true) } String toString() { diff --git a/grails-app/domain/rgms/publication/TeseOrDissertacao.groovy b/grails-app/domain/rgms/publication/TeseOrDissertacao.groovy index d9b50f8f..558f2a77 100644 --- a/grails-app/domain/rgms/publication/TeseOrDissertacao.groovy +++ b/grails-app/domain/rgms/publication/TeseOrDissertacao.groovy @@ -10,7 +10,7 @@ abstract class TeseOrDissertacao extends Publication { static constraints = { school nullable: false, blank: false - address nullable: false, blank: false + address nullable: true, blank: true } } diff --git a/grails-app/domain/rgms/researchProject/Funder.groovy b/grails-app/domain/rgms/researchProject/Funder.groovy index 9046786f..9e6cc91b 100644 --- a/grails-app/domain/rgms/researchProject/Funder.groovy +++ b/grails-app/domain/rgms/researchProject/Funder.groovy @@ -14,7 +14,7 @@ class Funder { } String toString(){ - return "[" + code + "] " + name + return "[" + code + "] " + name + " [" + nature + "]" } boolean equals(Funder other) { diff --git a/grails-app/domain/rgms/researchProject/ResearchProject.groovy b/grails-app/domain/rgms/researchProject/ResearchProject.groovy index 4e7d4fea..e68e8ee0 100644 --- a/grails-app/domain/rgms/researchProject/ResearchProject.groovy +++ b/grails-app/domain/rgms/researchProject/ResearchProject.groovy @@ -14,7 +14,7 @@ class ResearchProject { static constraints = { projectName(maxSize: 300, nullable: false, blank: false, unique: true) - description(maxSize: 3000, nullable: false, blank: false) + description(maxSize: 3000, nullable: true, blank: true) status(nullable: false, blank: false, inList: ["ENCERRADO","EM_ANDAMENTO","CONCLUIDO"]) startYear(nullable: false, blank: false) endYear(nullable: true, blank: true) From a50fd9d6d0de5fca787a1addebd23c912340c752 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Fri, 11 Jul 2014 18:51:01 -0300 Subject: [PATCH 11/54] =?UTF-8?q?Desenvolvimento=20da=20feature=20XMLImpor?= =?UTF-8?q?t=20e=20consequente=20atualiza=C3=A7=C3=A3o=20dos=20documentos?= =?UTF-8?q?=20usados=20nos=20testes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rgms/publication/XMLController.groovy | 63 ++- grails-app/i18n/messages.properties | 3 + grails-app/services/rgms/XMLService.groovy | 456 +++++++++++++++++- grails-app/views/XML/home.gsp | 195 ++++++++ test/files/cv-duplicatedRPC.xml | 5 + test/files/cv.xml | 10 + 6 files changed, 700 insertions(+), 32 deletions(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index 39191be9..5767d9bb 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -1,8 +1,15 @@ package rgms.publication +import grails.converters.JSON import org.apache.shiro.SecurityUtils +import org.codehaus.groovy.grails.web.json.JSONArray import rgms.authentication.User import rgms.member.Member +import rgms.member.Orientation +import rgms.researchProject.ResearchProject + +import org.codehaus.groovy.grails.web.json.JSONObject +import groovy.json.JsonSlurper /** * Created with IntelliJ IDEA. @@ -18,7 +25,7 @@ class XMLController { def home() {} def upload() { - String flashMessage = 'Publications imported!' + String flashMessage = 'default.xml.import.message' String controller = "Publication" if (!XMLService.Import(savePublication, returnWithMessage, flashMessage, controller, request)) return @@ -40,7 +47,8 @@ class XMLController { private saveTools = { Node xmlFile -> Member user = getCurrentUser() - XMLService.createTools(xmlFile, user.name) + def tools = XMLService.createTools(xmlFile, user.name)*.obj + XMLService.saveImportedTools(tools) } def uploadXMLBook() { @@ -53,26 +61,35 @@ class XMLController { private saveBook = { Node xmlFile -> Member user = getCurrentUser() - XMLService.createBooks(xmlFile, user.name) + def books = XMLService.createBooks(xmlFile, user.name)*.obj + XMLService.saveImportedBooks(books) } + //#if($researchLine) def uploadXMLResearchLine() { XMLService.Import(saveResearchLine, returnWithMessage, 'default.researchline.import.flashmessage.success', "ResearchLine", request) } private saveResearchLine = { Node xmlFile -> - XMLService.createResearchLines(xmlFile) + Member user = getCurrentUser() + def researchLines = XMLService.createResearchLines(xmlFile, user.name)*.obj + XMLService.saveImportedResearchLines(researchLines) } + //#end + //#if($researchProject && $funder) def uploadXMLResearchProject() { XMLService.Import(saveReseachProject, returnWithMessage, 'default.researchproject.import.flashmessage.success', "ResearchProject", request) } private saveReseachProject = { Node xmlFile -> - XMLService.createResearchProjects(xmlFile) + Member user = getCurrentUser() + def researchProjects = XMLService.createResearchProjects(xmlFile, user.name)*.obj + XMLService.saveImportedResearchProjects(researchProjects) } + //#end def uploadXMLBookChapter() { String flashMessage = 'The non existent Book Chapters were successfully imported' @@ -84,7 +101,8 @@ class XMLController { public saveBookChapters = { Node xmlFile -> Member user = getCurrentUser() - XMLService.createBooksChapters(xmlFile, user.name) + def bookChapters = XMLService.createBooksChapters(xmlFile, user.name)*.obj + XMLService.saveImportedBookChapters(bookChapters) } def uploadXMLDissertacao() { @@ -97,7 +115,8 @@ class XMLController { private saveDissertations = { Node xmlFile -> Member user = getCurrentUser() - XMLService.createMasterDissertation(xmlFile, user.name) + def dissertation = XMLService.createMasterDissertation(xmlFile, user.name).obj + XMLService.saveImportedDissertation(dissertation) } def enviarConferenciaXML() { @@ -110,9 +129,11 @@ class XMLController { private saveConferencias = { Node xmlFile -> Member user = getCurrentUser() - XMLService.createConferencias(xmlFile, user.name) + def conferences = XMLService.createConferencias(xmlFile, user.name)*.obj + XMLService.saveImportedConferences(conferences) } + //#if($Orientation) def uploadOrientationXML() { String flashMessage = 'default.orientation.imported.message' @@ -123,10 +144,12 @@ class XMLController { private saveOrientations = { Node xmlFile -> Member user = getCurrentUser() - - XMLService.createOrientations(xmlFile, user) + def orientations = XMLService.createOrientations(xmlFile, user)*.obj + XMLService.saveImportedOrientations(orientations) } + //#end + //#if($Article) def uploadXMLPeriodico() { String flashMessage = 'default.article.imported.message' @@ -137,8 +160,10 @@ class XMLController { private saveJournals = { Node xmlFile -> Member user = getCurrentUser() - XMLService.createJournals(xmlFile, user.name) + def journals = XMLService.createJournals(xmlFile, user.name)*.obj + XMLService.saveImportedJournals(journals) } + //#end def uploadMemberXML() { String flashMessage = 'XML data extracted. Complete the remaining fields' @@ -154,11 +179,14 @@ class XMLController { } private returnWithMessage = { String msg, String controller, publications -> - if (controller == "Publication"){ //importacao via opcao XMLImport no menu da tela inicial do sistema + //importacao via opcao XMLImport no menu da tela inicial do sistema + if (controller == "Publication"){ request.message = message(code: msg) + println "publications_import:"+publications render(view:"home", model:[publications:publications]) } - else{ //importacao via outras telas (ainda precisa corrigir) + //importacao via outras telas (ainda precisa corrigir) + else{ flash.message = message(code: msg) redirect(controller: controller, action: "list", params: params) } @@ -168,4 +196,13 @@ class XMLController { User user = User.findByUsername(SecurityUtils.getSubject()?.getPrincipal().toString()) return user?.author } + + def save() { + Member user = getCurrentUser() + println "params em save() = "+params + def msg = XMLService.saveImportedPublications(params, user) + flash.message = message(code: msg) + redirect(uri: '/') + } + } diff --git a/grails-app/i18n/messages.properties b/grails-app/i18n/messages.properties index 05c696f1..537fc575 100644 --- a/grails-app/i18n/messages.properties +++ b/grails-app/i18n/messages.properties @@ -168,6 +168,9 @@ orientation.same.members=Um membro nao pode orientar a si mesmo default.xml.parserror.message=No file uploaded or it wasn't a valid XML default.xml.structure.message=The XML struct doesn't comply with Lattes default.xml.unknownerror.message=An unknown error occurred. Contact the administrator +default.xml.saveerror.message=An error ocurred when saving the imported publications. The operation was canceled. +default.xml.save.message=The imported publications were saved! +default.xml.import.message=The publications were imported! xml.label=XMLImport file.already.exist.message=A file has already been saved with the same name diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index 8951050f..8171d853 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -2,7 +2,6 @@ package rgms import org.springframework.web.multipart.MultipartHttpServletRequest import org.springframework.web.multipart.MultipartFile -import org.xml.sax.SAXParseException import rgms.member.Member import rgms.member.Orientation import rgms.publication.* @@ -11,6 +10,8 @@ import rgms.researchProject.ResearchProject class XMLService { + def sessionFactory + public static final String PUB_STATUS_STABLE = "stable" public static final String PUB_STATUS_TO_UPDATE = "to update" public static final String PUB_STATUS_CONFLICTED= "conflicted" @@ -82,14 +83,14 @@ class XMLService { if(researchLines) publications.put("researchLines", researchLines) //#end - //#if($researchProject) + //#if($researchProject && $funder) def researchProjects = createResearchProjects(xmlFile, user.name) - publications.put("researchProjects", researchProjects) + if(researchProjects) publications.put("researchProjects", researchProjects) //#end //#if($Orientation) def orientations = createOrientations(xmlFile, user) - publications.put("orientations", orientations) + if(orientations) publications.put("orientations", orientations) //#end return publications @@ -437,7 +438,8 @@ class XMLService { def missingPropertiesDB = pubDB.properties.findAll{it.key != 'id' && !it.value} def missingProperties = pub.properties.findAll{it.key != 'id' && !it.value} - if(missingPropertiesDB != missingProperties){ + def diff = missingPropertiesDB - missingProperties + if(diff){ status = XMLService.PUB_STATUS_TO_UPDATE } @@ -503,7 +505,7 @@ class XMLService { def status = checkOrientationStatus(newOrientation, user) if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"]} + def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso", "orientador"]} orientations += ["obj": obj, "status":status] } } @@ -517,7 +519,7 @@ class XMLService { def status = checkOrientationStatus(newOrientation, user) if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"]} + def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso", "orientador"]} orientations += ["obj": obj, "status":status] } } @@ -534,7 +536,7 @@ class XMLService { def status = checkOrientationStatus(newOrientation, user) if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"]} + def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso", "orientador"]} orientations += ["obj": obj, "status":status] } } @@ -606,6 +608,7 @@ class XMLService { private static saveResearchLine(Node xmlFile) { ResearchLine newResearchLine = new ResearchLine() newResearchLine.members = [] + newResearchLine.publications = [] newResearchLine.name = getAttributeValueFromNode(xmlFile, "TITULO-DA-LINHA-DE-PESQUISA") newResearchLine.description = getAttributeValueFromNode(xmlFile, "OBJETIVOS-LINHA-DE-PESQUISA") return newResearchLine @@ -636,7 +639,7 @@ class XMLService { } //#end - //#if($researchProject) + //#if($researchProject && $funder) >>>> checar se expressão está correta static createResearchProjects(Node xmlFile, String authorName) { def author = xmlFile.depthFirst().find{it.name() == 'DADOS-GERAIS'}.'@NOME-COMPLETO' if(author != authorName) return null @@ -650,7 +653,7 @@ class XMLService { if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { def obj = newProject.properties.findAll{ - it.key in ["projectName","description", "status", "responsible", "startYear", "endYear", "members", "responsible"] + it.key in ["projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders"] } researchProjectsList += ["obj": obj, "status":status] } @@ -692,9 +695,8 @@ class XMLService { newProject.startYear = getAttributeValueFromNode(xmlFile, "ANO-INICIO").toInteger() newProject.endYear = getAttributeValueFromNode(xmlFile, "ANO-FIM").equals("") ? 0 : getAttributeValueFromNode(xmlFile, "ANO-FIM").toInteger() fillProjectMembers(getNodeFromNode(xmlFile, "EQUIPE-DO-PROJETO"), newProject) - //#if($funder) fillFunders(getNodeFromNode(xmlFile, "FINANCIADORES-DO-PROJETO"), newProject) - //#end + if(!newProject.funders) return null //no RGMS, não é possível cadastrar um projeto sem financiador return newProject } @@ -703,28 +705,26 @@ class XMLService { String name = (String) (node.attribute("NOME-COMPLETO")) Boolean responsavel = ((String) (node.attribute("FLAG-RESPONSAVEL"))).equals("SIM") if (responsavel) project.responsible = name - else project.addToMembers(name) + project.addToMembers(name) } } - //#end - //#if($funder) private static fillFunders(Node xmlFile, ResearchProject project) { for (Node node : xmlFile?.children()) { String code = getAttributeValueFromNode(node, "CODIGO-INSTITUICAO") Funder funder = Funder.findByCode(code) - if (funder) { project.addToFunders(funder) - } else { + } + def projectFunders = project.funders?.findAll{it.code = code} + if(!projectFunders) { Funder newFunder = new Funder() newFunder.code = code newFunder.name = getAttributeValueFromNode(node, "NOME-INSTITUICAO") newFunder.nature = getAttributeValueFromNode(node, "NATUREZA") project.addToFunders(newFunder) } - } - return project + } } //#end @@ -747,4 +747,422 @@ class XMLService { newMember.save(flush: false) } + private def extractDate(params,name,i,k){ + def dateString = params[name+i+".$k"] + if(!dateString || dateString=="null") return null + def date = new Date() + def year = dateString?.substring(dateString?.length()-5,dateString?.length()) as int + date.set(year: year) + return date + } + + private def extractAuthors(params,name,i,k){ + def authors = params[name+i+".$k"] + if(!authors || authors=="null") return null + def authorsList = authors?.substring(1,authors.length()-1)?.split(",") as List + def result = [] + authorsList.each{ + if(it.startsWith(" ")){ + result += it.substring(1) + } + else result += it + } + if(result.isEmpty()) return authorsList + else return result + } + + private def extractImportedJournals(name, params, keys){ + def journals = [] + def journalKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} + + if(!journalKeySet || !keys) return journals + + def n = journalKeySet.size()/keys.size() + for(i in 0..n-1){ + def journal = [:] + keys?.each{ k -> //"title", "publicationDate", "authors", "journal", "volume", "number", "pages" + if(k=="publicationDate") journal.put("$k", extractDate(params,name,i,k)) + else if(k=="authors") { + journal.put("$k", extractAuthors(params,name,i,k)) + } + else if(k=="volume" || k=="number") journal.put("$k", params[name+i+".$k"] as int) + else{ + def value = params[name+i+".$k"] + if(value == "null") value = null + journal.put("$k", value) + } + } + journals += journal + } + return journals + } + + private def extractImportedPublications(name, params, keys){ + def pubs = [] + def pubKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} + + if(!pubKeySet || !keys) return pubs + + def n = pubKeySet.size()/keys.size() + for(i in 0..n-1){ + def pub = [:] + keys.each{ k -> //"title", "publicationDate" e strings + if(k=="publicationDate") pub.put("$k", extractDate(params,name,i,k)) + else if(k=="authors") pub.put("$k", extractAuthors(params,name,i,k)) + else{ + def value = params[name+i+".$k"] + if(value == "null") value = null + pub.put("$k", value) + } + } + pubs += pub + } + return pubs + } + + private def extractBooks(name, params, keys){ + def books = [] + def bookKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} + + if(!bookKeySet || !keys) return books + + def n = bookKeySet.size()/keys.size() + for(i in 0..n-1){ + def book = [:] + keys?.each{ k -> //"title", "publicationDate", "authors", "publisher", "volume", "pages" + if(k=="publicationDate") book.put("$k", extractDate(params,name,i,k)) + else if(k=="authors") book.put("$k", extractAuthors(params,name,i,k)) + else if(k=="volume") book.put("$k", params[name+i+".$k"] as int) + else{ + def value = params[name+i+".$k"] + if(value == "null") value = null + book.put("$k", value) + } + } + books += book + } + return books + } + + private def extractDissertation(name, params, keys){ + def dissertationKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} + + def dissertation = [:] + if(dissertationKeySet.isEmpty()) return dissertation + + keys?.each{ k -> //"title", "publicationDate", "authors", "school" + if(k=="publicationDate") dissertation.put("$k", extractDate(params,name,0,k)) + else if(k=="authors") dissertation.put("$k", extractAuthors(params,name,0,k)) + else{ + def value = params[name+"0.$k"] + if(value == "null") value = null + dissertation.put("$k", value) + } + } + + return dissertation + } + + //#if($researchLine) + private def extractResearchLines(name, params, keys){ + def lines = [] + def linesKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} + + if(!linesKeySet || !keys) return lines + + def n = linesKeySet.size()/keys.size() + for(i in 0..n-1){ + def line = [:] + keys.each{ k -> //"name", "description" + def value = params[name+i+".$k"] + if(value == "null") value = null + line.put("$k", value) + } + lines += line + } + return lines + } + //#end + + //#if($researchProject && $funder) + private def extractResearchProjects(name, params, keys){ + def projects = [] + def projectKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} + + if(!projectKeySet || !keys) return projects + + def n = projectKeySet.size()/keys.size() + for(i in 0..n-1){ + def project = [:] + keys?.each{ k -> //"projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders" + if(k=="startYear" || k=="endYear") project.put("$k", params[name+i+".$k"] as int) + else if(k=="funders") project.put("$k", extractFunders(params,name,i,k)) + else if(k=="members") project.put("$k", extractAuthors(params,name,i,k)) + else{ + def value = params[name+i+".$k"] + if(value == "null") value = null + project.put("$k", value) + } + } + projects += project + } + return projects + } + + private def extractFunders(params,name,i,k){ + String fundersString = params[name+i+".$k"] + if(!fundersString || fundersString=="null") return [] + + fundersString = fundersString.substring(1,fundersString.length()-1) + def index1 = fundersString.indexOf("]") + def index2 = fundersString.lastIndexOf("[") + def code = fundersString.substring(1,index1) + def funderName = fundersString.substring(index1+2,index2-1) + def nature = fundersString.substring(index2+1,fundersString.length()-1) + return [code:code, name:funderName, nature:nature] + } + //#end + + //#if(Orientation) + private def extractOrientations(name, params, keys){ + def orientations = [] + def orientationKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} + + if(!orientationKeySet || !keys) return orientations + + def n = orientationKeySet.size()/keys.size() + for(i in 0..n-1){ + def orientation = [:] + keys?.each{ k -> //"tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso" + if(k=="anoPublicacao") orientation.put("$k", params[name+i+".$k"] as int) + else{ + def value = params[name+i+".$k"] + if(value == "null") value = null + orientation.put("$k", value) + } + } + orientations += orientation + } + return orientations + } + //#end + + def saveImportedPublications(params, user) { + def flashMessage = 'default.xml.save.message' + + try { + //#if($Article) + def journalKeys = ["title", "publicationDate", "authors", "journal", "volume", "number", "pages"] + def journals = extractImportedJournals("journals", params, journalKeys) + println "journals = " + journals + saveImportedJournals(journals) + println "journals saved!" + //#end + + def toolKeys = ["title", "publicationDate", "authors", "description"] + def tools = extractImportedPublications("tools", params, toolKeys) + println "tools = " + tools + saveImportedTools(tools) + println "tools saved!" + + def bookKeys = ["title", "publicationDate", "authors", "publisher", "volume", "pages"] + def books = extractBooks("books", params, bookKeys) + println "books = " + books + saveImportedBooks(books) + println "books saved!" + + def bookChapterKeys = ["title", "publicationDate", "authors", "publisher"] + def bookChapters = extractImportedPublications("bookChapters", params, bookChapterKeys) + println "bookChapters = " + bookChapters + saveImportedBookChapters(bookChapters) + println "bookChapters saved!" + + def dissertationKeys = ["title", "publicationDate", "authors", "school"] + def dissertation = extractDissertation("masterDissertation", params, dissertationKeys) + println "dissertation = " + dissertation + if (dissertation) saveImportedDissertation(dissertation) + println "dissertation saved!" + + def thesis = extractDissertation("thesis", params, dissertationKeys) + println "thesis = " + thesis + if (thesis) saveImportedThesis(thesis) + println "thesis saved!" + + def conferenceKeys = ["title", "publicationDate", "authors", "booktitle", "pages"] + def conferences = extractImportedPublications("conferences", params, conferenceKeys) + println "conferences = " + conferences + saveImportedConferences(conferences) + println "conferences saved!" + + //#if($researchLine) + def researchLineKeys = ["name","description"] + def researchLines = extractResearchLines("researchLines", params, researchLineKeys) + println "researchLines = " + researchLines + saveImportedResearchLines(researchLines) + println "researchLines saved!" + //#end + + //#if($researchProject && $funder) + def projectKeys = ["projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders"] + def researchProjects = extractResearchProjects("researchProjects", params, projectKeys) + println "researchProjects = " + researchProjects + saveImportedResearchProjects(researchProjects) + println "researchProjects saved!" + //#end + + //#if($Orientation) + def orientationKeys = ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"] + def orientations = extractOrientations("orientations", params, orientationKeys) + println "orientations = " + orientations + saveImportedOrientations(orientations, user) + println "orientations saved!" + //#end + + } catch(Exception ex){ + flashMessage = 'default.xml.saveerror.message' + println "exception message: " + ex.message + ex.printStackTrace() + } + + return flashMessage + } + + //#if($Article) + def saveImportedJournals(journals) { + journals.eachWithIndex(){ element, index -> + Periodico p = new Periodico(element) + p.members = [] + if(!p.save(flush:true)){ + p.errors.each{ error -> + println error + } + } + } + } + //#end + + def saveImportedTools(tools) { + tools?.eachWithIndex(){ element, index -> + Ferramenta f = new Ferramenta(element) + f.members = [] + if(!f.save(flush:true)){ + f.errors.each{ error -> + println error + } + } + } + } + + def saveImportedBooks(books) { + books?.eachWithIndex(){ element, index -> + Book b = new Book(element) + b.members = [] + if(!b.save(flush:true)){ + b.errors.each{ error -> + println error + } + } + } + } + + def saveImportedBookChapters(bookChapters) { + bookChapters?.eachWithIndex(){ element, index -> + BookChapter bc = new BookChapter(element) + bc.members = [] + if(!bc.save(flush:true)){ + bc.errors.each{ error -> + println error + } + } + } + } + + def saveImportedDissertation(masterDissertation) { + Dissertacao d = new Dissertacao(masterDissertation) + d.members = [] + if(!d.save(flush:true)){ + d.errors.each{ error -> + println error + } + } + } + + def saveImportedThesis(thesis) { + Tese t = new Tese(thesis) + t.members = [] + if(!t.save(flush:true)){ + t.errors.each{ error -> + println error + } + } + } + + def saveImportedConferences(conferences) { + conferences?.eachWithIndex(){ element, index -> + Conferencia c = new Conferencia(element) + c.members = [] + if(!c.save(flush:true)){ + c.errors.each{ error -> + println error + } + } + } + } + + //#if($researchLine) + def saveImportedResearchLines(researchLines){ + researchLines?.eachWithIndex(){ element, index -> + ResearchLine rl = new ResearchLine(element) + rl.members = [] + rl.publications = [] + if(!rl.save(flush:true)){ + rl.errors.each{ error -> + println error + } + } + } + } + //#end + + //#if($researchProject && $funder) + def saveImportedResearchProjects(researchProjects){ + researchProjects?.eachWithIndex(){ element, index -> + saveImportedFunders(element.funders) + def rp = new ResearchProject(element) + if(!rp.save(flush:true)){ + rp.errors.each{ er -> + println er + } + } + } + } + + def saveImportedFunders(project){ + project?.funders?.each(){ f -> + f = Funder.findByCode(f.code) + if(!f){ + if(!f.save(flush:true)){ + f.errors.each{ error -> + println error + } + } + } + project.addToFunders(f).save(flush:true) + } + } + //#end + + //#if($Orientation) + def saveImportedOrientations(orientations, user){ + orientations?.eachWithIndex(){ element, index -> + Orientation o = new Orientation(element) + o.orientador = user + if(!o.save(flush:true)){ + o.errors.each{ error -> + println error + } + } + } + } + //#end } diff --git a/grails-app/views/XML/home.gsp b/grails-app/views/XML/home.gsp index b417f8ed..18ea16c2 100644 --- a/grails-app/views/XML/home.gsp +++ b/grails-app/views/XML/home.gsp @@ -1,11 +1,13 @@ <%@ page contentType="text/html;charset=UTF-8" %> <%@ page import="rgms.publication.XMLController" %> +<%@ page import="rgms.publication.Periodico" %> <g:message code="default.list.label" args="[entityName]" /> + @@ -23,6 +25,199 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Publication TypeTitlePublication DateAuthorsImport Status
Journal${journalInstance?.obj?.title}${journalInstance?.obj?.authors}${journalInstance?.status}
Tool${toolInstance?.obj?.title}${toolInstance?.obj?.authors}${toolInstance?.status}
Book${bookInstance?.obj?.title}${bookInstance?.obj?.authors}${bookInstance?.status}
Book Chapter${bookChapterInstance?.obj?.title}${bookChapterInstance?.obj?.authors}${bookChapterInstance?.status}
Dissertation${publications?.masterDissertation?.obj?.title}${publications?.masterDissertation?.obj?.authors}${publications?.masterDissertation?.status}
Thesis${publications?.thesis?.obj?.title}${publications?.thesis?.obj?.authors}${publications?.thesis?.status}
Conference${conferenceInstance?.obj?.title}${conferenceInstance?.obj?.authors}${conferenceInstance?.status}
Research Line${researchLineInstance?.obj?.name} ${researchLineInstance?.obj?.members}${researchLineInstance?.status}
Research Project${projectInstance?.obj?.projectName}${projectInstance?.obj?.startYear} ${projectInstance?.obj?.funders}${projectInstance?.status}
Orientação de ${orientationInstance?.obj?.tipo}${orientationInstance?.obj?.tituloTese}${orientationInstance?.obj?.anoPublicacao}${orientationInstance?.obj?.orientando}${orientationInstance?.status}
+
+ +
+ +
+
+
diff --git a/test/files/cv-duplicatedRPC.xml b/test/files/cv-duplicatedRPC.xml index cc7019ad..af4d0999 100644 --- a/test/files/cv-duplicatedRPC.xml +++ b/test/files/cv-duplicatedRPC.xml @@ -319,6 +319,11 @@ NOME-PARA-CITACAO="Rubens Lopes da Silva" ORDEM-DE-INTEGRACAO="2" FLAG-RESPONSAVEL="NAO"/> + + + + + + + + + From f56695926755911a27725f9fb5acaaf21303939c Mon Sep 17 00:00:00 2001 From: thaisabr Date: Fri, 11 Jul 2014 19:01:04 -0300 Subject: [PATCH 12/54] =?UTF-8?q?Exclus=C3=A3o=20de=20imports=20adicionado?= =?UTF-8?q?s=20indevidamente.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/controllers/rgms/publication/XMLController.groovy | 5 ----- 1 file changed, 5 deletions(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index 5767d9bb..d96ec476 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -1,16 +1,11 @@ package rgms.publication -import grails.converters.JSON import org.apache.shiro.SecurityUtils -import org.codehaus.groovy.grails.web.json.JSONArray import rgms.authentication.User import rgms.member.Member import rgms.member.Orientation import rgms.researchProject.ResearchProject -import org.codehaus.groovy.grails.web.json.JSONObject -import groovy.json.JsonSlurper - /** * Created with IntelliJ IDEA. * User: Cynthia From 07d81d7520743b3a2f2295273098e575243ca6df Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sat, 12 Jul 2014 18:30:35 -0300 Subject: [PATCH 13/54] =?UTF-8?q?Corre=C3=A7=C3=A3o=20das=20diretivas=20de?= =?UTF-8?q?=20pr=C3=A9-processamento=20e=20da=20l=C3=B3gica=20de=20defini?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20status=20de=20publica=C3=A7=C3=B5es=20impo?= =?UTF-8?q?rtadas.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rgms/publication/XMLController.groovy | 4 +- grails-app/services/rgms/XMLService.groovy | 170 +++++++----------- grails-app/views/XML/home.gsp | 18 +- 3 files changed, 75 insertions(+), 117 deletions(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index d96ec476..4036481a 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -73,7 +73,7 @@ class XMLController { } //#end - //#if($researchProject && $funder) + //#if($researchProject) def uploadXMLResearchProject() { XMLService.Import(saveReseachProject, returnWithMessage, 'default.researchproject.import.flashmessage.success', "ResearchProject", request) } @@ -177,7 +177,6 @@ class XMLController { //importacao via opcao XMLImport no menu da tela inicial do sistema if (controller == "Publication"){ request.message = message(code: msg) - println "publications_import:"+publications render(view:"home", model:[publications:publications]) } //importacao via outras telas (ainda precisa corrigir) @@ -194,7 +193,6 @@ class XMLController { def save() { Member user = getCurrentUser() - println "params em save() = "+params def msg = XMLService.saveImportedPublications(params, user) flash.message = message(code: msg) redirect(uri: '/') diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index 8171d853..d586b8b0 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -83,7 +83,7 @@ class XMLService { if(researchLines) publications.put("researchLines", researchLines) //#end - //#if($researchProject && $funder) + //#if($researchProject) def researchProjects = createResearchProjects(xmlFile, user.name) if(researchProjects) publications.put("researchProjects", researchProjects) //#end @@ -433,18 +433,28 @@ class XMLService { } //#end + private static def calculateTotalMissingProperties(propertiesDB, properties){ + def missingPropertiesTotal = propertiesDB + properties.each{ + if(!missingPropertiesTotal.contains(it)) missingPropertiesTotal += it + } + return missingPropertiesTotal + } + private static checkPublicationStatus(Publication pubDB, Publication pub){ def status = XMLService.PUB_STATUS_DUPLICATED - def missingPropertiesDB = pubDB.properties.findAll{it.key != 'id' && !it.value} - def missingProperties = pub.properties.findAll{it.key != 'id' && !it.value} - def diff = missingPropertiesDB - missingProperties - if(diff){ + def missingPropertiesDB = pubDB.properties.findAll{it.key != 'id' && !it.value}.keySet() + def missingProperties = pub.properties.findAll{it.key != 'id' && !it.value}.keySet() + if(missingPropertiesDB != missingProperties){ status = XMLService.PUB_STATUS_TO_UPDATE } - def detailsDB = pubDB.properties.findAll{it.key!='id' && it.key!='publicationDate'} - missingPropertiesDB - def details = pub.properties.findAll{it.key!='id' && it.key!='publicationDate'} - missingProperties + def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) + def detailsPropertiesDB = pubDB.properties.findAll{!(it.key in missingPropertiesTotal)} + def detailsDB = detailsPropertiesDB.findAll{it.key!='id' && it.key!='publicationDate'} + def detailsProperties = pub.properties.findAll{!(it.key in missingPropertiesTotal)} + def details = detailsProperties.findAll{it.key!='id' && it.key!='publicationDate'} if(detailsDB != details){ status = XMLService.PUB_STATUS_CONFLICTED } @@ -547,19 +557,21 @@ class XMLService { if(!orientation) return null def status = XMLService.PUB_STATUS_STABLE def orientationDB = Orientation.findByOrientadorAndTituloTese(user, orientation.tituloTese) + if(orientationDB){ status = XMLService.PUB_STATUS_DUPLICATED - def missingPropertiesDB = orientationDB.properties.findAll{it.key != 'id' && !it.value} - def missingProperties = orientation.properties.findAll{it.key != 'id' && !it.value} - + def missingPropertiesDB = orientationDB.properties.findAll{it.key != 'id' && !it.value}.keySet() + def missingProperties = orientation.properties.findAll{it.key != 'id' && !it.value}.keySet() if(missingPropertiesDB != missingProperties){ status = XMLService.PUB_STATUS_TO_UPDATE } - def detailsDB = orientationDB.properties.findAll{it.key!='id'} - missingPropertiesDB - def details = orientation.properties.findAll{it.key!='id'} - missingProperties - + def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) + def detailsPropertiesDB = orientationDB.properties.findAll{!(it.key in missingPropertiesTotal)} + def detailsDB = detailsPropertiesDB.findAll{it.key!='id'} + def detailsProperties = orientation.properties.findAll{!(it.key in missingPropertiesTotal)} + def details = detailsProperties.findAll{it.key!='id'} if(detailsDB != details){ status = XMLService.PUB_STATUS_CONFLICTED } @@ -618,19 +630,21 @@ class XMLService { if(!researchLine) return null def status = XMLService.PUB_STATUS_STABLE def rlDB = ResearchLine.findByName(researchLine.name) + if(rlDB){ status = XMLService.PUB_STATUS_DUPLICATED - def missingPropertiesDB = rlDB.properties.findAll{it.key != 'id' && !it.value} - def missingProperties = researchLine.properties.findAll{it.key != 'id' && !it.value} - + def missingPropertiesDB = rlDB.properties.findAll{it.key != 'id' && !it.value}.keySet() + def missingProperties = researchLine.properties.findAll{it.key != 'id' && !it.value}.keySet() if(missingPropertiesDB != missingProperties){ status = XMLService.PUB_STATUS_TO_UPDATE } - def detailsDB = rlDB.properties.findAll{it.key!='id'} - missingPropertiesDB - def details = researchLine.properties.findAll{it.key!='id'} - missingProperties - + def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) + def detailsPropertiesDB = rlDB.properties.findAll{!(it.key in missingPropertiesTotal)} + def detailsDB = detailsPropertiesDB.findAll{it.key!='id'} + def detailsProperties = researchLine.properties.findAll{!(it.key in missingPropertiesTotal)} + def details = detailsProperties.findAll{it.key!='id'} if(detailsDB != details){ status = XMLService.PUB_STATUS_CONFLICTED } @@ -639,7 +653,7 @@ class XMLService { } //#end - //#if($researchProject && $funder) >>>> checar se expressão está correta + //#if($researchProject) static createResearchProjects(Node xmlFile, String authorName) { def author = xmlFile.depthFirst().find{it.name() == 'DADOS-GERAIS'}.'@NOME-COMPLETO' if(author != authorName) return null @@ -668,17 +682,17 @@ class XMLService { if(researchProjectDB){ status = XMLService.PUB_STATUS_DUPLICATED - - def missingPropertiesDB = researchProjectDB.properties.findAll{it.key != 'id' && !it.value} - def missingProperties = researchProject.properties.findAll{it.key != 'id' && !it.value} - + def missingPropertiesDB = researchProjectDB.properties.findAll{it.key != 'id' && !it.value}.keySet() + def missingProperties = researchProject.properties.findAll{it.key != 'id' && !it.value}.keySet() if(missingPropertiesDB != missingProperties){ status = XMLService.PUB_STATUS_TO_UPDATE } - def detailsDB = researchProjectDB.properties.findAll{it.key!='id'} - missingPropertiesDB - def details = researchProject.properties.findAll{it.key!='id '} - missingProperties - + def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) + def detailsPropertiesDB = researchProjectDB.properties.findAll{!(it.key in missingPropertiesTotal)} + def detailsDB = detailsPropertiesDB.findAll{it.key!='id'} + def detailsProperties = researchProject.properties.findAll{!(it.key in missingPropertiesTotal)} + def details = detailsProperties.findAll{it.key!='id'} if(detailsDB != details){ status = XMLService.PUB_STATUS_CONFLICTED } @@ -695,8 +709,9 @@ class XMLService { newProject.startYear = getAttributeValueFromNode(xmlFile, "ANO-INICIO").toInteger() newProject.endYear = getAttributeValueFromNode(xmlFile, "ANO-FIM").equals("") ? 0 : getAttributeValueFromNode(xmlFile, "ANO-FIM").toInteger() fillProjectMembers(getNodeFromNode(xmlFile, "EQUIPE-DO-PROJETO"), newProject) + //#if($funder) fillFunders(getNodeFromNode(xmlFile, "FINANCIADORES-DO-PROJETO"), newProject) - if(!newProject.funders) return null //no RGMS, não é possível cadastrar um projeto sem financiador + //#end return newProject } @@ -708,7 +723,9 @@ class XMLService { project.addToMembers(name) } } + //#end + //#if($funder) private static fillFunders(Node xmlFile, ResearchProject project) { for (Node node : xmlFile?.children()) { String code = getAttributeValueFromNode(node, "CODIGO-INSTITUICAO") @@ -954,73 +971,52 @@ class XMLService { //#if($Article) def journalKeys = ["title", "publicationDate", "authors", "journal", "volume", "number", "pages"] def journals = extractImportedJournals("journals", params, journalKeys) - println "journals = " + journals saveImportedJournals(journals) - println "journals saved!" //#end def toolKeys = ["title", "publicationDate", "authors", "description"] def tools = extractImportedPublications("tools", params, toolKeys) - println "tools = " + tools saveImportedTools(tools) - println "tools saved!" def bookKeys = ["title", "publicationDate", "authors", "publisher", "volume", "pages"] def books = extractBooks("books", params, bookKeys) - println "books = " + books saveImportedBooks(books) - println "books saved!" def bookChapterKeys = ["title", "publicationDate", "authors", "publisher"] def bookChapters = extractImportedPublications("bookChapters", params, bookChapterKeys) - println "bookChapters = " + bookChapters saveImportedBookChapters(bookChapters) - println "bookChapters saved!" def dissertationKeys = ["title", "publicationDate", "authors", "school"] def dissertation = extractDissertation("masterDissertation", params, dissertationKeys) - println "dissertation = " + dissertation if (dissertation) saveImportedDissertation(dissertation) - println "dissertation saved!" def thesis = extractDissertation("thesis", params, dissertationKeys) - println "thesis = " + thesis if (thesis) saveImportedThesis(thesis) - println "thesis saved!" def conferenceKeys = ["title", "publicationDate", "authors", "booktitle", "pages"] def conferences = extractImportedPublications("conferences", params, conferenceKeys) - println "conferences = " + conferences saveImportedConferences(conferences) - println "conferences saved!" //#if($researchLine) def researchLineKeys = ["name","description"] def researchLines = extractResearchLines("researchLines", params, researchLineKeys) - println "researchLines = " + researchLines saveImportedResearchLines(researchLines) - println "researchLines saved!" //#end - //#if($researchProject && $funder) + //#if($researchProject) def projectKeys = ["projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders"] def researchProjects = extractResearchProjects("researchProjects", params, projectKeys) - println "researchProjects = " + researchProjects saveImportedResearchProjects(researchProjects) - println "researchProjects saved!" //#end //#if($Orientation) def orientationKeys = ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"] def orientations = extractOrientations("orientations", params, orientationKeys) - println "orientations = " + orientations saveImportedOrientations(orientations, user) - println "orientations saved!" //#end } catch(Exception ex){ flashMessage = 'default.xml.saveerror.message' - println "exception message: " + ex.message ex.printStackTrace() } @@ -1032,11 +1028,7 @@ class XMLService { journals.eachWithIndex(){ element, index -> Periodico p = new Periodico(element) p.members = [] - if(!p.save(flush:true)){ - p.errors.each{ error -> - println error - } - } + p.save(flush:true) } } //#end @@ -1045,11 +1037,7 @@ class XMLService { tools?.eachWithIndex(){ element, index -> Ferramenta f = new Ferramenta(element) f.members = [] - if(!f.save(flush:true)){ - f.errors.each{ error -> - println error - } - } + f.save(flush:true) } } @@ -1057,11 +1045,7 @@ class XMLService { books?.eachWithIndex(){ element, index -> Book b = new Book(element) b.members = [] - if(!b.save(flush:true)){ - b.errors.each{ error -> - println error - } - } + b.save(flush:true) } } @@ -1069,43 +1053,27 @@ class XMLService { bookChapters?.eachWithIndex(){ element, index -> BookChapter bc = new BookChapter(element) bc.members = [] - if(!bc.save(flush:true)){ - bc.errors.each{ error -> - println error - } - } + bc.save(flush:true) } } def saveImportedDissertation(masterDissertation) { Dissertacao d = new Dissertacao(masterDissertation) d.members = [] - if(!d.save(flush:true)){ - d.errors.each{ error -> - println error - } - } + d.save(flush:true) } def saveImportedThesis(thesis) { Tese t = new Tese(thesis) t.members = [] - if(!t.save(flush:true)){ - t.errors.each{ error -> - println error - } - } + t.save(flush:true) } def saveImportedConferences(conferences) { conferences?.eachWithIndex(){ element, index -> Conferencia c = new Conferencia(element) c.members = [] - if(!c.save(flush:true)){ - c.errors.each{ error -> - println error - } - } + c.save(flush:true) } } @@ -1115,38 +1083,28 @@ class XMLService { ResearchLine rl = new ResearchLine(element) rl.members = [] rl.publications = [] - if(!rl.save(flush:true)){ - rl.errors.each{ error -> - println error - } - } + rl.save(flush:true) } } //#end - //#if($researchProject && $funder) + //#if($researchProject) def saveImportedResearchProjects(researchProjects){ researchProjects?.eachWithIndex(){ element, index -> + //#if($funder) saveImportedFunders(element.funders) + //#end def rp = new ResearchProject(element) - if(!rp.save(flush:true)){ - rp.errors.each{ er -> - println er - } - } + rp.save(flush:true) } } + //#end + //#if($funder) def saveImportedFunders(project){ project?.funders?.each(){ f -> f = Funder.findByCode(f.code) - if(!f){ - if(!f.save(flush:true)){ - f.errors.each{ error -> - println error - } - } - } + if(!f) f.save(flush:true) project.addToFunders(f).save(flush:true) } } @@ -1157,11 +1115,7 @@ class XMLService { orientations?.eachWithIndex(){ element, index -> Orientation o = new Orientation(element) o.orientador = user - if(!o.save(flush:true)){ - o.errors.each{ error -> - println error - } - } + o.save(flush:true) } } //#end diff --git a/grails-app/views/XML/home.gsp b/grails-app/views/XML/home.gsp index 18ea16c2..e4bc0293 100644 --- a/grails-app/views/XML/home.gsp +++ b/grails-app/views/XML/home.gsp @@ -41,7 +41,7 @@ - + @@ -76,6 +76,7 @@ + @@ -157,7 +158,7 @@ - + @@ -171,7 +172,8 @@ - + + @@ -181,17 +183,20 @@ - + + + Research Project ${projectInstance?.obj?.projectName} ${projectInstance?.obj?.startYear} - ${projectInstance?.obj?.funders} + ${projectInstance?.obj?.funders} ${projectInstance?.status} - + + @@ -209,6 +214,7 @@ + From c81ba741e1d75c5932e333ec7ef5de4bc4ced300 Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Mon, 14 Jul 2014 22:10:43 -0300 Subject: [PATCH 14/54] Atividade 5 --- .../publication/ResearchLineController.groovy | 96 +++++++++++++++++++ grails-app/services/rgms/XMLService.groovy | 87 +++++++++++------ 2 files changed, 154 insertions(+), 29 deletions(-) diff --git a/grails-app/controllers/rgms/publication/ResearchLineController.groovy b/grails-app/controllers/rgms/publication/ResearchLineController.groovy index 77fa344f..610cf75d 100644 --- a/grails-app/controllers/rgms/publication/ResearchLineController.groovy +++ b/grails-app/controllers/rgms/publication/ResearchLineController.groovy @@ -238,5 +238,101 @@ class ResearchLineController { researchLineInstance } + + def findAllResearchLine(){ + HashMap lista = new HashMap() + for(researchline in ResearchLine.getAll()) + { + if(!researchline.getDescription().equals("stable")){ + researchline.setDescription("stable") + lista.put(researchline.getName(),researchline.getDescription()) + }else{ + lista.put(researchline.getName(),researchline.getDescription()) + } + + } + [researchLineInstanceList: lista] + } + + def findByActor(def member){ + ArrayList lista = new ArrayList() + Member actor = new Member() + for(research in ResearchLine.getAll()) + { + for(members in research.getMembers()){ + if(member.equals(members.getName())){ + actor.getId() + } + } + } + return actor + } + + def findResearchByActor(def member,def research){ + HashMap listagem = new HashMap() + for(researchL in ResearchLine.getAll()){ + if((researchL.equals(research)) && (researchL.getMembers().contains(member))){ + listagem.put(member, research) + } + } + return listagem.size() + } + + def checkIfResearchLineExists(researchName, list){ + List listCheck = list + for(research in list){ + if((!listCheck.isEmpty()) && (research.equals(researchName))){ + return true + } + } + return false + } + + def checkIfResearchLineNoExists(def researchName){ + ResearchLine line = ResearchLine.findByName(researchName) + line == null + } + + def statusChanged(def lista){ + def sizeList = findAllResearchLine() + def inicialSize = sizeList.size() + def finalSize = lista.size() + if((lista.empty) || (inicialSize == finalSize) ){ + return false + }else if (inicialSize < finalSize){ + return true + } + return false + } + + def checkSavedResearchByDescription(nameOfResearch, status){ + HashMap lista = findAllResearchLine() + for(int i; i< lista.size(); i++){ + + if(lista.containsKey(nameOfResearch)) { + if(lista.containsValue(status)){ + + return true + } + } + } + return false + + } + + def checkDeletedResearchByDescription(nameOfResearch, status){ + List lista = ResearchLine.findAll() + boolean exist = checkSavedResearchByDescription(nameOfResearch, status) + for(research in lista){ + if((exist) && (research.getName().equals(nameOfResearch))){ + if(research.getDescription().equals(status)){ + lista.remove(research) + return true + } + + } + } + return false + } } //#end \ No newline at end of file diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index d586b8b0..500d2a65 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -2,6 +2,7 @@ package rgms import org.springframework.web.multipart.MultipartHttpServletRequest import org.springframework.web.multipart.MultipartFile +import org.xml.sax.SAXParseException import rgms.member.Member import rgms.member.Orientation import rgms.publication.* @@ -162,8 +163,8 @@ class XMLService { List book = currentNode.children() Node basicData = (Node) book[0] Node bookDetails = (Node) book[1] - Book newBook = new Book() + newBook.members = [] newBook = (Book) addAuthors(currentNode, newBook) if(!newBook.authors.contains(authorName)) return null //the user is not author @@ -546,7 +547,7 @@ class XMLService { def status = checkOrientationStatus(newOrientation, user) if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso", "orientador"]} + def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"]} orientations += ["obj": obj, "status":status] } } @@ -619,8 +620,6 @@ class XMLService { private static saveResearchLine(Node xmlFile) { ResearchLine newResearchLine = new ResearchLine() - newResearchLine.members = [] - newResearchLine.publications = [] newResearchLine.name = getAttributeValueFromNode(xmlFile, "TITULO-DA-LINHA-DE-PESQUISA") newResearchLine.description = getAttributeValueFromNode(xmlFile, "OBJETIVOS-LINHA-DE-PESQUISA") return newResearchLine @@ -726,10 +725,11 @@ class XMLService { //#end //#if($funder) - private static fillFunders(Node xmlFile, ResearchProject project) { + private static void fillFunders(Node xmlFile, ResearchProject project) { for (Node node : xmlFile?.children()) { String code = getAttributeValueFromNode(node, "CODIGO-INSTITUICAO") Funder funder = Funder.findByCode(code) + if (funder) { project.addToFunders(funder) } @@ -764,29 +764,7 @@ class XMLService { newMember.save(flush: false) } - private def extractDate(params,name,i,k){ - def dateString = params[name+i+".$k"] - if(!dateString || dateString=="null") return null - def date = new Date() - def year = dateString?.substring(dateString?.length()-5,dateString?.length()) as int - date.set(year: year) - return date - } - private def extractAuthors(params,name,i,k){ - def authors = params[name+i+".$k"] - if(!authors || authors=="null") return null - def authorsList = authors?.substring(1,authors.length()-1)?.split(",") as List - def result = [] - authorsList.each{ - if(it.startsWith(" ")){ - result += it.substring(1) - } - else result += it - } - if(result.isEmpty()) return authorsList - else return result - } private def extractImportedJournals(name, params, keys){ def journals = [] @@ -799,7 +777,7 @@ class XMLService { def journal = [:] keys?.each{ k -> //"title", "publicationDate", "authors", "journal", "volume", "number", "pages" if(k=="publicationDate") journal.put("$k", extractDate(params,name,i,k)) - else if(k=="authors") { + else if(k=="au thors") { journal.put("$k", extractAuthors(params,name,i,k)) } else if(k=="volume" || k=="number") journal.put("$k", params[name+i+".$k"] as int) @@ -837,7 +815,31 @@ class XMLService { return pubs } - private def extractBooks(name, params, keys){ + //if(ResearchLine) + private static checkContResearch(List researchLine, int i, String researchName) { + List researchs = ((Node) researchLine[i]).children() + Node basicData = (Node) researchs[0] + List lista = new ArrayList() + + ResearchLine newResearch = new ResearchLine() + + while (!basicData.children().empty){ + newResearch = getResearchLine(basicData, researchName) + lista.add(newResearch) + researchs.add(this) + } + return lista + } + + private static String getResearchLine(Node basicData, ResearchLine researchLine) { + researchLine.name = getAttributeValueFromNode(basicData, "TITULO-DA-LINHA-DE-PESQUISA") + return researchLine.name + } + //end + + + + private def extractBooks(name, params, keys){ def books = [] def bookKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} @@ -880,6 +882,33 @@ class XMLService { return dissertation } + + private def extractDate(params,name,i,k){ + def dateString = params[name+i+".$k"] + if(!dateString || dateString=="null") return null + def date = new Date() + def year = dateString?.substring(dateString?.length()-5,dateString?.length()) as int + date.set(year: year) + return date + } + + private def extractAuthors(params,name,i,k){ + def authors = params[name+i+".$k"] + if(!authors || authors=="null") return null + def authorsList = authors?.substring(1,authors.length()-1)?.split(",") as List + def result = [] + authorsList.each{ + if(it.startsWith(" ")){ + result += it.substring(1) + } + else result += it + } + if(result.isEmpty()) return authorsList + else return result + } + + + //#if($researchLine) private def extractResearchLines(name, params, keys){ def lines = [] From ced452cd4e9c0e390dbae7d8f6911265cafec0b7 Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Mon, 14 Jul 2014 22:17:53 -0300 Subject: [PATCH 15/54] Atividade 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Atraso devido problemas na integração, o processo está correção --- .../publication/PublicationController.groovy | 33 +++- test/cucumber/XMLImport.feature | 5 +- test/cucumber/steps/XMLImportSteps.groovy | 151 +++++++++++------- test/files/cv.pdf | Bin 0 -> 185176 bytes test/files/cv.xml | 5 + .../TestDataAndOperationsPublication.groovy | 11 ++ .../TestDataAndOperationsResearchLine.groovy | 8 + .../XMLImportTestDataAndOperations.groovy | 31 ++++ 8 files changed, 173 insertions(+), 71 deletions(-) create mode 100644 test/files/cv.pdf diff --git a/grails-app/controllers/rgms/publication/PublicationController.groovy b/grails-app/controllers/rgms/publication/PublicationController.groovy index 3f0198ee..308a8f9a 100644 --- a/grails-app/controllers/rgms/publication/PublicationController.groovy +++ b/grails-app/controllers/rgms/publication/PublicationController.groovy @@ -137,13 +137,13 @@ class PublicationController { * Para enviar o post, é preciso esta autenticado com o facebook. * - Para um usuario novo, no momento do registro basta logar com o FB * - Para um usuario ja existente, edit esse usuario e adicione o a conta do FB ao usuario. - */ - //#if($facebook) - def static sendPostFacebook(Member user, String title){ - def url = "https://graph.facebook.com/me/feed?access_token=" + user?.access_token + */ + //#if($facebook) + def static sendPostFacebook(Member user, String title){ + def url = "https://graph.facebook.com/me/feed?access_token=" + user?.access_token System.out.println(title); List params = new ArrayList(); - + HttpPost post = new HttpPost(url); params.add(new BasicNameValuePair("access_token", user?.access_token)); @@ -153,12 +153,31 @@ class PublicationController { UrlEncodedFormEntity postEntity = new UrlEncodedFormEntity(params, HTTP.UTF_8); post.setEntity(postEntity); - + HttpClient client = new DefaultHttpClient(); HttpResponse response = client.execute(post); StatusLine statusLine = response.getStatusLine(); - + // return statusLine.getStatusCode(); } //#end + + def static checkTypeFile(file){ + if(!file.hasProperty(".xml")){ + return false + } + return true + } + + def statusChanged(def lista){ + def sizeList = Publication.findAll() + def inicialSize = sizeList.size() + def finalSize = lista.size() + if((lista.empty) || (inicialSize == finalSize) ){ + return false + }else if (inicialSize < finalSize){ + return true + } + return false + } } diff --git a/test/cucumber/XMLImport.feature b/test/cucumber/XMLImport.feature index e94fecb2..3c80885f 100644 --- a/test/cucumber/XMLImport.feature +++ b/test/cucumber/XMLImport.feature @@ -5,7 +5,6 @@ Feature: XMLImport I want to import a xml file So that the system register the corresponding publications in my profile - @ignore Scenario: invalid file web Given I am at the "Import XML File" Page When I select the "upload" button @@ -14,7 +13,6 @@ Feature: XMLImport And no new publication is stored by the system And the previously stored publications do not change - @ignore Scenario: invalid file Given the system has some publications stored When I upload the file "cv.pdf" @@ -126,7 +124,6 @@ Feature: XMLImport And the previously stored publications do not change #if ($ResearchLine) - @ignore Scenario: new research line Given the system has some research lines stored And the system has no research line named as "Modularidade Emergente" associated with me @@ -135,7 +132,7 @@ Feature: XMLImport And no new research line is stored by the system And the previously stored research lines do not change - @ignore + Scenario: confirm import of new research line Given the system has some research lines stored And the system has no research line named as "Modularidade Emergente" associated with me diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index 8fe6408e..4a9f0348 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -10,7 +10,9 @@ import rgms.publication.* import rgms.researchProject.ResearchProject import steps.ArticleTestDataAndOperations import steps.ConferenciaTestDataAndOperations +import steps.ResearchLineTestDataAndOperations import steps.ResearchProjectTestDadaAndOperations +import steps.TestDataAndOperationsPublication import steps.TestDataAndOperationsResearchLine import steps.XMLImportTestDataAndOperations @@ -197,9 +199,9 @@ Then(~'^the previously stored research projects do not change$'){ -> Then(~'^the system outputs a list of imported research projects that contains the one named as "([^"]*)" with status "([^"]*)"$'){ projectName, status -> - assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status - assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render - assert xmlController.modelAndView.model.publications.researchProjects.find{it["obj"].projectName == projectName && it["status"] == status} + assert xmlController.response.status == HttpServletResponse.SC_OK //A grails render is an HTTP 200 status + assert xmlController.modelAndView.viewName == '/XML/home' //modelAndView is used only when is render + assert xmlController.modelAndView.model.publications.researchProjects.find{it["obj"].projectName == projectName && it["status"] == status} } Given(~'^the system has a research project named as "([^"]*)", among several research projects$'){ projectName -> @@ -252,104 +254,133 @@ When(~'^I upload the file "([^"]*)" that also contains a research project named } //#end -/** - * @author Kamilla Cardoso - * #if($ResearchLine) - * Scenario: new research line - */ -Given(~'^ The system has some research lines stored$'){ -> + +// #if($ResearchLine) +Given(~'^ the system has some research lines stored$'){ TestDataAndOperations.loginController(this) + ResearchLineTestDataAndOperations.createResearchLine(0) + ResearchLineTestDataAndOperations.createResearchLine(1) + ResearchLineTestDataAndOperations.createResearchLine(2) initialSize = ResearchLine.findAll().size() } -Given(~'^ The system has no research line named as "([^"]*)" associated with me $'){ String nameResearch -> - //Se existe uma linha de pesquisa de nome (nameResearch), mas ela não é existe na lista de pesquisas do usuário atual, - // o usuário logado não à possui - assert ResearchLine.findByName(nameResearch) != PublicationController.getLoggedMember().researchLines.contains(nameResearch) +Given(~'^ the system has no research line named as "([^"]*)" associated with me $') { String nameOfResearch -> + xmlImport = new ResearchLineController() + def autor = xmlImport.findByActor(this) + List listaDeVerificacao = xmlImport.findResearchByActor(autor, nameOfResearch) + assert listaDeVerificacao.size() == 0 } -When(~'^ I upload the file "([^"]*)" which contains a research line named as "([^"]*)" $'){ file, researchLineName -> +When(~'^I upload the file "([^"]*)" which contains a research line named as "([^"]*)" $'){ String research, file -> TestDataAndOperations.uploadPublications(file) - TestDataAndOperationsResearchLine.findResearchLineByName(researchLineName) + String path = "test" + File.separator + "files" + File.separator + file + xmlImport2 = XMLImportTestDataAndOperations.checkResearchLineFromFile(file, research) + assert xmlImport2 != 0 } -Then(~'^ The system outputs a list of imported research lines which contains the one named as "([^"]*)" with status "([^"]*)" $'){ research_Line, status -> - //lista a quantidades de linha de pesquisas armazenadas para o nome especifico - assert Publication.findAllByResearchLineInListAndTitle(ResearchLine.findAll(), research_Line).size() > 1 - //deve conter apenas uma linha de pesquisa, entao a linha de pesquisa armazenada recentemente é removida - TestDataAndOperationsResearchLine.deleteResearchLine(ResearchLine.findByName(research_Line).getMembers()) - //status definido na descrição deve ser "stable" para a linha de pesquisa que se manteve armazenada - assert ResearchLine.findByName(research_Line).getDescription() == status +Then(~'^the system outputs a list of imported research lines which contains the one named as "([^"]*)" with status "([^"]*)" $'){ String researchOfLine, status -> + + xmlImport2 = new ResearchLineController() + //O metodo retorna todos as research que estao com status stable + lista = xmlImport2.findAllResearchLine() + assert xmlImport2.checkIfResearchLineExists(researchOfLine,lista) } -Then(~'^ No new research line is stored by the system$'){ -> +Then(~'^ no new research line is stored by the system $'){ finalSize = ResearchLine.findAll().size() + assert initialSize == finalSize + } -Then(~'^ The previously stored research lines do not change$'){ - assert initialSize == finalSize +Then(~'^ the previously stored research lines do not change $'){ + ResearchLineController controller = new XMLController() + def lista = ResearchLine.findAll() + status = controller.statusChanged(lista) //se true é porque modificou, se false é porque nada foi modificado + assert status == false } //#end -/** - * @author Kamilla Cardoso - * #if($ResearchLine) - * Scenario: import xml file that contains a research line - */ -Given(~'^ The system has no research line named as "([^"]*)" $'){ nameResearch -> - assert ResearchLine.findByName(nameResearch) == null - inicialSize = ResearchLineController.findAll().size() + +Given(~'^ the system has some research lines stored $'){ + ResearchLineTestDataAndOperations.createResearchLine(0) + ResearchLineTestDataAndOperations.createResearchLine(1) + initialSize = ResearchLine.findAll().size() +} +Given(~'^ the system has no research line named as "([^"]*)" associated with me $'){ nameOfResearch -> + xmlImport = new ResearchLineController() + def autor = xmlImport.findByActor(this) + List listaDeVerificacao = xmlImport.findResearchByActor(autor, nameOfResearch) + assert listaDeVerificacao.size() == 0 } -When(~'^ I upload the file "([^"]*)" which contains a research line named as "([^"]*)" $') { file, research_name -> - TestDataAndOperations.uploadPublications(file) - assert ResearchLine.findByName(research_name) == research_name - finalSize = ResearchLineController.findAll().size() - assert inicialSize < finalSize +Given(~'^the file "([^"]*)", which contains a research line named as "([^"]*)", is uploaded $'){ String theFile, researchLineName -> + TestDataAndOperations.uploadPublications(theFile) + status = XMLImportTestDataAndOperations.extractSpecificResearchLineFromFile(theFile, researchLineName) + assert status == true +} + +When(~'^ I confirm the import of the research line named as "([^"]*)" with status "([^"]*)" $'){ String nameOfResearch, status -> + ResearchLineController controller = new ResearchLineController() + statusController = controller.checkSavedResearchByDescription(nameOfResearch, status) + assert statusController == true + +} +Then(~'^ the research line named as "([^"]*)" is stored by the system $'){ String research -> + list = ResearchLine.findAll() + ResearchLineController controller = new ResearchLineController() + statusSave = controller.checkIfResearchLineExists(research,list) + assert statusSave == true } -Then(~'^ the research line named as "([^"]*)" is stored &'){ research-> - assert ResearchLine.findByName(research) != null +Then(~'^ the research line named as "([^"]*)" with status "([^"]*)" is removed from the list of imported research lines $'){ String nameOfResearch, status -> + ResearchLineController cont = new ResearchLineController() + check = cont.checkDeletedResearchByDescription(nameOfResearch, status) + assert check == true + +} +Then(~'^ the previously stored research lines do not change $'){ + + ResearchLineController controller = new XMLController() + def lista = ResearchLine.findAll() + status = controller.statusChanged(lista) //se true é porque modificou, se false é porque nada foi modificado + assert status == false } //#end -/** - * @author Kamilla Cardoso - * Scenario: import invalid file - */ + Given(~'^The system has some publications stored $'){ -> + Publication.findOrSaveById(0) + Publication.findOrSaveById(1) + Publication.findOrSaveById(2) inicial = Publication.findAll().size() } When(~'^ I upload the file "([^"]*)" $') { String typeFile -> - TestDataAndOperations.uploadPublications(typeFile) - currentTypeFile = Publication.findByFile(typeFile).getFile().hasProperty(".xml") - assert currentTypeFile == false - - //Verifica se o arquivo possuia tipo invalido, caso seja e removido do sistema - //Como este arquivo pode ter persistido informacoes entao deve-se deletar todas as informacoes juntamente com o arquivo - - Publication.findAllByFile(typeFile).remove(this) + TestDataAndOperationsPublication.uploadPublication(typeFile) + PublicationController controllerP = new PublicationController() + status = controllerP.checkTypeFile(typeFile) + assert status == false } Then(~'^ no publication is stored by the system $') { -> finalS = Publication.findAll().size() + assert inicial == finalS } Then(~'^ And previusly stored publications do not change $'){-> - assert inicial == finalS + PublicationController contr = new PublicationController() + def lista = Publication.findAll() + result = contr.statusChanged(lista) + assert result == false TestDataAndOperations.logoutController(this) } -/** - * @author Kamilla Cardoso - * Scenario: invalid file web - */ + Given(~'^I am at the "Import XML File" page$'){ -> to LoginPage at LoginPage page.add("admin", "adminadmin") - at XMLImportPage + to XMLImportPage } When(~'^I select the "([^"]*)" button$'){ String uploadButton -> @@ -367,7 +398,7 @@ Then(~'^ the system outputs an error message$'){ -> assert page.invalidXML() } -And(~'^ no new publication is stored by the system$'){ -> +Then(~'^ no new publication is stored by the system$'){ -> to ArticlesPage at ArticlesPage page.checkIfArticlesListIsEmpty() @@ -393,7 +424,7 @@ And(~'^ no new publication is stored by the system$'){ -> page.checkIfOrientationListIsEmpty() } -And(~'^ the previously stored publications do not change$'){ +Then(~'^ the previously stored publications do not change$'){ to XMLImportPage at XMLImportPage } \ No newline at end of file diff --git a/test/files/cv.pdf b/test/files/cv.pdf new file mode 100644 index 0000000000000000000000000000000000000000..124db3749b986163cc3c81453a59b876d6ee3304 GIT binary patch literal 185176 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 literal 0 HcmV?d00001 diff --git a/test/files/cv.xml b/test/files/cv.xml index 37c76da2..2806cbbe 100644 --- a/test/files/cv.xml +++ b/test/files/cv.xml @@ -125,6 +125,11 @@ FLAG-LINHA-DE-PESQUISA-ATIVA="NAO" OBJETIVOS-LINHA-DE-PESQUISA=""> + + + diff --git a/test/functional/steps/TestDataAndOperationsPublication.groovy b/test/functional/steps/TestDataAndOperationsPublication.groovy index 92cb3127..4be55280 100644 --- a/test/functional/steps/TestDataAndOperationsPublication.groovy +++ b/test/functional/steps/TestDataAndOperationsPublication.groovy @@ -2,6 +2,8 @@ package steps import rgms.authentication.User import rgms.member.* +import rgms.publication.PublicationController +import rgms.publication.XMLController class TestDataAndOperationsPublication { @@ -10,4 +12,13 @@ class TestDataAndOperationsPublication { return members.contains(userData) } + static public void uploadPublication(filepath) { + def cont = new XMLController() + def xml = new File((String) filepath); + def records = new XmlParser() + cont.upload(records.parse(xml)) + cont.response.reset() + } + + } diff --git a/test/functional/steps/TestDataAndOperationsResearchLine.groovy b/test/functional/steps/TestDataAndOperationsResearchLine.groovy index eae46fc2..4cabd5d3 100644 --- a/test/functional/steps/TestDataAndOperationsResearchLine.groovy +++ b/test/functional/steps/TestDataAndOperationsResearchLine.groovy @@ -44,6 +44,7 @@ class TestDataAndOperationsResearchLine { res.delete() res.response.reset() } + static public void updateResearchLine(String name, String description) { def res = new ResearchLineController() def research_line = ResearchLine.findByName(name) @@ -66,4 +67,11 @@ class TestDataAndOperationsResearchLine { cont.response.reset() } + static public void listAllResearchLine(){ + def cont2 = new ResearchLineController() + cont2.request.setContent(new byte[1000]) // Could also vary the request content. + cont2.findAllResearchLine() + cont2.response.reset() + } + } diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index cd32786a..a825ab29 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -8,6 +8,8 @@ import rgms.member.Orientation import rgms.publication.Conferencia import rgms.publication.Periodico import rgms.publication.ResearchLine +import rgms.publication.ResearchLineController +import rgms.publication.XMLController import rgms.researchProject.ResearchProject /** @@ -190,4 +192,33 @@ class XMLImportTestDataAndOperations { o.save(flush: true) } //#end + + //#if(ResearchLine) + static checkResearchLineFromFile(fileName, researchName){ + getRootNode(fileName) + def researchLine = fileName.depthFirst().findAll{ it.name() == 'TITULO-DA-LINHA-DE-PESQUISA' } + def List researchs = new ArrayList() + def newResearch + + for (int i = 0; i < researchLine?.size(); ++i) { + newResearch = XMLService.checkContResearch(researchLine, i, researchName) + researchs.add(newResearch) + } + + return researchs.size() + } + + static extractSpecificResearchLineFromFile(fileName, researchLineName){ + getRootNode(fileName) + List specificResearchs = fileName.depthFirst().findAll{ it.name() == 'TITULO-DA-LINHA-DE-PESQUISA' } + for (research in specificResearchs) { + if(research.equals(researchLineName)){ + TestDataAndOperationsResearchLine.createResearchLine(researchLineName) + return true + } + } + return false + } + + } From 4cb01985bbe4894e1a02505cc31e6198941d9aeb Mon Sep 17 00:00:00 2001 From: thaisabr Date: Fri, 25 Jul 2014 02:56:04 -0300 Subject: [PATCH 16/54] =?UTF-8?q?Corre=C3=A7=C3=A3o=20na=20persist=C3=AAnc?= =?UTF-8?q?ia=20de=20publica=C3=A7=C3=B5es=20importadas=20no=20geral=20e?= =?UTF-8?q?=20nos=20dados=20usados=20nos=20testes=20associados=20=C3=A0=20?= =?UTF-8?q?ResearchProject?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rgms/publication/XMLController.groovy | 4 +- grails-app/services/rgms/XMLService.groovy | 680 +++++++++--------- grails-app/views/XML/home.gsp | 149 +++- ...esearchProjectTestDadaAndOperations.groovy | 7 +- .../XMLImportTestDataAndOperations.groovy | 61 +- 5 files changed, 490 insertions(+), 411 deletions(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index 4036481a..5ddfb290 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -1,5 +1,6 @@ package rgms.publication +import grails.converters.JSON import org.apache.shiro.SecurityUtils import rgms.authentication.User import rgms.member.Member @@ -192,8 +193,9 @@ class XMLController { } def save() { + def msg = 'default.xml.saveerror.message' Member user = getCurrentUser() - def msg = XMLService.saveImportedPublications(params, user) + msg = XMLService.saveImportedPublications(params, user) flash.message = message(code: msg) redirect(uri: '/') } diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index 500d2a65..f2db30cd 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -2,7 +2,6 @@ package rgms import org.springframework.web.multipart.MultipartHttpServletRequest import org.springframework.web.multipart.MultipartFile -import org.xml.sax.SAXParseException import rgms.member.Member import rgms.member.Orientation import rgms.publication.* @@ -18,13 +17,31 @@ class XMLService { public static final String PUB_STATUS_CONFLICTED= "conflicted" public static final String PUB_STATUS_DUPLICATED = "duplicated" + //#if($Article) + public static final JOURNAL_KEYS = ["title", "publicationDate", "authors", "journal", "volume", "number", "pages"] + //#end + public static final TOOL_KEYS = ["title", "publicationDate", "authors", "description"] + public static final BOOK_KEYS = ["title", "publicationDate", "authors", "publisher", "volume", "pages"] + public static final BOOK_CHAPTER_KEYS = ["title", "publicationDate", "authors", "publisher"] + public static final DISSERTATION_KEYS = ["title", "publicationDate", "authors", "school"] + public static final CONFERENCE_KEYS = ["title", "publicationDate", "authors", "booktitle", "pages"] + //#if($researchLine) + public static final RESEARCH_LINE_KEYS = ["name","description"] + //#end + //#if($researchProject) + public static final RESEARCH_PROJECT_KEYS = ["projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders"] + //#end + //#if($Orientation) + public static final ORIENTATION_KEYS = ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"] + //#end + /* saveEntity - closure que salva a classe de domínio que está usando a importação */ def boolean Import(Closure saveEntity, Closure returnWithMessage, - String flashMessage, String controller, - javax.servlet.http.HttpServletRequest request) { + String flashMessage, String controller, + javax.servlet.http.HttpServletRequest request) { boolean errorFound = false def publications @@ -103,10 +120,10 @@ class XMLService { for (Node currentNode : softwares) { def newTool = saveNewTool(currentNode, authorName) - def status = checkToolStatus(newTool) - if (status && status != XMLService.PUB_STATUS_DUPLICATED) { - def obj = newTool.properties.findAll{it.key in ["title","publicationDate", "authors", "description"]} - tools += ["obj": obj, "status": status] + def result = checkToolStatus(newTool) + if (result?.status && result?.status != XMLService.PUB_STATUS_DUPLICATED) { + def obj = newTool.properties.findAll{it.key in XMLService.TOOL_KEYS} + tools += [obj: obj, status:result.status, id:result.id] } } @@ -138,7 +155,7 @@ class XMLService { def status = XMLService.PUB_STATUS_STABLE def toolDB = Ferramenta.findByTitle(tool.title) if(toolDB) status = checkPublicationStatus(toolDB, tool) - return status + return [status:status, id:toolDB?.id] } def createBooks(Node xmlFile, String authorName) { @@ -148,10 +165,10 @@ class XMLService { int i = 0 for (Node currentNode : publishedBooks) { def newBook = createNewBook(currentNode, i, authorName) - def status = checkBookStatus(newBook) - if(status && status != PUB_STATUS_DUPLICATED) { - def obj = newBook.properties.findAll{it.key in ["title","publicationDate", "authors", "publisher", "volume", "pages"]} - booksList += ["obj": obj, "status": status] + def result = checkBookStatus(newBook) + if(result?.status && result?.status != PUB_STATUS_DUPLICATED) { + def obj = newBook.properties.findAll{it.key in XMLService.BOOK_KEYS} + booksList += [obj: obj, status:result.status, id:result.id] } ++i } @@ -163,8 +180,8 @@ class XMLService { List book = currentNode.children() Node basicData = (Node) book[0] Node bookDetails = (Node) book[1] - Book newBook = new Book() + Book newBook = new Book() newBook.members = [] newBook = (Book) addAuthors(currentNode, newBook) if(!newBook.authors.contains(authorName)) return null //the user is not author @@ -183,7 +200,7 @@ class XMLService { def status = PUB_STATUS_STABLE def bookDB = Book.findByTitleAndVolume(book.title, book.volume) if(bookDB) status = checkPublicationStatus(bookDB, book) - return status + return [status:status, id:bookDB?.id] } def createBooksChapters(Node xmlFile, String authorName) { @@ -192,10 +209,10 @@ class XMLService { for (int i = 0; i < publishedBookChapters?.size(); ++i) { def newBookChapter = createNewBookChapter(publishedBookChapters, i, authorName) - def status = checkBookChapterStatus(newBookChapter) - if(status && status != PUB_STATUS_DUPLICATED) { - def obj = newBookChapter.properties.findAll{it.key in ["title","publicationDate", "authors", "publisher"]} - bookChaptersList += ["obj": obj, "status": status] + def result = checkBookChapterStatus(newBookChapter) + if(result?.status && result?.status != PUB_STATUS_DUPLICATED) { + def obj = newBookChapter.properties.findAll{it.key in XMLService.BOOK_CHAPTER_KEYS} + bookChaptersList += [obj: obj, status:result.status, id:result.id] } } @@ -224,7 +241,7 @@ class XMLService { def status = PUB_STATUS_STABLE def bookChapterDB = BookChapter.findByTitleAndChapter(bookChapter.title, bookChapter.chapter) if(bookChapterDB) status = checkPublicationStatus(bookChapterDB, bookChapter) - return status + return [status:status, id:bookChapterDB?.id] } private static Publication addAuthors(publication, newPublication) { @@ -245,8 +262,8 @@ class XMLService { def status = checkDissertationOrThesisStatus(dissertationDB, newDissertation) if(status == PUB_STATUS_DUPLICATED) return null - def obj = newDissertation.properties.findAll{it.key in ["title","publicationDate", "authors", "school"]} - return ["obj": obj, "status":status] + def obj = newDissertation.properties.findAll{it.key in XMLService.DISSERTATION_KEYS} + return [obj: obj, status:status, id:dissertationDB?.id] } private static saveMasterDissertation(Node xmlFile, String authorName){ @@ -281,8 +298,8 @@ class XMLService { def status = checkDissertationOrThesisStatus(thesisDB, newThesis) if(status == PUB_STATUS_DUPLICATED) return null - def obj = newThesis.properties.findAll{it.key in ["title","publicationDate", "authors", "school"]} - return ["obj": obj, "status":status] + def obj = newThesis.properties.findAll{it.key in XMLService.DISSERTATION_KEYS} + return [obj: obj, status:status, id:thesisDB?.id] } private static saveThesis(Node xmlFile, String authorName){ @@ -312,12 +329,12 @@ class XMLService { def conferences = [] for (Node currentNode : conferencePublications) { - def newConference = saveNewConferencia(currentNode, authorName); - def status = checkConferenceStatus(newConference) + def newConference = saveNewConferencia(currentNode, authorName) + def result = checkConferenceStatus(newConference) - if(status && status != PUB_STATUS_DUPLICATED){ - def obj = newConference.properties.findAll{it.key in ["title","publicationDate", "authors", "booktitle", "pages"]} - conferences += ["obj": obj, "status":status] + if(result?.status && result?.status != PUB_STATUS_DUPLICATED){ + def obj = newConference.properties.findAll{it.key in XMLService.CONFERENCE_KEYS} + conferences += [obj: obj, status:result.status, id:result.id] } } @@ -355,7 +372,7 @@ class XMLService { def status = PUB_STATUS_STABLE def conferenceDB = Conferencia.findByTitleAndBooktitle(conference.title, conference.booktitle) if(conferenceDB) status = checkPublicationStatus(conferenceDB, conference) - return status + return [status:status, id:conferenceDB?.id] } //#if($Article) @@ -365,11 +382,11 @@ class XMLService { for (int i = 0; i < publishedArticles?.size(); ++i) { def newJournal = saveNewJournal(publishedArticles, i, authorName) - def status = checkJournalStatus(newJournal) + def result = checkJournalStatus(newJournal) - if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newJournal.properties.findAll{it.key in ["title","publicationDate", "authors", "journal", "volume", "number", "pages"]} - journals += ["obj": obj, "status":status] + if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newJournal.properties.findAll{it.key in XMLService.JOURNAL_KEYS} + journals += [obj: obj, status:result.status, id:result.id] } } @@ -400,7 +417,7 @@ class XMLService { def status = XMLService.PUB_STATUS_STABLE def journalDB = Periodico.findByJournalAndTitle(journal.journal,journal.title) if(journalDB) status = checkPublicationStatus(journalDB, journal) - return status + return [status:status, id:journalDB?.id] } private static void getJournalTitle(Node basicData, Periodico newJournal) { @@ -513,11 +530,11 @@ class XMLService { def masterOrientations = completedOrientationNode?.getAt("ORIENTACOES-CONCLUIDAS-PARA-MESTRADO") for(Node orientation: masterOrientations){ def newOrientation = fillOrientationData(orientation, user, "Mestrado") - def status = checkOrientationStatus(newOrientation, user) + def result = checkOrientationStatus(newOrientation, user) - if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso", "orientador"]} - orientations += ["obj": obj, "status":status] + if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newOrientation.properties.findAll{it.key in XMLService.ORIENTATION_KEYS} + orientations += ["obj": obj, status:result.status, id:result.id] } } return orientations @@ -527,11 +544,11 @@ class XMLService { def thesisOrientations = completedOrientationNode?.getAt("ORIENTACOES-CONCLUIDAS-PARA-DOUTORADO") for(Node orientation: thesisOrientations){ def newOrientation = fillOrientationData(orientation, user, "Doutorado") - def status = checkOrientationStatus(newOrientation, user) + def result = checkOrientationStatus(newOrientation, user) - if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso", "orientador"]} - orientations += ["obj": obj, "status":status] + if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newOrientation.properties.findAll{it.key in XMLService.ORIENTATION_KEYS} + orientations += ["obj": obj, status:result.status, id:result.id] } } return orientations @@ -544,11 +561,11 @@ class XMLService { for(Node orientation: undergraduateResearch){ def newOrientation = fillOrientationData(orientation, user, "Iniciação Científica") - def status = checkOrientationStatus(newOrientation, user) + def result = checkOrientationStatus(newOrientation, user) - if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newOrientation.properties.findAll{it.key in ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"]} - orientations += ["obj": obj, "status":status] + if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newOrientation.properties.findAll{it.key in XMLService.ORIENTATION_KEYS} + orientations += [obj: obj, status:result.status, id:result.id] } } return orientations @@ -577,7 +594,7 @@ class XMLService { status = XMLService.PUB_STATUS_CONFLICTED } } - return status + return [status:status, id:orientationDB?.id] } private static fillOrientationData(Node node, Member user, String type) { @@ -607,11 +624,11 @@ class XMLService { def researchLines = i.getAt("LINHA-DE-PESQUISA") for(Node j:researchLines){ def newResearchLine = saveResearchLine(j) - def status = checkResearchLineStatus(newResearchLine) + def result = checkResearchLineStatus(newResearchLine) - if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newResearchLine.properties.findAll{it.key in ["name","description"]} - researchLinesList += ["obj": obj, "status":status] + if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newResearchLine.properties.findAll{it.key in XMLService.RESEARCH_LINE_KEYS} + researchLinesList += [obj: obj, status:result.status, id:result.id] } } } @@ -620,6 +637,8 @@ class XMLService { private static saveResearchLine(Node xmlFile) { ResearchLine newResearchLine = new ResearchLine() + newResearchLine.members = [] + newResearchLine.publications = [] newResearchLine.name = getAttributeValueFromNode(xmlFile, "TITULO-DA-LINHA-DE-PESQUISA") newResearchLine.description = getAttributeValueFromNode(xmlFile, "OBJETIVOS-LINHA-DE-PESQUISA") return newResearchLine @@ -648,7 +667,7 @@ class XMLService { status = XMLService.PUB_STATUS_CONFLICTED } } - return status + return [status:status, id:rlDB?.id] } //#end @@ -662,13 +681,11 @@ class XMLService { for(Node project: researchProjects){ def newProject = saveResearchProject(project) - def status = checkResearchProjectStatus(newProject) + def result = checkResearchProjectStatus(newProject) - if(status && status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newProject.properties.findAll{ - it.key in ["projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders"] - } - researchProjectsList += ["obj": obj, "status":status] + if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { + def obj = newProject.properties.findAll{ it.key in XMLService.RESEARCH_PROJECT_KEYS } + researchProjectsList += [obj: obj, status:result.status, id:result.id] } } return researchProjectsList @@ -697,7 +714,7 @@ class XMLService { } } - return status + return [status:status, id:researchProjectDB?.id] } private static saveResearchProject(Node xmlFile) { @@ -725,11 +742,10 @@ class XMLService { //#end //#if($funder) - private static void fillFunders(Node xmlFile, ResearchProject project) { + private static fillFunders(Node xmlFile, ResearchProject project) { for (Node node : xmlFile?.children()) { String code = getAttributeValueFromNode(node, "CODIGO-INSTITUICAO") Funder funder = Funder.findByCode(code) - if (funder) { project.addToFunders(funder) } @@ -741,7 +757,7 @@ class XMLService { newFunder.nature = getAttributeValueFromNode(node, "NATUREZA") project.addToFunders(newFunder) } - } + } } //#end @@ -764,151 +780,149 @@ class XMLService { newMember.save(flush: false) } + private static extractDate(params,name,i,k){ + def dateString = params[name+i+".$k"] + if(!dateString || dateString=="null") return null + def date = new Date() + date.set(year: dateString as int) + return date + } + private static extractNamesSet(params,name,i,k){ + def authors = params.keySet()?.findAll{ it.contains(name+i+".$k")}?.sort() + if(!authors || authors=="null") return null - private def extractImportedJournals(name, params, keys){ - def journals = [] - def journalKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} - - if(!journalKeySet || !keys) return journals - - def n = journalKeySet.size()/keys.size() - for(i in 0..n-1){ - def journal = [:] - keys?.each{ k -> //"title", "publicationDate", "authors", "journal", "volume", "number", "pages" - if(k=="publicationDate") journal.put("$k", extractDate(params,name,i,k)) - else if(k=="au thors") { - journal.put("$k", extractAuthors(params,name,i,k)) - } - else if(k=="volume" || k=="number") journal.put("$k", params[name+i+".$k"] as int) - else{ - def value = params[name+i+".$k"] - if(value == "null") value = null - journal.put("$k", value) - } - } - journals += journal + def result = [] as Set + authors.each{ author -> + result += params[author] } - return journals - } - - private def extractImportedPublications(name, params, keys){ - def pubs = [] - def pubKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} - if(!pubKeySet || !keys) return pubs + return result + } - def n = pubKeySet.size()/keys.size() - for(i in 0..n-1){ - def pub = [:] - keys.each{ k -> //"title", "publicationDate" e strings - if(k=="publicationDate") pub.put("$k", extractDate(params,name,i,k)) - else if(k=="authors") pub.put("$k", extractAuthors(params,name,i,k)) - else{ - def value = params[name+i+".$k"] - if(value == "null") value = null - pub.put("$k", value) - } + private static extractFunders(params,name,i,k){ + def result = [] + def funders = params.keySet()?.findAll{ it.contains(name+i+".$k")}?.sort() + if(!funders || funders=="null") return result + + def funderKeys = ["name", "code", "nature"] + def n = funders.size()/funderKeys.size() + for(j in 0..n-1) { + def funder = new Funder() + funderKeys.each{ fk -> + def value = params[name+i+".$k"+j+".$fk"] + if(value == "null") value = null + funder."$fk" = value } - pubs += pub + result += funder } - return pubs + return result } - //if(ResearchLine) - private static checkContResearch(List researchLine, int i, String researchName) { - List researchs = ((Node) researchLine[i]).children() - Node basicData = (Node) researchs[0] - List lista = new ArrayList() - - ResearchLine newResearch = new ResearchLine() + private static calcImportedPublicationSize(importedKeys, keys){ + def authors = importedKeys.findAll{ it.contains("authors")} + def authorsTotal = (authors) ? authors.size() : 0 + def n = (importedKeys.size()-authorsTotal)/(keys.size()-1) + } - while (!basicData.children().empty){ - newResearch = getResearchLine(basicData, researchName) - lista.add(newResearch) - researchs.add(this) + private static getObject(name){ + switch(name){ + case "journals": return new Periodico() + case "tools": return new Ferramenta() + case "books": return new Book() + case "bookChapters": return new BookChapter() + case "conferences": return new Conferencia() + //#if($researchLine) + case "researchLines": return new ResearchLine() + //#end + //#if($researchProject) + case "researchProjects": return new ResearchProject() + //#end + //#if($Orientation) + case "orientations": return new Orientation() + //#end + default: return null } - return lista } - private static String getResearchLine(Node basicData, ResearchLine researchLine) { - researchLine.name = getAttributeValueFromNode(basicData, "TITULO-DA-LINHA-DE-PESQUISA") - return researchLine.name + //esse método deveria funcionar com ResearchProject, mas não funciona + private static fullImportedPublication(keys, params, name, i){ + def pub = getObject(name) + keys.each{ k -> + switch (k){ + case "publicationDate": pub."$k" = XMLService.extractDate(params,name,i,k) + break + case "authors": pub."$k" = XMLService.extractNamesSet(params,name,i,k) + break + case "id": + case "volume": + case "number": + case "startYear": + case "endYear": pub."$k" = params[name+i+".$k"] as int + break + //#if($researchProject) + case "members": pub."$k" = XMLService.extractNamesSet(params,name,i,k) + break + //#end + //#if($funder) + case "funders": + def funders = XMLService.extractFunders(params,name,i,k) + saveImportedFunders(funders, pub) + break + //#end + //#if($Orientation) + case "anoPublicacao": pub."$k" = params[name+i+".$k"] as int + break + //#end + default: def value = params[name+i+".$k"] + if(value == "null") value = null + pub."$k" = value + } + } + return pub } - //end - + private def extractImportedPublications(name, params, keys){ + def pubs = [] + def pubKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} - private def extractBooks(name, params, keys){ - def books = [] - def bookKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} - - if(!bookKeySet || !keys) return books + if(!pubKeySet || !keys) return pubs - def n = bookKeySet.size()/keys.size() + def n = XMLService.calcImportedPublicationSize(pubKeySet, keys) for(i in 0..n-1){ - def book = [:] - keys?.each{ k -> //"title", "publicationDate", "authors", "publisher", "volume", "pages" - if(k=="publicationDate") book.put("$k", extractDate(params,name,i,k)) - else if(k=="authors") book.put("$k", extractAuthors(params,name,i,k)) - else if(k=="volume") book.put("$k", params[name+i+".$k"] as int) - else{ - def value = params[name+i+".$k"] - if(value == "null") value = null - book.put("$k", value) - } - } - books += book + String title = params[name+i+".title"] + if(!title || title.isEmpty()) continue + def pub = XMLService.fullImportedPublication(keys, params, name, i) + pubs += pub } - return books + return pubs } - private def extractDissertation(name, params, keys){ + private def extractDissertation(obj, name, params, keys){ def dissertationKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} - def dissertation = [:] - if(dissertationKeySet.isEmpty()) return dissertation + if(dissertationKeySet.isEmpty()) return null + obj.title = params[name+"0.title"] + if(!obj.title || obj.title.isEmpty()) return null keys?.each{ k -> //"title", "publicationDate", "authors", "school" - if(k=="publicationDate") dissertation.put("$k", extractDate(params,name,0,k)) - else if(k=="authors") dissertation.put("$k", extractAuthors(params,name,0,k)) - else{ - def value = params[name+"0.$k"] - if(value == "null") value = null - dissertation.put("$k", value) + switch (k){ + case "publicationDate": obj."$k" = XMLService.extractDate(params,name,0,k) + break + case "authors": obj."$k" = XMLService.extractNamesSet(params,name,0,k) + break + case "id": obj."$k" = params[name+"0.$k"] as int + break + default: + def value = params[name+"0.$k"] + if(value == "null") value = null + obj."$k" = value } } - return dissertation - } - - - private def extractDate(params,name,i,k){ - def dateString = params[name+i+".$k"] - if(!dateString || dateString=="null") return null - def date = new Date() - def year = dateString?.substring(dateString?.length()-5,dateString?.length()) as int - date.set(year: year) - return date - } - - private def extractAuthors(params,name,i,k){ - def authors = params[name+i+".$k"] - if(!authors || authors=="null") return null - def authorsList = authors?.substring(1,authors.length()-1)?.split(",") as List - def result = [] - authorsList.each{ - if(it.startsWith(" ")){ - result += it.substring(1) - } - else result += it - } - if(result.isEmpty()) return authorsList - else return result + return obj } - - //#if($researchLine) private def extractResearchLines(name, params, keys){ def lines = [] @@ -918,59 +932,66 @@ class XMLService { def n = linesKeySet.size()/keys.size() for(i in 0..n-1){ - def line = [:] - keys.each{ k -> //"name", "description" - def value = params[name+i+".$k"] - if(value == "null") value = null - line.put("$k", value) - } - lines += line + String title = params[name+i+".name"] + if(!title || title.isEmpty()) continue + def pub = XMLService.fullImportedPublication(keys, params, name, i) + pub.publications = [] + lines += pub } return lines } //#end - //#if($researchProject && $funder) + //#if($researchProject) private def extractResearchProjects(name, params, keys){ def projects = [] def projectKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} if(!projectKeySet || !keys) return projects - def n = projectKeySet.size()/keys.size() + def n = XMLService.calcImportedResearchProjects(projectKeySet,keys) for(i in 0..n-1){ - def project = [:] - keys?.each{ k -> //"projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders" - if(k=="startYear" || k=="endYear") project.put("$k", params[name+i+".$k"] as int) - else if(k=="funders") project.put("$k", extractFunders(params,name,i,k)) - else if(k=="members") project.put("$k", extractAuthors(params,name,i,k)) - else{ - def value = params[name+i+".$k"] - if(value == "null") value = null - project.put("$k", value) + String title = params[name+i+".projectName"] + if(!title || title.isEmpty()) continue + + def pub = new ResearchProject() + keys.each{ k -> //"projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders" + switch (k){ + case "id": + case "startYear": + case "endYear": pub."$k" = params[name+i+".$k"] as int + break + //#if($researchProject) + case "members": pub."$k" = XMLService.extractNamesSet(params,name,i,k) + break + //#end + //#if($funder) + case "funders": + def funders = XMLService.extractFunders(params,name,i,k) + saveImportedFunders(funders, pub) + break + //#end + default: def value = params[name+i+".$k"] + if(value == "null") value = null + pub."$k" = value } } - projects += project + projects += pub } return projects } - private def extractFunders(params,name,i,k){ - String fundersString = params[name+i+".$k"] - if(!fundersString || fundersString=="null") return [] - - fundersString = fundersString.substring(1,fundersString.length()-1) - def index1 = fundersString.indexOf("]") - def index2 = fundersString.lastIndexOf("[") - def code = fundersString.substring(1,index1) - def funderName = fundersString.substring(index1+2,index2-1) - def nature = fundersString.substring(index2+1,fundersString.length()-1) - return [code:code, name:funderName, nature:nature] + private static calcImportedResearchProjects(importedKeys, keys){ + def funders = importedKeys.findAll{ it.contains("funders")} + def fundersTotal = (funders) ? funders.size() : 0 + def members = importedKeys.findAll{ it.contains("members")} + def membersTotal = (members) ? members.size() : 0 + def n = (importedKeys.size()-fundersTotal-membersTotal)/(keys.size()-2) } //#end //#if(Orientation) - private def extractOrientations(name, params, keys){ + private def extractOrientations(name, params, keys, user){ def orientations = [] def orientationKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} @@ -978,15 +999,10 @@ class XMLService { def n = orientationKeySet.size()/keys.size() for(i in 0..n-1){ - def orientation = [:] - keys?.each{ k -> //"tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso" - if(k=="anoPublicacao") orientation.put("$k", params[name+i+".$k"] as int) - else{ - def value = params[name+i+".$k"] - if(value == "null") value = null - orientation.put("$k", value) - } - } + String title = params[name+i+".tituloTese"] + if(!title || title.isEmpty()) continue + def orientation = XMLService.fullImportedPublication(keys, params, name, i) + orientation.orientador = user orientations += orientation } return orientations @@ -994,158 +1010,142 @@ class XMLService { //#end def saveImportedPublications(params, user) { - def flashMessage = 'default.xml.save.message' - - try { - //#if($Article) - def journalKeys = ["title", "publicationDate", "authors", "journal", "volume", "number", "pages"] - def journals = extractImportedJournals("journals", params, journalKeys) - saveImportedJournals(journals) - //#end - - def toolKeys = ["title", "publicationDate", "authors", "description"] - def tools = extractImportedPublications("tools", params, toolKeys) - saveImportedTools(tools) - - def bookKeys = ["title", "publicationDate", "authors", "publisher", "volume", "pages"] - def books = extractBooks("books", params, bookKeys) - saveImportedBooks(books) + //#if($Article) + def journals = extractImportedPublications("journals", params, XMLService.JOURNAL_KEYS) + println "total de journals importados = "+journals.size() + save(journals) + println "salvou journals!" + //#end - def bookChapterKeys = ["title", "publicationDate", "authors", "publisher"] - def bookChapters = extractImportedPublications("bookChapters", params, bookChapterKeys) - saveImportedBookChapters(bookChapters) + def tools = extractImportedPublications("tools", params, TOOL_KEYS) + println "total de tools importados = "+tools.size() + save(tools) + println "salvou tools!" - def dissertationKeys = ["title", "publicationDate", "authors", "school"] - def dissertation = extractDissertation("masterDissertation", params, dissertationKeys) - if (dissertation) saveImportedDissertation(dissertation) + def books = extractImportedPublications("books", params, BOOK_KEYS) + println "total de books importados = "+books.size() + save(books) + println "salvou books!" - def thesis = extractDissertation("thesis", params, dissertationKeys) - if (thesis) saveImportedThesis(thesis) + def bookChapters = extractImportedPublications("bookChapters", params, BOOK_CHAPTER_KEYS) + println "total de bookChapters importados = "+bookChapters.size() + save(bookChapters) + println "salvou bookChapters!" - def conferenceKeys = ["title", "publicationDate", "authors", "booktitle", "pages"] - def conferences = extractImportedPublications("conferences", params, conferenceKeys) - saveImportedConferences(conferences) + def dissertation = extractDissertation(new Dissertacao(), "masterDissertation", params, DISSERTATION_KEYS) + if (dissertation) save([dissertation]) + println "salvou dissertation!" - //#if($researchLine) - def researchLineKeys = ["name","description"] - def researchLines = extractResearchLines("researchLines", params, researchLineKeys) - saveImportedResearchLines(researchLines) - //#end + def thesis = extractDissertation(new Tese(), "thesis", params, DISSERTATION_KEYS) + if (thesis) save([thesis]) + println "salvou thesis!" - //#if($researchProject) - def projectKeys = ["projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders"] - def researchProjects = extractResearchProjects("researchProjects", params, projectKeys) - saveImportedResearchProjects(researchProjects) - //#end + def conferences = extractImportedPublications("conferences", params, CONFERENCE_KEYS) + println "total de conferences importados = "+conferences.size() + save(conferences) + println "salvou conferences!" - //#if($Orientation) - def orientationKeys = ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"] - def orientations = extractOrientations("orientations", params, orientationKeys) - saveImportedOrientations(orientations, user) - //#end + //#if($researchLine) + def researchLines = extractResearchLines("researchLines", params, RESEARCH_LINE_KEYS) + println "total de researchLines importados = "+researchLines.size() + save(researchLines) + println "salvou researchLines!" + //#end - } catch(Exception ex){ - flashMessage = 'default.xml.saveerror.message' - ex.printStackTrace() - } + //#if($researchProject) + def researchProjects = extractResearchProjects("researchProjects", params, RESEARCH_PROJECT_KEYS) + println "total de researchProjects importados = "+researchProjects.size() + saveImportedResearchProjects(researchProjects) + println "salvou researchProjects!" + //#end - return flashMessage - } + //#if($Orientation) + def orientations = extractOrientations("orientations", params, ORIENTATION_KEYS, user) + println "total de orientations importados = "+orientations.size() + saveImportedOrientations(orientations) + println "salvou orientations!" + //#end - //#if($Article) - def saveImportedJournals(journals) { - journals.eachWithIndex(){ element, index -> - Periodico p = new Periodico(element) - p.members = [] - p.save(flush:true) - } + return 'default.xml.save.message' } - //#end - def saveImportedTools(tools) { - tools?.eachWithIndex(){ element, index -> - Ferramenta f = new Ferramenta(element) - f.members = [] - f.save(flush:true) + def save(publications) { + publications?.each{ pub -> + if(!pub?.members) pub?.members = [] + if(pub.id){ + def originalPub = Publication.get(pub.id) + originalPub.properties = pub.properties + originalPub.save(flush:true) + } + else{ + pub.save(flush:true) + } } } - def saveImportedBooks(books) { - books?.eachWithIndex(){ element, index -> - Book b = new Book(element) - b.members = [] - b.save(flush:true) + //#if($researchProject) + def saveImportedResearchProjects(researchProjects){ + researchProjects?.each { rp -> + if(rp.id){ + def originalRP = ResearchProject.get(rp.id) + originalRP.properties = rp.properties + originalRP.save(flush:true) + } + else{ + rp.save(flush:true) + } } } + //#end - def saveImportedBookChapters(bookChapters) { - bookChapters?.eachWithIndex(){ element, index -> - BookChapter bc = new BookChapter(element) - bc.members = [] - bc.save(flush:true) + //#if($funder) + public def saveImportedFunders(funders, ResearchProject project){ + funders?.each(){ f -> + Funder funder = Funder.findByCode(f.code) + if (funder) { + project.addToFunders(funder) + } else { + f.save(flush:true) + project.addToFunders(f) + } } } + //#end - def saveImportedDissertation(masterDissertation) { - Dissertacao d = new Dissertacao(masterDissertation) - d.members = [] - d.save(flush:true) - } - - def saveImportedThesis(thesis) { - Tese t = new Tese(thesis) - t.members = [] - t.save(flush:true) - } - - def saveImportedConferences(conferences) { - conferences?.eachWithIndex(){ element, index -> - Conferencia c = new Conferencia(element) - c.members = [] - c.save(flush:true) + //#if($Orientation) + def saveImportedOrientations(orientations){ + orientations?.each{ orientation -> + if(orientation.id){ + def originalOrientation = Orientation.get(orientation.id) + originalOrientation.properties = orientation.properties + originalOrientation.save(flush:true) + } + else{ + orientation.save(flush:true) + } } } + //#end //#if($researchLine) - def saveImportedResearchLines(researchLines){ - researchLines?.eachWithIndex(){ element, index -> - ResearchLine rl = new ResearchLine(element) - rl.members = [] - rl.publications = [] - rl.save(flush:true) - } - } - //#end + private static checkContResearch(List researchLine, int i, String researchName) { + List researchs = ((Node) researchLine[i]).children() + Node basicData = (Node) researchs[0] + List lista = new ArrayList() - //#if($researchProject) - def saveImportedResearchProjects(researchProjects){ - researchProjects?.eachWithIndex(){ element, index -> - //#if($funder) - saveImportedFunders(element.funders) - //#end - def rp = new ResearchProject(element) - rp.save(flush:true) - } - } - //#end + ResearchLine newResearch = new ResearchLine() - //#if($funder) - def saveImportedFunders(project){ - project?.funders?.each(){ f -> - f = Funder.findByCode(f.code) - if(!f) f.save(flush:true) - project.addToFunders(f).save(flush:true) + while (!basicData.children().empty){ + newResearch = getResearchLine(basicData, researchName) + lista.add(newResearch) + researchs.add(this) } + return lista } - //#end - //#if($Orientation) - def saveImportedOrientations(orientations, user){ - orientations?.eachWithIndex(){ element, index -> - Orientation o = new Orientation(element) - o.orientador = user - o.save(flush:true) - } + private static String getResearchLine(Node basicData, ResearchLine researchLine) { + researchLine.name = getAttributeValueFromNode(basicData, "TITULO-DA-LINHA-DE-PESQUISA") + return researchLine.name } - //#end + //end } diff --git a/grails-app/views/XML/home.gsp b/grails-app/views/XML/home.gsp index e4bc0293..20c9250b 100644 --- a/grails-app/views/XML/home.gsp +++ b/grails-app/views/XML/home.gsp @@ -27,12 +27,12 @@ - - +
+ @@ -44,18 +44,24 @@ + - - + + - - + + @@ -63,15 +69,21 @@ + - - + + - - + + @@ -80,17 +92,23 @@ + - - + + - - + + @@ -98,62 +116,78 @@ + - - + + - - + + + - - + + + - - + + + - - + + + - - + + + - - + + - - + + @@ -161,13 +195,15 @@ + + - - + + @@ -176,21 +212,31 @@ + - - + + + + + + - - + + @@ -199,6 +245,7 @@ + @@ -206,6 +253,7 @@ + @@ -222,8 +270,33 @@
- + + + + - + \ No newline at end of file diff --git a/test/functional/steps/ResearchProjectTestDadaAndOperations.groovy b/test/functional/steps/ResearchProjectTestDadaAndOperations.groovy index c6daa152..80955cfb 100644 --- a/test/functional/steps/ResearchProjectTestDadaAndOperations.groovy +++ b/test/functional/steps/ResearchProjectTestDadaAndOperations.groovy @@ -2,6 +2,7 @@ package steps import rgms.publication.XMLController import rgms.researchProject.ResearchProject +import rgms.researchProject.Funder import rgms.researchProject.ResearchProjectController /** @@ -18,8 +19,7 @@ class ResearchProjectTestDadaAndOperations { responsible: "Paulo Henrique Monteiro Borba", startYear: 2000, endYear: 2003, - funders: FunderTestDataAndOperations.funder[0], - members: ["Rubens Lopes da Silva"] as Set + members: ["Paulo Henrique Monteiro Borba", "Rubens Lopes da Silva"] as Set ], [projectName:"Implementação Progressiva de Aplicações Orientadas a Aspectos", description:"Neste projeto pretendemso definir e validar um método para a implementação de aplicações orientadas a Aspectos. Em particular, este método deve suportar uma abordagem progressiva para implementação orientada a aspectos, de forma que aspectos de distribuição, concorrência, e persistência não sejam inicialmente considerados pelo processo de implementação, mas sejam gradualmente introduzidos, preservando os requisitos funcionais da aplicação.", @@ -27,7 +27,7 @@ class ResearchProjectTestDadaAndOperations { responsible: "Paulo Henrique Monteiro Borba", startYear: 2001, endYear: 2004, - members: ["Bruno Soares da Silva","Dyego Felipe Oliveira de Penha", "Pedro Henrique Torres Gonçalves"] as Set + members: ["Paulo Henrique Monteiro Borba", "Bruno Soares da Silva","Dyego Felipe Oliveira de Penha", "Pedro Henrique Torres Gonçalves"] as Set ] ] @@ -70,7 +70,6 @@ class ResearchProjectTestDadaAndOperations { static public boolean compatibleTo(project, projectName) { def testProject = findResearchProjectByProjectName(projectName) - testProject.funders = null //desconsiderar provisoriamente def compatible = false if (testProject == null && project == null) { compatible = true diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index a825ab29..fcbf6d1d 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -11,6 +11,7 @@ import rgms.publication.ResearchLine import rgms.publication.ResearchLineController import rgms.publication.XMLController import rgms.researchProject.ResearchProject +import rgms.researchProject.Funder /** * Created by Thaís Burity on 02/06/2014. @@ -142,13 +143,44 @@ class XMLImportTestDataAndOperations { rl = new ResearchLine(ResearchLineTestDataAndOperations.researchlines[1]) rl.save(flush: true) } + + static checkResearchLineFromFile(fileName, researchName){ + getRootNode(fileName) + def researchLine = fileName.depthFirst().findAll{ it.name() == 'TITULO-DA-LINHA-DE-PESQUISA' } + def List researchs = new ArrayList() + def newResearch + + for (int i = 0; i < researchLine?.size(); ++i) { + newResearch = XMLService.checkContResearch(researchLine, i, researchName) + researchs.add(newResearch) + } + + return researchs.size() + } + + static extractSpecificResearchLineFromFile(fileName, researchLineName){ + getRootNode(fileName) + List specificResearchs = fileName.depthFirst().findAll{ it.name() == 'TITULO-DA-LINHA-DE-PESQUISA' } + for (research in specificResearchs) { + if(research.equals(researchLineName)){ + TestDataAndOperationsResearchLine.createResearchLine(researchLineName) + return true + } + } + return false + } //#end //#if($ResearchProject) static void initializeResearchProjectDB() { + def xmlservice = new XMLService() ResearchProject rp = new ResearchProject(ResearchProjectTestDadaAndOperations.researchProjects[0]) - rp.funders = null + //#if($funder) + def funder1 = new Funder(FunderTestDataAndOperations.funder[0]) + xmlservice.saveImportedFunders([funder1], rp) + //#end rp.save(flush: true) + rp = new ResearchProject(ResearchProjectTestDadaAndOperations.researchProjects[1]) rp.save(flush: true) } @@ -193,32 +225,5 @@ class XMLImportTestDataAndOperations { } //#end - //#if(ResearchLine) - static checkResearchLineFromFile(fileName, researchName){ - getRootNode(fileName) - def researchLine = fileName.depthFirst().findAll{ it.name() == 'TITULO-DA-LINHA-DE-PESQUISA' } - def List researchs = new ArrayList() - def newResearch - - for (int i = 0; i < researchLine?.size(); ++i) { - newResearch = XMLService.checkContResearch(researchLine, i, researchName) - researchs.add(newResearch) - } - - return researchs.size() - } - - static extractSpecificResearchLineFromFile(fileName, researchLineName){ - getRootNode(fileName) - List specificResearchs = fileName.depthFirst().findAll{ it.name() == 'TITULO-DA-LINHA-DE-PESQUISA' } - for (research in specificResearchs) { - if(research.equals(researchLineName)){ - TestDataAndOperationsResearchLine.createResearchLine(researchLineName) - return true - } - } - return false - } - } From b599f331ffeade75d5fbde9eca72a061d8dc660c Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sat, 26 Jul 2014 13:53:10 -0300 Subject: [PATCH 17/54] =?UTF-8?q?Ajustes=20quanto=20ao=20uso=20de=20intern?= =?UTF-8?q?acionaliza=C3=A7=C3=A3o=20em=20XMLImport.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/i18n/messages.properties | 15 ++++++++- grails-app/i18n/messages_pt_BR.properties | 20 ++++++++++++ grails-app/views/XML/home.gsp | 37 ++++++++++++----------- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/grails-app/i18n/messages.properties b/grails-app/i18n/messages.properties index 537fc575..1ce3d8a2 100644 --- a/grails-app/i18n/messages.properties +++ b/grails-app/i18n/messages.properties @@ -165,6 +165,8 @@ tese.duplicatetitle.failure = Thesis not stored because there is another one wit #end orientation.same.members=Um membro nao pode orientar a si mesmo + +#if($importXML) default.xml.parserror.message=No file uploaded or it wasn't a valid XML default.xml.structure.message=The XML struct doesn't comply with Lattes default.xml.unknownerror.message=An unknown error occurred. Contact the administrator @@ -172,6 +174,17 @@ default.xml.saveerror.message=An error ocurred when saving the imported publicat default.xml.save.message=The imported publications were saved! default.xml.import.message=The publications were imported! xml.label=XMLImport +xml.importedPublicationType.label=Publication Type +xml.importedPublicationTitle.label=Title +xml.importedPublicationDate.label=Publication Date +xml.importedPublicationAuthors.label=Authors +xml.importStatus.label=Import Status +xml.checkAll.label=To Save +xml.button.save.label=Save +#if($Orientation) +xml.importOrientation.label=Orientation Type: {0} +#end +#end file.already.exist.message=A file has already been saved with the same name @@ -225,7 +238,7 @@ orientation.tituloTese.unique = Orientation title already registered, you can no #if($researchProject) default.researchProject.label = "ResearchProject" -researchProject.label = ResearchProject +researchProject.label = Research Project default.researchproject.import.flashmessage.success = "The non existent Research Project were successfully imported" #end diff --git a/grails-app/i18n/messages_pt_BR.properties b/grails-app/i18n/messages_pt_BR.properties index 6d3cc59d..be825831 100644 --- a/grails-app/i18n/messages_pt_BR.properties +++ b/grails-app/i18n/messages_pt_BR.properties @@ -223,4 +223,24 @@ default.researchproject.import.flashmessage.success = "Os Projetos de Pesquisa n #if($researchLine) default.researchline.import.flashmessage.success = "As linhas de pesquisa que não existiam foram importadas com sucesso" +#end + +#if($importXML) +default.xml.parserror.message=Não foi selecionado nenhum arquivo ou o arquivo selecionado não é um XML válido +default.xml.structure.message=A estrutura do arquivo XML não está de acordo com o Lattes +default.xml.unknownerror.message=Ocorreu um erro desconhecido. Entre em contato com o administrador. +default.xml.saveerror.message=Ocorreu um erro ao salvar as publicações importadas. A operação foi cancelada. +default.xml.save.message=As publicações importadas foram salvas! +default.xml.import.message=As publicações foram importadas! +xml.label=XMLImport +xml.importedPublicationType.label=Tipo da Publicação +xml.importedPublicationTitle.label=Título +xml.importedPublicationDate.label=Data da Publicação +xml.importedPublicationAuthors.label=Autores +xml.importStatus.label=Estado da Importação +xml.checkAll.label=Para salvar +xml.button.save.label=Salvar +#if($Orientation) +xml.importOrientation.label=Tipo da Orientação: {0} +#end #end \ No newline at end of file diff --git a/grails-app/views/XML/home.gsp b/grails-app/views/XML/home.gsp index 20c9250b..92a04700 100644 --- a/grails-app/views/XML/home.gsp +++ b/grails-app/views/XML/home.gsp @@ -32,12 +32,13 @@
Publication Type Title Publication Date
Journal ${journalInstance?.obj?.title}${journalInstance?.obj?.authors}${journalInstance?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} + + + ${author}
+
+
${journalInstance?.status}
Tool ${toolInstance?.obj?.title}${toolInstance?.obj?.authors}${toolInstance?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} + + + ${author}
+
+
${toolInstance?.status}
Book ${bookInstance?.obj?.title}${bookInstance?.obj?.authors}${bookInstance?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} + + + ${author}
+
+
${bookInstance?.status}
Book Chapter ${bookChapterInstance?.obj?.title}${bookChapterInstance?.obj?.authors}${bookChapterInstance?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} + + + ${author}
+
+
${bookChapterInstance?.status}
Dissertation ${publications?.masterDissertation?.obj?.title}${publications?.masterDissertation?.obj?.authors}${publications?.masterDissertation?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)}${publications?.masterDissertation?.obj?.authors?.iterator()[0]} ${publications?.masterDissertation?.status}
Thesis ${publications?.thesis?.obj?.title}${publications?.thesis?.obj?.authors}${publications?.thesis?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)}${publications?.thesis?.obj?.authors?.iterator()[0]} ${publications?.thesis?.status}
Conference ${conferenceInstance?.obj?.title}${conferenceInstance?.obj?.authors}${conferenceInstance?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} + + + ${author}
+
+
${conferenceInstance?.status}
Research Line ${researchLineInstance?.obj?.name} ${researchLineInstance?.obj?.members} ${researchLineInstance?.status}
Research Project ${projectInstance?.obj?.projectName}${projectInstance?.obj?.startYear} ${projectInstance?.obj?.funders}${projectInstance?.obj?.startYear} + + + ${member}
+
+
${projectInstance?.status}
Orientação de ${orientationInstance?.obj?.tipo} ${orientationInstance?.obj?.tituloTese} ${orientationInstance?.obj?.anoPublicacao}
- - - - - - + + + + + + + @@ -53,7 +54,7 @@ - + - + - + - + - + @@ -162,7 +163,7 @@ - + @@ -179,7 +180,7 @@ - + - + @@ -228,7 +229,7 @@ - + - + + @@ -268,7 +270,8 @@
- + +
From dab201015bf929e50387962d7db12ef6063616c5 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sat, 26 Jul 2014 14:57:13 -0300 Subject: [PATCH 18/54] =?UTF-8?q?Refatora=C3=A7=C3=A3o:=20Renomea=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20m=C3=A9todos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rgms/publication/XMLController.groovy | 22 ++++++------ grails-app/services/rgms/XMLService.groovy | 34 +++++++++---------- .../XMLImportTestDataAndOperations.groovy | 6 ++-- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index 5ddfb290..bf9a422f 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -1,11 +1,8 @@ package rgms.publication -import grails.converters.JSON import org.apache.shiro.SecurityUtils import rgms.authentication.User import rgms.member.Member -import rgms.member.Orientation -import rgms.researchProject.ResearchProject /** * Created with IntelliJ IDEA. @@ -44,7 +41,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def tools = XMLService.createTools(xmlFile, user.name)*.obj - XMLService.saveImportedTools(tools) + XMLService.save(tools) } def uploadXMLBook() { @@ -58,7 +55,8 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def books = XMLService.createBooks(xmlFile, user.name)*.obj - XMLService.saveImportedBooks(books) + XMLService.save(books) + println "salvou books!" } //#if($researchLine) @@ -70,7 +68,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def researchLines = XMLService.createResearchLines(xmlFile, user.name)*.obj - XMLService.saveImportedResearchLines(researchLines) + XMLService.save(researchLines) } //#end @@ -98,7 +96,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def bookChapters = XMLService.createBooksChapters(xmlFile, user.name)*.obj - XMLService.saveImportedBookChapters(bookChapters) + XMLService.save(bookChapters) } def uploadXMLDissertacao() { @@ -111,8 +109,8 @@ class XMLController { private saveDissertations = { Node xmlFile -> Member user = getCurrentUser() - def dissertation = XMLService.createMasterDissertation(xmlFile, user.name).obj - XMLService.saveImportedDissertation(dissertation) + def dissertation = XMLService.createDissertation(xmlFile, user.name).obj + XMLService.save(dissertation) } def enviarConferenciaXML() { @@ -126,7 +124,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def conferences = XMLService.createConferencias(xmlFile, user.name)*.obj - XMLService.saveImportedConferences(conferences) + XMLService.save(conferences) } //#if($Orientation) @@ -157,7 +155,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def journals = XMLService.createJournals(xmlFile, user.name)*.obj - XMLService.saveImportedJournals(journals) + XMLService.save(journals) } //#end @@ -171,7 +169,7 @@ class XMLController { private Closure saveMember = { Node xmlFile -> Member newMember = new Member(params) - XMLService.createMember(xmlFile, newMember) + XMLService.saveMember(xmlFile, newMember) } private returnWithMessage = { String msg, String controller, publications -> diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index f2db30cd..b5ed15bc 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -87,7 +87,7 @@ class XMLService { def bookChapters = createBooksChapters(xmlFile, user.name) if(bookChapters) publications.put("bookChapters", bookChapters) - def masterDissertation = createMasterDissertation(xmlFile, user.name) + def masterDissertation = createDissertation(xmlFile, user.name) if(masterDissertation) publications.put("masterDissertation", masterDissertation) def thesis = createThesis(xmlFile, user.name) @@ -119,7 +119,7 @@ class XMLService { def tools = [] for (Node currentNode : softwares) { - def newTool = saveNewTool(currentNode, authorName) + def newTool = createNewTool(currentNode, authorName) def result = checkToolStatus(newTool) if (result?.status && result?.status != XMLService.PUB_STATUS_DUPLICATED) { def obj = newTool.properties.findAll{it.key in XMLService.TOOL_KEYS} @@ -130,7 +130,7 @@ class XMLService { return tools } - private static saveNewTool(Node currentNode, String authorName) { + private static createNewTool(Node currentNode, String authorName) { Node basicData = (Node) currentNode.children()[0] Node softwareDetails = (Node) currentNode.children()[1] Node additionalInfo = getNodeFromNode(currentNode, "INFORMACOES-ADICIONAIS") @@ -253,8 +253,8 @@ class XMLService { return newPublication } - def createMasterDissertation(Node xmlFile, String authorName) { - def newDissertation = saveMasterDissertation(xmlFile, authorName) + def createDissertation(Node xmlFile, String authorName) { + def newDissertation = createNewDissertation(xmlFile, authorName) if(!newDissertation) return null def dissertationDB = Dissertacao.findByTitle(newDissertation?.title) @@ -266,7 +266,7 @@ class XMLService { return [obj: obj, status:status, id:dissertationDB?.id] } - private static saveMasterDissertation(Node xmlFile, String authorName){ + private static createNewDissertation(Node xmlFile, String authorName){ def mestrado = xmlFile.depthFirst().find{ it.name() == 'MESTRADO' } if(!mestrado) return null @@ -289,7 +289,7 @@ class XMLService { } def createThesis(Node xmlFile, String authorName) { - def newThesis = saveThesis(xmlFile, authorName) + def newThesis = createNewThesis(xmlFile, authorName) if(!newThesis) return null def thesisDB = Tese.findByTitle(newThesis?.title) @@ -302,7 +302,7 @@ class XMLService { return [obj: obj, status:status, id:thesisDB?.id] } - private static saveThesis(Node xmlFile, String authorName){ + private static createNewThesis(Node xmlFile, String authorName){ def doutorado = xmlFile.depthFirst().find{ it.name() == 'DOUTORADO' } if(!doutorado) return null @@ -329,7 +329,7 @@ class XMLService { def conferences = [] for (Node currentNode : conferencePublications) { - def newConference = saveNewConferencia(currentNode, authorName) + def newConference = createNewConferencia(currentNode, authorName) def result = checkConferenceStatus(newConference) if(result?.status && result?.status != PUB_STATUS_DUPLICATED){ @@ -341,7 +341,7 @@ class XMLService { return conferences } - private static saveNewConferencia(conferenceNode, authorName) { + private static createNewConferencia(conferenceNode, authorName) { def newConference = null def basicData = conferenceNode?.depthFirst()?.find{ it.name() == 'DADOS-BASICOS-DO-TRABALHO' } def details = conferenceNode?.depthFirst()?.find{ it.name() == 'DETALHAMENTO-DO-TRABALHO' } @@ -381,7 +381,7 @@ class XMLService { def journals = [] for (int i = 0; i < publishedArticles?.size(); ++i) { - def newJournal = saveNewJournal(publishedArticles, i, authorName) + def newJournal = createNewJournal(publishedArticles, i, authorName) def result = checkJournalStatus(newJournal) if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { @@ -393,7 +393,7 @@ class XMLService { return journals } - private static saveNewJournal(List publishedArticlesChildren, int i, String authorName) { + private static createNewJournal(List publishedArticlesChildren, int i, String authorName) { List firstArticle = ((Node) publishedArticlesChildren[i]).children() Node basicData = (Node) firstArticle[0] Node articleDetails = (Node) firstArticle[1] @@ -623,7 +623,7 @@ class XMLService { for(Node i: researchAndDevelopment){ def researchLines = i.getAt("LINHA-DE-PESQUISA") for(Node j:researchLines){ - def newResearchLine = saveResearchLine(j) + def newResearchLine = createNewResearchLine(j) def result = checkResearchLineStatus(newResearchLine) if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { @@ -635,7 +635,7 @@ class XMLService { return researchLinesList } - private static saveResearchLine(Node xmlFile) { + private static createNewResearchLine(Node xmlFile) { ResearchLine newResearchLine = new ResearchLine() newResearchLine.members = [] newResearchLine.publications = [] @@ -680,7 +680,7 @@ class XMLService { def researchProjects = xmlFile.depthFirst().findAll{ it.name() == 'PROJETO-DE-PESQUISA' } for(Node project: researchProjects){ - def newProject = saveResearchProject(project) + def newProject = createNewResearchProject(project) def result = checkResearchProjectStatus(newProject) if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { @@ -717,7 +717,7 @@ class XMLService { return [status:status, id:researchProjectDB?.id] } - private static saveResearchProject(Node xmlFile) { + private static createNewResearchProject(Node xmlFile) { ResearchProject newProject = new ResearchProject() newProject.projectName = getAttributeValueFromNode(xmlFile, "NOME-DO-PROJETO") newProject.description = getAttributeValueFromNode(xmlFile, "DESCRICAO-DO-PROJETO") @@ -761,7 +761,7 @@ class XMLService { } //#end - static void createMember(Node xmlFile, Member newMember) { + static void saveMember(Node xmlFile, Member newMember) { Node dadosGerais = (Node) xmlFile.children()[0] List dadosGeraisChildren = dadosGerais.children() diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index fcbf6d1d..b8fa1ac8 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -47,7 +47,7 @@ class XMLImportTestDataAndOperations { def journalArticles = [] for (int i = 0; i < publishedArticles?.size(); ++i) { - def newJournal = XMLService.saveNewJournal(publishedArticles, i, name) + def newJournal = XMLService.createNewJournal(publishedArticles, i, name) if(newJournal) journalArticles += newJournal } @@ -60,7 +60,7 @@ class XMLImportTestDataAndOperations { def conferences = [] for (Node currentNode : conferencePublications) { - def newConference = XMLService.saveNewConferencia(currentNode, name); + def newConference = XMLService.createNewConferencia(currentNode, name); if(newConference) conferences += newConference } @@ -209,7 +209,7 @@ class XMLImportTestDataAndOperations { def projectsList = [] for (Node project: projects) { - def newProject = XMLService.saveResearchProject(project) + def newProject = XMLService.createNewResearchProject(project) if(newProject){ projectsList += newProject } From 7f55a3ea92fc977e25c3b1ec28beae2c4165d9be Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sat, 26 Jul 2014 19:41:39 -0300 Subject: [PATCH 19/54] =?UTF-8?q?Corre=C3=A7=C3=A3o=20para=20salvar=20publ?= =?UTF-8?q?ica=C3=A7=C3=B5es=20importadas=20em=20telas=20espec=C3=ADficas?= =?UTF-8?q?=20do=20tipo=20da=20publica=C3=A7=C3=A3o=20(o=20objetivo=20?= =?UTF-8?q?=C3=A9=20deixar=20o=20comportamento=20antigo=20em=20perfeito=20?= =?UTF-8?q?funcionamento)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rgms/publication/XMLController.groovy | 21 +++---- grails-app/services/rgms/XMLService.groovy | 61 ++++++++++++++++--- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index bf9a422f..f4068b63 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -41,7 +41,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def tools = XMLService.createTools(xmlFile, user.name)*.obj - XMLService.save(tools) + XMLService.saveImportedPubsOfType(tools,"tools") } def uploadXMLBook() { @@ -55,8 +55,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def books = XMLService.createBooks(xmlFile, user.name)*.obj - XMLService.save(books) - println "salvou books!" + XMLService.saveImportedPubsOfType(books, "books") } //#if($researchLine) @@ -68,7 +67,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def researchLines = XMLService.createResearchLines(xmlFile, user.name)*.obj - XMLService.save(researchLines) + XMLService.saveImportedPubsOfType(researchLines, "researchLines") } //#end @@ -81,7 +80,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def researchProjects = XMLService.createResearchProjects(xmlFile, user.name)*.obj - XMLService.saveImportedResearchProjects(researchProjects) + XMLService.saveImportedPubsOfType(researchProjects, "researchProjects") } //#end @@ -96,7 +95,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def bookChapters = XMLService.createBooksChapters(xmlFile, user.name)*.obj - XMLService.save(bookChapters) + XMLService.saveImportedPubsOfType(bookChapters, "bookChapters") } def uploadXMLDissertacao() { @@ -110,7 +109,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def dissertation = XMLService.createDissertation(xmlFile, user.name).obj - XMLService.save(dissertation) + XMLService.saveImportedPubsOfType([dissertation], "dissertation") } def enviarConferenciaXML() { @@ -124,7 +123,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def conferences = XMLService.createConferencias(xmlFile, user.name)*.obj - XMLService.save(conferences) + XMLService.saveImportedPubsOfType(conferences, "conferences") } //#if($Orientation) @@ -139,7 +138,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def orientations = XMLService.createOrientations(xmlFile, user)*.obj - XMLService.saveImportedOrientations(orientations) + XMLService.saveImportedPubsOfType(orientations, "orientations") } //#end @@ -155,7 +154,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def journals = XMLService.createJournals(xmlFile, user.name)*.obj - XMLService.save(journals) + XMLService.saveImportedPubsOfType(journals, "journals") } //#end @@ -181,7 +180,7 @@ class XMLController { //importacao via outras telas (ainda precisa corrigir) else{ flash.message = message(code: msg) - redirect(controller: controller, action: "list", params: params) + redirect(controller: controller, action: "list") } } diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index b5ed15bc..85466404 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -825,21 +825,52 @@ class XMLService { def n = (importedKeys.size()-authorsTotal)/(keys.size()-1) } - private static getObject(name){ + private static getObject(name, properties){ switch(name){ - case "journals": return new Periodico() - case "tools": return new Ferramenta() - case "books": return new Book() - case "bookChapters": return new BookChapter() - case "conferences": return new Conferencia() + case "journals": + if(properties) return new Periodico(properties) + else return new Periodico() + case "tools": + if(properties) return new Ferramenta(properties) + else return new Ferramenta() + case "books": + if(properties) return new Book(properties) + else return new Book() + case "bookChapters": + if(properties) return new BookChapter(properties) + else return new BookChapter() + case "conferences": + if(properties) return new Conferencia(properties) + else return new Conferencia() + case "dissertation": + if(properties) return new Dissertacao(properties) + else return new Dissertacao() + case "thesis": + if(properties) return new Tese(properties) + else return new Tese() //#if($researchLine) - case "researchLines": return new ResearchLine() + case "researchLines": + if(properties) return new ResearchLine(properties) + else return new ResearchLine() //#end //#if($researchProject) - case "researchProjects": return new ResearchProject() + case "researchProjects": + def project = new ResearchProject() + if(properties){ + project.properties = properties + return project + } + else return project + //#end + //#if($funder) + case "funders": + if(properties) return new Funder(properties) + else return new Funder() //#end //#if($Orientation) - case "orientations": return new Orientation() + case "orientations": + if(properties) return new Orientation(properties) + else return new Orientation() //#end default: return null } @@ -847,7 +878,7 @@ class XMLService { //esse método deveria funcionar com ResearchProject, mas não funciona private static fullImportedPublication(keys, params, name, i){ - def pub = getObject(name) + def pub = getObject(name, null) keys.each{ k -> switch (k){ case "publicationDate": pub."$k" = XMLService.extractDate(params,name,i,k) @@ -1083,6 +1114,16 @@ class XMLService { } } + def saveImportedPubsOfType(pubs, type) { + def objects = [] + pubs?.each{ p -> + def obj = getObject(type, p) + objects += obj + } + save(objects) + return objects + } + //#if($researchProject) def saveImportedResearchProjects(researchProjects){ researchProjects?.each { rp -> From c131a69a71e3d19fe6ada92aa0064ca42ec85ab9 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sat, 26 Jul 2014 19:44:20 -0300 Subject: [PATCH 20/54] =?UTF-8?q?Refatora=C3=A7=C3=A3o:=20Renomea=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20m=C3=A9todo=20de=20savePublication=20para=20crea?= =?UTF-8?q?tePublication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/rgms/publication/XMLController.groovy | 4 ++-- test/functional/steps/TestDataAndOperations.groovy | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index f4068b63..7a43046b 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -20,11 +20,11 @@ class XMLController { def upload() { String flashMessage = 'default.xml.import.message' String controller = "Publication" - if (!XMLService.Import(savePublication, returnWithMessage, flashMessage, controller, request)) + if (!XMLService.Import(createPublication, returnWithMessage, flashMessage, controller, request)) return } - private savePublication = { + private createPublication = { Node xmlFile -> Member user = getCurrentUser() XMLService.createPublications(xmlFile, user) diff --git a/test/functional/steps/TestDataAndOperations.groovy b/test/functional/steps/TestDataAndOperations.groovy index 83225946..bc8ca4ff 100644 --- a/test/functional/steps/TestDataAndOperations.groovy +++ b/test/functional/steps/TestDataAndOperations.groovy @@ -2,14 +2,10 @@ package steps import org.apache.shiro.subject.Subject import org.apache.shiro.util.ThreadContext -import org.codehaus.groovy.tools.GroovyClass import rgms.authentication.User import rgms.member.* -import rgms.news.News -import rgms.news.NewsController import rgms.publication.* import org.apache.shiro.SecurityUtils -import steps.ThesisOrDissertationTestDataAndOperations class TestDataAndOperations { @@ -311,7 +307,7 @@ class TestDataAndOperations { def cont = new XMLController() def xml = new File(filename); def records = new XmlParser() - cont.savePublication(records.parse(xml)); + cont.createPublication(records.parse(xml)); cont.response.reset() } From 5ecef4bb02166a665a3ed8ca3f559a7aed6a5e09 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sat, 26 Jul 2014 19:48:46 -0300 Subject: [PATCH 21/54] =?UTF-8?q?Ajuste=20em=20label=20de=20bot=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/i18n/messages.properties | 2 +- grails-app/i18n/messages_pt_BR.properties | 2 +- grails-app/views/XML/home.gsp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/grails-app/i18n/messages.properties b/grails-app/i18n/messages.properties index 1ce3d8a2..609fee03 100644 --- a/grails-app/i18n/messages.properties +++ b/grails-app/i18n/messages.properties @@ -179,7 +179,7 @@ xml.importedPublicationTitle.label=Title xml.importedPublicationDate.label=Publication Date xml.importedPublicationAuthors.label=Authors xml.importStatus.label=Import Status -xml.checkAll.label=To Save +xml.checkAll.label=All xml.button.save.label=Save #if($Orientation) xml.importOrientation.label=Orientation Type: {0} diff --git a/grails-app/i18n/messages_pt_BR.properties b/grails-app/i18n/messages_pt_BR.properties index be825831..1835260a 100644 --- a/grails-app/i18n/messages_pt_BR.properties +++ b/grails-app/i18n/messages_pt_BR.properties @@ -238,7 +238,7 @@ xml.importedPublicationTitle.label=Título xml.importedPublicationDate.label=Data da Publicação xml.importedPublicationAuthors.label=Autores xml.importStatus.label=Estado da Importação -xml.checkAll.label=Para salvar +xml.checkAll.label=Todos xml.button.save.label=Salvar #if($Orientation) xml.importOrientation.label=Tipo da Orientação: {0} diff --git a/grails-app/views/XML/home.gsp b/grails-app/views/XML/home.gsp index 92a04700..3ccd9c83 100644 --- a/grails-app/views/XML/home.gsp +++ b/grails-app/views/XML/home.gsp @@ -32,7 +32,7 @@
Publication TypeTitlePublication DateAuthorsImport Status
Journal ${journalInstance?.obj?.title} ${journalInstance?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} @@ -75,7 +76,7 @@
Tool ${toolInstance?.obj?.title} ${toolInstance?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} @@ -100,7 +101,7 @@
Book ${bookInstance?.obj?.title} ${bookInstance?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} @@ -122,7 +123,7 @@
Book Chapter ${bookChapterInstance?.obj?.title} ${bookChapterInstance?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} @@ -145,7 +146,7 @@
Dissertation ${publications?.masterDissertation?.obj?.title} ${publications?.masterDissertation?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} ${publications?.masterDissertation?.obj?.authors?.iterator()[0]}
Thesis ${publications?.thesis?.obj?.title} ${publications?.thesis?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} ${publications?.thesis?.obj?.authors?.iterator()[0]}
Conference ${conferenceInstance?.obj?.title} ${conferenceInstance?.obj?.publicationDate?.toCalendar().get(Calendar.YEAR)} @@ -200,7 +201,7 @@
Research Line ${researchLineInstance?.obj?.name}
Research Project ${projectInstance?.obj?.projectName} ${projectInstance?.obj?.startYear} @@ -254,7 +255,8 @@
Orientação de ${orientationInstance?.obj?.tipo} ${orientationInstance?.obj?.tituloTese} ${orientationInstance?.obj?.anoPublicacao} ${orientationInstance?.obj?.orientando}
- + From 3a36120d61c1beb0e34b1fe777d3aa1805d22b1e Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sun, 27 Jul 2014 09:43:47 -0300 Subject: [PATCH 22/54] =?UTF-8?q?Corre=C3=A7=C3=A3o:=20Exclus=C3=A3o=20de?= =?UTF-8?q?=20etapa=20manual=20para=20execu=C3=A7=C3=A3o=20de=20testes=20a?= =?UTF-8?q?trav=C3=A9s=20da=20inclus=C3=A3o=20de=20m=C3=A9todo=20para=20au?= =?UTF-8?q?tomaticamente=20configurar=20o=20usu=C3=A1rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/XMLImport.feature | 1 - test/cucumber/steps/XMLImportSteps.groovy | 15 ++++++------ .../steps/TestDataAndOperations.groovy | 12 ++++++++++ .../XMLImportTestDataAndOperations.groovy | 24 +++++++++++++++++++ 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/test/cucumber/XMLImport.feature b/test/cucumber/XMLImport.feature index 3c80885f..ca842fe9 100644 --- a/test/cucumber/XMLImport.feature +++ b/test/cucumber/XMLImport.feature @@ -1,4 +1,3 @@ -#Para executar os testes, alterar o nome do usuário admin para "Paulo Henrique Monteiro Borba" em BootStrap.groovy @i9n Feature: XMLImport As a member of a research group diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index 4a9f0348..b1408a1c 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -24,14 +24,15 @@ import steps.TestDataAndOperations XMLController xmlController int publicationsTotal -String authorName = User.findByUsername('admin')?.author?.name +String authorName = XMLImportTestDataAndOperations.getUser() +String user = "paulo" //#if($ResearchProject) int researchProjectsTotal //#end Given(~'^the system has some publications stored$') { -> - TestDataAndOperations.loginController(this) + TestDataAndOperations.loginController(this,user) XMLImportTestDataAndOperations.initializePublicationDB() publicationsTotal = 4 assert Publication.findAll().size() == publicationsTotal @@ -85,7 +86,7 @@ Then(~'^the system outputs a list of imported publications that contains the jou } Given(~'^the system has a journal article entitled "([^"]*)" with journal "([^"]*)" authored by me, among several publications$'){ pubName, journalName -> - TestDataAndOperations.loginController(this) + TestDataAndOperations.loginController(this, user) XMLImportTestDataAndOperations.initializePublicationDB() XMLImportTestDataAndOperations.addJournalPublication(pubName, journalName) publicationsTotal = 5 @@ -133,7 +134,7 @@ Then(~'^the system outputs a list of imported publications that does not contain } Given(~'^the system has a journal article entitled "([^"]*)" with journal "([^"]*)" and pages "([^"]*)" that is authored by me, among several publications$'){ pubName, journalName, pages -> - TestDataAndOperations.loginController(this) + TestDataAndOperations.loginController(this, user) XMLImportTestDataAndOperations.initializePublicationDB() XMLImportTestDataAndOperations.addJournalPublication(pubName, journalName) publicationsTotal = 5 @@ -168,7 +169,7 @@ Then(~'^the system outputs an error message$') { -> //#if ($ResearchProject) Given(~'^the system has some research projects stored$'){ -> - TestDataAndOperations.loginController(this) + TestDataAndOperations.loginController(this, user) XMLImportTestDataAndOperations.initializeResearchProjectDB() researchProjectsTotal = 2 assert ResearchProject.findAll().size() == researchProjectsTotal @@ -205,7 +206,7 @@ Then(~'^the system outputs a list of imported research projects that contains th } Given(~'^the system has a research project named as "([^"]*)", among several research projects$'){ projectName -> - TestDataAndOperations.loginController(this) + TestDataAndOperations.loginController(this, user) XMLImportTestDataAndOperations.initializeResearchProjectDB() researchProjectsTotal = 2 def project = ResearchProject.findByProjectName(projectName) @@ -233,7 +234,7 @@ Then(~'^the system outputs a list of imported research projects that does not co Given(~'^the system has a research project named as "([^"]*)" with status "([^"]*)", among several research projects$'){ projectName, projectStatus -> - TestDataAndOperations.loginController(this) + TestDataAndOperations.loginController(this, user) XMLImportTestDataAndOperations.initializeResearchProjectDB() researchProjectsTotal = 2 def project = ResearchProject.findByProjectNameAndStatus(projectName, projectStatus) diff --git a/test/functional/steps/TestDataAndOperations.groovy b/test/functional/steps/TestDataAndOperations.groovy index bc8ca4ff..789aedfb 100644 --- a/test/functional/steps/TestDataAndOperations.groovy +++ b/test/functional/steps/TestDataAndOperations.groovy @@ -323,6 +323,18 @@ class TestDataAndOperations { SecurityUtils.metaClass.static.getSubject = { subject } } + static public void loginController(cla, username){ + def registry = GroovySystem.metaClassRegistry + cla.oldMetaClass = registry.getMetaClass(SecurityUtils) + registry.removeMetaClass(SecurityUtils) + def subject = [getPrincipal: { username }, + isAuthenticated: { true } + ]as Subject + ThreadContext.put(ThreadContext.SECURITY_MANAGER_KEY, + [getSubject: { subject } as SecurityManager]) + SecurityUtils.metaClass.static.getSubject = { subject } + } + static public void logoutController(cla){ GroovySystem.metaClassRegistry.setMetaClass(SecurityUtils, cla.oldMetaClass) } diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index b8fa1ac8..fbe6cb18 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -1,9 +1,12 @@ package steps +import org.apache.shiro.crypto.hash.Sha256Hash import org.springframework.mock.web.MockMultipartFile import org.springframework.mock.web.MockMultipartHttpServletRequest import rgms.XMLService +import rgms.authentication.Role import rgms.authentication.User +import rgms.member.Member import rgms.member.Orientation import rgms.publication.Conferencia import rgms.publication.Periodico @@ -225,5 +228,26 @@ class XMLImportTestDataAndOperations { } //#end + static getUser(){ + def adminRole = Role.findByName("Administrator") + if (!adminRole) { + adminRole = new Role(name: 'Administrator') + adminRole.addToPermissions("*:*") + adminRole.save() + } + + def user = new User(username: 'paulo', passwordHash: new Sha256Hash("paulo").toHex(),enabled: true) + def member = new Member(name: "Paulo Henrique Monteiro Borba",email: "phmb@cin.ufpe.br", status: "Professor", + university: "UFPE") + + adminRole.addToUsers(user) + adminRole.save() + + member.save() + + user.author = member + user.save() + return User.findByUsername('paulo')?.author?.name + } } From 43e4e7fee5f1726f64b88270ef2f3b26437457c3 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sun, 27 Jul 2014 09:48:01 -0300 Subject: [PATCH 23/54] =?UTF-8?q?Exclus=C3=A3o=20de=20chamadas=20de=20prin?= =?UTF-8?q?tln?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/services/rgms/XMLService.groovy | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index 85466404..ecc9eee2 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -1043,58 +1043,40 @@ class XMLService { def saveImportedPublications(params, user) { //#if($Article) def journals = extractImportedPublications("journals", params, XMLService.JOURNAL_KEYS) - println "total de journals importados = "+journals.size() save(journals) - println "salvou journals!" //#end def tools = extractImportedPublications("tools", params, TOOL_KEYS) - println "total de tools importados = "+tools.size() save(tools) - println "salvou tools!" def books = extractImportedPublications("books", params, BOOK_KEYS) - println "total de books importados = "+books.size() save(books) - println "salvou books!" def bookChapters = extractImportedPublications("bookChapters", params, BOOK_CHAPTER_KEYS) - println "total de bookChapters importados = "+bookChapters.size() save(bookChapters) - println "salvou bookChapters!" def dissertation = extractDissertation(new Dissertacao(), "masterDissertation", params, DISSERTATION_KEYS) if (dissertation) save([dissertation]) - println "salvou dissertation!" def thesis = extractDissertation(new Tese(), "thesis", params, DISSERTATION_KEYS) if (thesis) save([thesis]) - println "salvou thesis!" def conferences = extractImportedPublications("conferences", params, CONFERENCE_KEYS) - println "total de conferences importados = "+conferences.size() save(conferences) - println "salvou conferences!" //#if($researchLine) def researchLines = extractResearchLines("researchLines", params, RESEARCH_LINE_KEYS) - println "total de researchLines importados = "+researchLines.size() save(researchLines) - println "salvou researchLines!" //#end //#if($researchProject) def researchProjects = extractResearchProjects("researchProjects", params, RESEARCH_PROJECT_KEYS) - println "total de researchProjects importados = "+researchProjects.size() saveImportedResearchProjects(researchProjects) - println "salvou researchProjects!" //#end //#if($Orientation) def orientations = extractOrientations("orientations", params, ORIENTATION_KEYS, user) - println "total de orientations importados = "+orientations.size() saveImportedOrientations(orientations) - println "salvou orientations!" //#end return 'default.xml.save.message' From fcff6c837f0da02c7bb0cf55d020be48e40d88bb Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sun, 27 Jul 2014 09:52:41 -0300 Subject: [PATCH 24/54] =?UTF-8?q?Refatora=C3=A7=C3=A3o:=20Inclus=C3=A3o=20?= =?UTF-8?q?da=20feature=20Funder=20e=20exclus=C3=A3o=20de=20altera=C3=A7?= =?UTF-8?q?=C3=B5es=20indevidamente=20realizadas=20em=20atividades=20passa?= =?UTF-8?q?das.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProductGeneration/featureModel.xml | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/ProductGeneration/featureModel.xml b/ProductGeneration/featureModel.xml index fd1a8a9e..e41f455c 100644 --- a/ProductGeneration/featureModel.xml +++ b/ProductGeneration/featureModel.xml @@ -39,21 +39,14 @@ - + - - - - - - - - - - - - - - + + + + + + + \ No newline at end of file From 92fbef3867f08cd2c2205d558d28bad575e72091 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sun, 27 Jul 2014 10:00:28 -0300 Subject: [PATCH 25/54] =?UTF-8?q?Inclus=C3=A3o=20da=20feature=20Funder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProductGeneration/featureModel.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ProductGeneration/featureModel.xml b/ProductGeneration/featureModel.xml index e41f455c..3a379e8d 100644 --- a/ProductGeneration/featureModel.xml +++ b/ProductGeneration/featureModel.xml @@ -41,12 +41,12 @@ - - - - + + + + \ No newline at end of file From f97e7f28f289fd1ffbfd4aa316467ec412706f7f Mon Sep 17 00:00:00 2001 From: thaisabr Date: Sun, 27 Jul 2014 10:16:06 -0300 Subject: [PATCH 26/54] =?UTF-8?q?Identifica=C3=A7=C3=A3o=20de=20artefatos?= =?UTF-8?q?=20relacionados=20=C3=A0=20feature=20XMLImport.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProductGeneration/ck.xml | 35 ++++++++++++++++++++++++++++ ProductGeneration/componentModel.txt | 7 ++++++ 2 files changed, 42 insertions(+) diff --git a/ProductGeneration/ck.xml b/ProductGeneration/ck.xml index be484695..a47fd526 100644 --- a/ProductGeneration/ck.xml +++ b/ProductGeneration/ck.xml @@ -1237,4 +1237,39 @@ test/cucumber/XMLImport.feature + + XMLImport + + selectAndMoveComponent + XMLImportSteps,test/cucumber/steps/XMLImportSteps.groovy + + + + XMLImport + + preprocessFiles + test/cucumber/steps/XMLImportSteps.groovy + + + + XMLImport + + selectAndMoveComponent + XMLImportTestDataAndOperations,test/functional/steps/XMLImportTestDataAndOperations.groovy + + + + XMLImport + + preprocessFiles + test/functional/steps/XMLImportTestDataAndOperations.groovy + + + + XMLImport + + selectAndMoveComponent + XMLImportPage,test/functional/pages/XMLImportPage.groovy + + diff --git a/ProductGeneration/componentModel.txt b/ProductGeneration/componentModel.txt index 18b45958..ab8ab79e 100644 --- a/ProductGeneration/componentModel.txt +++ b/ProductGeneration/componentModel.txt @@ -221,3 +221,10 @@ ArticleEditPage => test/functional/pages/ArticleEditPage.groovy; ArticleShowPage => test/functional/pages/ArticleShowPage.groovy; ArticlePage => test/functional/pages/ArticlesPage.groovy; UserRegisterPage => test/functional/pages/UserRegisterPage.groovy; + +XMLController => grails-app/controllers/rgms/publication/XMLController.groovy; +XMLService => grails-app/services/rgms/XMLService.groovy; +XMLImportTest => test/cucumber/XMLImport.feature; +XMLImportSteps => test/cucumber/steps/XMLImportSteps.groovy; +XMLImportTestDataAndOperations => test/functional/steps/XMLImportTestDataAndOperations.groovy; +XMLImportPage => test/functional/pages/XMLImportPage.groovy; From 0842010eb7cae0ae25e0caa146a1bf29546bab4b Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Wed, 30 Jul 2014 13:37:51 -0300 Subject: [PATCH 27/54] =?UTF-8?q?Atividade=205=20-=20Corre=C3=A7=C3=A3o=20?= =?UTF-8?q?relacionadas=20a=20duplica=C3=A7=C3=A3o=20e=20modularidade?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../publication/PublicationController.groovy | 19 +++---- .../publication/ResearchLineController.groovy | 50 ++++++++----------- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/grails-app/controllers/rgms/publication/PublicationController.groovy b/grails-app/controllers/rgms/publication/PublicationController.groovy index 308a8f9a..2c2d8b6a 100644 --- a/grails-app/controllers/rgms/publication/PublicationController.groovy +++ b/grails-app/controllers/rgms/publication/PublicationController.groovy @@ -163,21 +163,18 @@ class PublicationController { //#end def static checkTypeFile(file){ - if(!file.hasProperty(".xml")){ - return false - } - return true + file.hasProperty(".xml") } def statusChanged(def lista){ def sizeList = Publication.findAll() - def inicialSize = sizeList.size() - def finalSize = lista.size() - if((lista.empty) || (inicialSize == finalSize) ){ - return false - }else if (inicialSize < finalSize){ - return true + def sizeI = sizeList.size() + def sizeF = lista.size() + + def resultado = Math.max(sizeI, sizeF) + if(sizeF.compareTo(resultado)){ + return true } - return false + return false } } diff --git a/grails-app/controllers/rgms/publication/ResearchLineController.groovy b/grails-app/controllers/rgms/publication/ResearchLineController.groovy index 610cf75d..a79c56a2 100644 --- a/grails-app/controllers/rgms/publication/ResearchLineController.groovy +++ b/grails-app/controllers/rgms/publication/ResearchLineController.groovy @@ -60,8 +60,6 @@ class ResearchLineController { [researchLineInstanceList: lista] } - - } //#end @@ -243,32 +241,29 @@ class ResearchLineController { HashMap lista = new HashMap() for(researchline in ResearchLine.getAll()) { - if(!researchline.getDescription().equals("stable")){ + if(!researchline.getDescription().equals("stable")) { researchline.setDescription("stable") - lista.put(researchline.getName(),researchline.getDescription()) - }else{ - lista.put(researchline.getName(),researchline.getDescription()) } - + lista.put(researchline.getName(),researchline.getDescription()) } [researchLineInstanceList: lista] } - def findByActor(def member){ + def findByActor(member){ ArrayList lista = new ArrayList() Member actor = new Member() for(research in ResearchLine.getAll()) { - for(members in research.getMembers()){ - if(member.equals(members.getName())){ - actor.getId() + for(currentMember in research.getMembers()){ + if(member.equals(currentMember.getName())){ + actor = currentMember } } } - return actor + return actor.getId() } - def findResearchByActor(def member,def research){ + def findAllResearchByMember(member, research){ HashMap listagem = new HashMap() for(researchL in ResearchLine.getAll()){ if((researchL.equals(research)) && (researchL.getMembers().contains(member))){ @@ -280,29 +275,24 @@ class ResearchLineController { def checkIfResearchLineExists(researchName, list){ List listCheck = list - for(research in list){ - if((!listCheck.isEmpty()) && (research.equals(researchName))){ - return true - } - } - return false - } - - def checkIfResearchLineNoExists(def researchName){ - ResearchLine line = ResearchLine.findByName(researchName) - line == null + for (research in list) { + if (research.equals(researchName)) { + return true + } + } + return false } def statusChanged(def lista){ def sizeList = findAllResearchLine() - def inicialSize = sizeList.size() - def finalSize = lista.size() - if((lista.empty) || (inicialSize == finalSize) ){ - return false - }else if (inicialSize < finalSize){ + def sizeI = sizeList.size() + def sizeF = lista.size() + + def resultado = Math.max(sizeI, sizeF) + if(sizeF.compareTo(resultado)){ return true } - return false + return false } def checkSavedResearchByDescription(nameOfResearch, status){ From 718540e2faf4876ee26df5ae77bedbcd3b5db6e1 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Thu, 31 Jul 2014 12:10:35 -0300 Subject: [PATCH 28/54] =?UTF-8?q?Altera=C3=A7=C3=B5es=20e=20ajustes=20deco?= =?UTF-8?q?rrentes=20de=20atualiza=C3=A7=C3=B5es=20no=20reposit=C3=B3rio?= =?UTF-8?q?=20central.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/XMLImportSteps.groovy | 6 +++--- .../steps/ArticleTestDataAndOperations.groovy | 14 ++++++++++---- .../steps/XMLImportTestDataAndOperations.groovy | 4 ++-- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index b1408a1c..60bd4a05 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -66,15 +66,15 @@ Then(~'^the previously stored publications do not change$'){ -> def conf2 = Conferencia.findByTitle(ConferenciaTestDataAndOperations.conferencias[1].title) assert ConferenciaTestDataAndOperations.conferenciaCompatibleTo(conf2, conf2.title) - def journal1 = Periodico.findByTitle(ArticleTestDataAndOperations.articles[0].title) + def journal1 = Periodico.findByTitle(ArticleTestDataAndOperations.articles[4].title) assert ArticleTestDataAndOperations.compatibleTo(journal1, journal1.title) - def journal2 = Periodico.findByTitle(ArticleTestDataAndOperations.articles[1].title) + def journal2 = Periodico.findByTitle(ArticleTestDataAndOperations.articles[5].title) assert ArticleTestDataAndOperations.compatibleTo(journal2, journal2.title) //caso do sistema já ter um dado periodico if(publicationsTotal == 5){ - def journal3 = Periodico.findByTitle(ArticleTestDataAndOperations.articles[2].title) + def journal3 = Periodico.findByTitle(ArticleTestDataAndOperations.articles[6].title) assert ArticleTestDataAndOperations.compatibleTo(journal3, journal3.title) } } diff --git a/test/functional/steps/ArticleTestDataAndOperations.groovy b/test/functional/steps/ArticleTestDataAndOperations.groovy index 59ff1e51..ffa95554 100644 --- a/test/functional/steps/ArticleTestDataAndOperations.groovy +++ b/test/functional/steps/ArticleTestDataAndOperations.groovy @@ -16,15 +16,21 @@ class ArticleTestDataAndOperations { static articles = [ [journal: "Theoretical Computer Science", volume: 455, number: 1, pages: "2-30", title: "A theory of software product line refinement", - publicationDate: (new Date("12 October 2012")), members:[] as Set], + publicationDate: (new Date("12 October 2012"))], [journal: "Science of Computer Programming", volume: 455, pages: "2-30", title: "Algebraic reasoning for object-oriented programming", - publicationDate: (new Date("12 October 2012")), members:[] as Set], + publicationDate: (new Date("12 October 2012"))], [journal: "Science of Computer Programming", volume: 300, pages: "50-70", title: "Modularity analysis of use case implementations", - publicationDate: (new Date("12 October 2012")), members:[] as Set], + publicationDate: (new Date("12 October 2012"))], [journal: "Science of Computer Programming", volume: 255, pages: "30-50", - filename: "TCS-01.pdf", publicationDate: (new Date("12 October 2012")), members:[] as Set], + filename: "TCS-01.pdf", publicationDate: (new Date("12 October 2012"))], + [journal: "Eletronic Notes On Theoretical Computer Science", volume: 14, number: 1, pages: "10-25", + title: "Systematic development of concurrent object-oriented programs", + publicationDate: (new Date("10 January 1998")), members:[] as Set], + [journal: "ACM Sigplan Notices", volume: 37, number: 11, pages: "174-190", + title: "Implementing distribution and persistence aspects with AspectJ", + publicationDate: (new Date("12 October 2012")), members:[] as Set], [journal: "Eletronic Notes In Theoretical Computer Science", volume: 130, number: 1, pages: "3-21", title: "An Abstract Equivalence Notion for Object Models", publicationDate: (new Date("12 October 2005")), members:[] as Set, diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index fbe6cb18..71d80bd4 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -133,9 +133,9 @@ class XMLImportTestDataAndOperations { conf.save(flush: true) //salvar 2 artigos de periodico - Periodico journal = new Periodico(ArticleTestDataAndOperations.articles[0]) + Periodico journal = new Periodico(ArticleTestDataAndOperations.articles[4]) journal.save(flush: true) - journal = new Periodico(ArticleTestDataAndOperations.articles[1]) + journal = new Periodico(ArticleTestDataAndOperations.articles[5]) journal.save(flush: true) } From 0e471d80282b13fde2941c18d223a5854d391ed9 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Thu, 31 Jul 2014 13:30:43 -0300 Subject: [PATCH 29/54] =?UTF-8?q?Inclus=C3=A3o=20de=20mensagem=20de=20aler?= =?UTF-8?q?ta=20no=20caso=20de=20n=C3=A3o=20haver=20publica=C3=A7=C3=B5es?= =?UTF-8?q?=20para=20serem=20importadas=20a=20partir=20do=20arquivo=20sele?= =?UTF-8?q?cionado.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/controllers/rgms/publication/XMLController.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index 7a43046b..0922ab7d 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -109,7 +109,7 @@ class XMLController { Node xmlFile -> Member user = getCurrentUser() def dissertation = XMLService.createDissertation(xmlFile, user.name).obj - XMLService.saveImportedPubsOfType([dissertation], "dissertation") + XMLService.saveImportedPubsOfType([dissertation], "masterDissertation") } def enviarConferenciaXML() { @@ -174,7 +174,8 @@ class XMLController { private returnWithMessage = { String msg, String controller, publications -> //importacao via opcao XMLImport no menu da tela inicial do sistema if (controller == "Publication"){ - request.message = message(code: msg) + if(publications.isEmpty()) request.message = message(code: "xml.import.empty.message") + else request.message = message(code: msg) render(view:"home", model:[publications:publications]) } //importacao via outras telas (ainda precisa corrigir) From 0dbf706230839881f74203b5fe8364c61756318e Mon Sep 17 00:00:00 2001 From: thaisabr Date: Thu, 31 Jul 2014 13:31:47 -0300 Subject: [PATCH 30/54] =?UTF-8?q?Inclus=C3=A3o=20de=20mensagem=20de=20XMLI?= =?UTF-8?q?mport.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/i18n/messages.properties | 1 + grails-app/i18n/messages_pt_BR.properties | 1 + 2 files changed, 2 insertions(+) diff --git a/grails-app/i18n/messages.properties b/grails-app/i18n/messages.properties index 609fee03..9cf03bb4 100644 --- a/grails-app/i18n/messages.properties +++ b/grails-app/i18n/messages.properties @@ -173,6 +173,7 @@ default.xml.unknownerror.message=An unknown error occurred. Contact the administ default.xml.saveerror.message=An error ocurred when saving the imported publications. The operation was canceled. default.xml.save.message=The imported publications were saved! default.xml.import.message=The publications were imported! +xml.import.empty.message=There is no publication to import from the selected file. Possible reasons: (i) the file does not contain any publication, (ii) the publications aren't from your authorship or (iii) the system already contains the publications. xml.label=XMLImport xml.importedPublicationType.label=Publication Type xml.importedPublicationTitle.label=Title diff --git a/grails-app/i18n/messages_pt_BR.properties b/grails-app/i18n/messages_pt_BR.properties index 1835260a..ca9dc5ed 100644 --- a/grails-app/i18n/messages_pt_BR.properties +++ b/grails-app/i18n/messages_pt_BR.properties @@ -232,6 +232,7 @@ default.xml.unknownerror.message=Ocorreu um erro desconhecido. Entre em contato default.xml.saveerror.message=Ocorreu um erro ao salvar as publicações importadas. A operação foi cancelada. default.xml.save.message=As publicações importadas foram salvas! default.xml.import.message=As publicações foram importadas! +xml.import.empty.message=Não há publicações para importar a partir do arquivo selecionado. Razões possíveis: (i) o arquivo não contém nenhuma publicação, (ii) as publicações não são de sua autoria ou (iii) o sistema já contém as publicações. xml.label=XMLImport xml.importedPublicationType.label=Tipo da Publicação xml.importedPublicationTitle.label=Título From 785900b38bd04902295d9ab722a51d04f4244c5d Mon Sep 17 00:00:00 2001 From: thaisabr Date: Thu, 31 Jul 2014 13:36:58 -0300 Subject: [PATCH 31/54] =?UTF-8?q?Refatora=C3=A7=C3=A3o:=20Exclus=C3=A3o=20?= =?UTF-8?q?de=20m=C3=A9todo=20redundante=20e=20limpeza=20de=20c=C3=B3digo?= =?UTF-8?q?=20simples.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/services/rgms/XMLService.groovy | 173 +++++++++------------ 1 file changed, 77 insertions(+), 96 deletions(-) diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index ecc9eee2..5c7deac9 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -18,21 +18,21 @@ class XMLService { public static final String PUB_STATUS_DUPLICATED = "duplicated" //#if($Article) - public static final JOURNAL_KEYS = ["title", "publicationDate", "authors", "journal", "volume", "number", "pages"] + public static final JOURNAL_KEYS = ["id", "title", "publicationDate", "authors", "journal", "volume", "number", "pages"] //#end - public static final TOOL_KEYS = ["title", "publicationDate", "authors", "description"] - public static final BOOK_KEYS = ["title", "publicationDate", "authors", "publisher", "volume", "pages"] - public static final BOOK_CHAPTER_KEYS = ["title", "publicationDate", "authors", "publisher"] - public static final DISSERTATION_KEYS = ["title", "publicationDate", "authors", "school"] - public static final CONFERENCE_KEYS = ["title", "publicationDate", "authors", "booktitle", "pages"] + public static final TOOL_KEYS = ["id", "title", "publicationDate", "authors", "description"] + public static final BOOK_KEYS = ["id", "title", "publicationDate", "authors", "publisher", "volume", "pages"] + public static final BOOK_CHAPTER_KEYS = ["id", "title", "publicationDate", "authors", "publisher"] + public static final DISSERTATION_KEYS = ["id", "title", "publicationDate", "authors", "school"] + public static final CONFERENCE_KEYS = ["id", "title", "publicationDate", "authors", "booktitle", "pages"] //#if($researchLine) - public static final RESEARCH_LINE_KEYS = ["name","description"] + public static final RESEARCH_LINE_KEYS = ["id", "name","description"] //#end //#if($researchProject) - public static final RESEARCH_PROJECT_KEYS = ["projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders"] + public static final RESEARCH_PROJECT_KEYS = ["id", "projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders"] //#end //#if($Orientation) - public static final ORIENTATION_KEYS = ["tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"] + public static final ORIENTATION_KEYS = ["id", "tipo", "orientando", "tituloTese", "anoPublicacao", "instituicao", "curso"] //#end /* @@ -59,7 +59,7 @@ class XMLService { flashMessage = 'default.xml.structure.message' errorFound = true } - catch (Exception ex) { + catch (Exception) { flashMessage = 'default.xml.unknownerror.message' errorFound = true } @@ -121,8 +121,8 @@ class XMLService { for (Node currentNode : softwares) { def newTool = createNewTool(currentNode, authorName) def result = checkToolStatus(newTool) - if (result?.status && result?.status != XMLService.PUB_STATUS_DUPLICATED) { - def obj = newTool.properties.findAll{it.key in XMLService.TOOL_KEYS} + if (result?.status && result?.status != PUB_STATUS_DUPLICATED) { + def obj = newTool.properties.findAll{it.key in TOOL_KEYS} tools += [obj: obj, status:result.status, id:result.id] } } @@ -152,7 +152,7 @@ class XMLService { static checkToolStatus(Ferramenta tool){ if(!tool) return null - def status = XMLService.PUB_STATUS_STABLE + def status = PUB_STATUS_STABLE def toolDB = Ferramenta.findByTitle(tool.title) if(toolDB) status = checkPublicationStatus(toolDB, tool) return [status:status, id:toolDB?.id] @@ -167,7 +167,7 @@ class XMLService { def newBook = createNewBook(currentNode, i, authorName) def result = checkBookStatus(newBook) if(result?.status && result?.status != PUB_STATUS_DUPLICATED) { - def obj = newBook.properties.findAll{it.key in XMLService.BOOK_KEYS} + def obj = newBook.properties.findAll{it.key in BOOK_KEYS} booksList += [obj: obj, status:result.status, id:result.id] } ++i @@ -211,7 +211,7 @@ class XMLService { def newBookChapter = createNewBookChapter(publishedBookChapters, i, authorName) def result = checkBookChapterStatus(newBookChapter) if(result?.status && result?.status != PUB_STATUS_DUPLICATED) { - def obj = newBookChapter.properties.findAll{it.key in XMLService.BOOK_CHAPTER_KEYS} + def obj = newBookChapter.properties.findAll{it.key in BOOK_CHAPTER_KEYS} bookChaptersList += [obj: obj, status:result.status, id:result.id] } } @@ -262,12 +262,12 @@ class XMLService { def status = checkDissertationOrThesisStatus(dissertationDB, newDissertation) if(status == PUB_STATUS_DUPLICATED) return null - def obj = newDissertation.properties.findAll{it.key in XMLService.DISSERTATION_KEYS} + def obj = newDissertation.properties.findAll{it.key in DISSERTATION_KEYS} return [obj: obj, status:status, id:dissertationDB?.id] } private static createNewDissertation(Node xmlFile, String authorName){ - def mestrado = xmlFile.depthFirst().find{ it.name() == 'MESTRADO' } + def mestrado = xmlFile?.depthFirst()?.find{ it.name() == 'MESTRADO' } if(!mestrado) return null String author = xmlFile.depthFirst().find{it.name() == 'DADOS-GERAIS'}.'@NOME-COMPLETO' @@ -298,7 +298,7 @@ class XMLService { def status = checkDissertationOrThesisStatus(thesisDB, newThesis) if(status == PUB_STATUS_DUPLICATED) return null - def obj = newThesis.properties.findAll{it.key in XMLService.DISSERTATION_KEYS} + def obj = newThesis.properties.findAll{it.key in DISSERTATION_KEYS} return [obj: obj, status:status, id:thesisDB?.id] } @@ -333,7 +333,7 @@ class XMLService { def result = checkConferenceStatus(newConference) if(result?.status && result?.status != PUB_STATUS_DUPLICATED){ - def obj = newConference.properties.findAll{it.key in XMLService.CONFERENCE_KEYS} + def obj = newConference.properties.findAll{it.key in CONFERENCE_KEYS} conferences += [obj: obj, status:result.status, id:result.id] } } @@ -384,8 +384,8 @@ class XMLService { def newJournal = createNewJournal(publishedArticles, i, authorName) def result = checkJournalStatus(newJournal) - if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newJournal.properties.findAll{it.key in XMLService.JOURNAL_KEYS} + if(result?.status && result?.status!=PUB_STATUS_DUPLICATED) { + def obj = newJournal.properties.findAll{it.key in JOURNAL_KEYS} journals += [obj: obj, status:result.status, id:result.id] } } @@ -460,12 +460,12 @@ class XMLService { } private static checkPublicationStatus(Publication pubDB, Publication pub){ - def status = XMLService.PUB_STATUS_DUPLICATED + def status = PUB_STATUS_DUPLICATED def missingPropertiesDB = pubDB.properties.findAll{it.key != 'id' && !it.value}.keySet() def missingProperties = pub.properties.findAll{it.key != 'id' && !it.value}.keySet() if(missingPropertiesDB != missingProperties){ - status = XMLService.PUB_STATUS_TO_UPDATE + status = PUB_STATUS_TO_UPDATE } def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) @@ -474,13 +474,13 @@ class XMLService { def detailsProperties = pub.properties.findAll{!(it.key in missingPropertiesTotal)} def details = detailsProperties.findAll{it.key!='id' && it.key!='publicationDate'} if(detailsDB != details){ - status = XMLService.PUB_STATUS_CONFLICTED + status = PUB_STATUS_CONFLICTED } - def date1 = pubDB.publicationDate?.toCalendar().get(Calendar.YEAR) - def date2 = pub.publicationDate?.toCalendar().get(Calendar.YEAR) + def date1 = pubDB?.publicationDate?.toCalendar()?.get(Calendar.YEAR) + def date2 = pub?.publicationDate?.toCalendar()?.get(Calendar.YEAR) if(date1 && date2 && date1!=date2){ - status = XMLService.PUB_STATUS_CONFLICTED + status = PUB_STATUS_CONFLICTED } return status @@ -519,7 +519,7 @@ class XMLService { if(author != user.name) return null def orientations = [] - Node completedOrientationNode = xmlFile.depthFirst().find{ it.name() == 'ORIENTACOES-CONCLUIDAS' } + def completedOrientationNode = xmlFile.depthFirst().find{ it.name() == 'ORIENTACOES-CONCLUIDAS' } orientations = createMasterOrientations(orientations, completedOrientationNode, user) orientations = createThesisOrientations(orientations, completedOrientationNode, user) orientations = createUndergraduateResearch(orientations, completedOrientationNode, user) @@ -532,8 +532,8 @@ class XMLService { def newOrientation = fillOrientationData(orientation, user, "Mestrado") def result = checkOrientationStatus(newOrientation, user) - if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newOrientation.properties.findAll{it.key in XMLService.ORIENTATION_KEYS} + if(result?.status && result?.status!=PUB_STATUS_DUPLICATED) { + def obj = newOrientation.properties.findAll{it.key in ORIENTATION_KEYS} orientations += ["obj": obj, status:result.status, id:result.id] } } @@ -546,8 +546,8 @@ class XMLService { def newOrientation = fillOrientationData(orientation, user, "Doutorado") def result = checkOrientationStatus(newOrientation, user) - if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newOrientation.properties.findAll{it.key in XMLService.ORIENTATION_KEYS} + if(result?.status && result?.status!=PUB_STATUS_DUPLICATED) { + def obj = newOrientation.properties.findAll{it.key in ORIENTATION_KEYS} orientations += ["obj": obj, status:result.status, id:result.id] } } @@ -555,16 +555,16 @@ class XMLService { } static private createUndergraduateResearch(orientations, completedOrientationNode, user){ - def undergraduateResearch = completedOrientationNode?.getAt("OUTRAS-ORIENTACOES-CONCLUIDAS").findAll{ - it.children().get(0).'@NATUREZA' == "INICIACAO_CIENTIFICA" + def undergraduateResearch = completedOrientationNode?.getAt("OUTRAS-ORIENTACOES-CONCLUIDAS")?.findAll{ + it.children()?.get(0)?.'@NATUREZA' == "INICIACAO_CIENTIFICA" } for(Node orientation: undergraduateResearch){ def newOrientation = fillOrientationData(orientation, user, "Iniciação Científica") def result = checkOrientationStatus(newOrientation, user) - if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newOrientation.properties.findAll{it.key in XMLService.ORIENTATION_KEYS} + if(result?.status && result?.status!=PUB_STATUS_DUPLICATED) { + def obj = newOrientation.properties.findAll{it.key in ORIENTATION_KEYS} orientations += [obj: obj, status:result.status, id:result.id] } } @@ -573,16 +573,16 @@ class XMLService { static checkOrientationStatus(Orientation orientation, Member user){ if(!orientation) return null - def status = XMLService.PUB_STATUS_STABLE + def status = PUB_STATUS_STABLE def orientationDB = Orientation.findByOrientadorAndTituloTese(user, orientation.tituloTese) if(orientationDB){ - status = XMLService.PUB_STATUS_DUPLICATED + status = PUB_STATUS_DUPLICATED def missingPropertiesDB = orientationDB.properties.findAll{it.key != 'id' && !it.value}.keySet() def missingProperties = orientation.properties.findAll{it.key != 'id' && !it.value}.keySet() if(missingPropertiesDB != missingProperties){ - status = XMLService.PUB_STATUS_TO_UPDATE + status = PUB_STATUS_TO_UPDATE } def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) @@ -591,7 +591,7 @@ class XMLService { def detailsProperties = orientation.properties.findAll{!(it.key in missingPropertiesTotal)} def details = detailsProperties.findAll{it.key!='id'} if(detailsDB != details){ - status = XMLService.PUB_STATUS_CONFLICTED + status = PUB_STATUS_CONFLICTED } } return [status:status, id:orientationDB?.id] @@ -626,8 +626,8 @@ class XMLService { def newResearchLine = createNewResearchLine(j) def result = checkResearchLineStatus(newResearchLine) - if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newResearchLine.properties.findAll{it.key in XMLService.RESEARCH_LINE_KEYS} + if(result?.status && result?.status!=PUB_STATUS_DUPLICATED) { + def obj = newResearchLine.properties.findAll{it.key in RESEARCH_LINE_KEYS} researchLinesList += [obj: obj, status:result.status, id:result.id] } } @@ -646,16 +646,16 @@ class XMLService { static checkResearchLineStatus(ResearchLine researchLine){ if(!researchLine) return null - def status = XMLService.PUB_STATUS_STABLE + def status = PUB_STATUS_STABLE def rlDB = ResearchLine.findByName(researchLine.name) if(rlDB){ - status = XMLService.PUB_STATUS_DUPLICATED + status = PUB_STATUS_DUPLICATED def missingPropertiesDB = rlDB.properties.findAll{it.key != 'id' && !it.value}.keySet() def missingProperties = researchLine.properties.findAll{it.key != 'id' && !it.value}.keySet() if(missingPropertiesDB != missingProperties){ - status = XMLService.PUB_STATUS_TO_UPDATE + status = PUB_STATUS_TO_UPDATE } def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) @@ -664,7 +664,7 @@ class XMLService { def detailsProperties = researchLine.properties.findAll{!(it.key in missingPropertiesTotal)} def details = detailsProperties.findAll{it.key!='id'} if(detailsDB != details){ - status = XMLService.PUB_STATUS_CONFLICTED + status = PUB_STATUS_CONFLICTED } } return [status:status, id:rlDB?.id] @@ -683,8 +683,8 @@ class XMLService { def newProject = createNewResearchProject(project) def result = checkResearchProjectStatus(newProject) - if(result?.status && result?.status!=XMLService.PUB_STATUS_DUPLICATED) { - def obj = newProject.properties.findAll{ it.key in XMLService.RESEARCH_PROJECT_KEYS } + if(result?.status && result?.status!=PUB_STATUS_DUPLICATED) { + def obj = newProject.properties.findAll{ it.key in RESEARCH_PROJECT_KEYS } researchProjectsList += [obj: obj, status:result.status, id:result.id] } } @@ -693,15 +693,15 @@ class XMLService { static checkResearchProjectStatus(ResearchProject researchProject){ if(!researchProject) return null - def status = XMLService.PUB_STATUS_STABLE + def status = PUB_STATUS_STABLE def researchProjectDB = ResearchProject.findByProjectName(researchProject.projectName) if(researchProjectDB){ - status = XMLService.PUB_STATUS_DUPLICATED + status = PUB_STATUS_DUPLICATED def missingPropertiesDB = researchProjectDB.properties.findAll{it.key != 'id' && !it.value}.keySet() def missingProperties = researchProject.properties.findAll{it.key != 'id' && !it.value}.keySet() if(missingPropertiesDB != missingProperties){ - status = XMLService.PUB_STATUS_TO_UPDATE + status = PUB_STATUS_TO_UPDATE } def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) @@ -710,7 +710,7 @@ class XMLService { def detailsProperties = researchProject.properties.findAll{!(it.key in missingPropertiesTotal)} def details = detailsProperties.findAll{it.key!='id'} if(detailsDB != details){ - status = XMLService.PUB_STATUS_CONFLICTED + status = PUB_STATUS_CONFLICTED } } @@ -842,7 +842,7 @@ class XMLService { case "conferences": if(properties) return new Conferencia(properties) else return new Conferencia() - case "dissertation": + case "masterDissertation": if(properties) return new Dissertacao(properties) else return new Dissertacao() case "thesis": @@ -881,23 +881,26 @@ class XMLService { def pub = getObject(name, null) keys.each{ k -> switch (k){ - case "publicationDate": pub."$k" = XMLService.extractDate(params,name,i,k) + case "publicationDate": pub."$k" = extractDate(params,name,i,k) break - case "authors": pub."$k" = XMLService.extractNamesSet(params,name,i,k) + case "authors": pub."$k" = extractNamesSet(params,name,i,k) break case "id": case "volume": case "number": case "startYear": - case "endYear": pub."$k" = params[name+i+".$k"] as int + case "endYear": + def value = params[name+i+".$k"] + if(value!="") pub."$k" = value as int + else pub."$k" = null break //#if($researchProject) - case "members": pub."$k" = XMLService.extractNamesSet(params,name,i,k) + case "members": pub."$k" = extractNamesSet(params,name,i,k) break //#end //#if($funder) case "funders": - def funders = XMLService.extractFunders(params,name,i,k) + def funders = extractFunders(params,name,i,k) saveImportedFunders(funders, pub) break //#end @@ -919,41 +922,16 @@ class XMLService { if(!pubKeySet || !keys) return pubs - def n = XMLService.calcImportedPublicationSize(pubKeySet, keys) + def n = calcImportedPublicationSize(pubKeySet, keys) for(i in 0..n-1){ String title = params[name+i+".title"] if(!title || title.isEmpty()) continue - def pub = XMLService.fullImportedPublication(keys, params, name, i) + def pub = fullImportedPublication(keys, params, name, i) pubs += pub } return pubs } - private def extractDissertation(obj, name, params, keys){ - def dissertationKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} - - if(dissertationKeySet.isEmpty()) return null - obj.title = params[name+"0.title"] - if(!obj.title || obj.title.isEmpty()) return null - - keys?.each{ k -> //"title", "publicationDate", "authors", "school" - switch (k){ - case "publicationDate": obj."$k" = XMLService.extractDate(params,name,0,k) - break - case "authors": obj."$k" = XMLService.extractNamesSet(params,name,0,k) - break - case "id": obj."$k" = params[name+"0.$k"] as int - break - default: - def value = params[name+"0.$k"] - if(value == "null") value = null - obj."$k" = value - } - } - - return obj - } - //#if($researchLine) private def extractResearchLines(name, params, keys){ def lines = [] @@ -965,7 +943,7 @@ class XMLService { for(i in 0..n-1){ String title = params[name+i+".name"] if(!title || title.isEmpty()) continue - def pub = XMLService.fullImportedPublication(keys, params, name, i) + def pub = fullImportedPublication(keys, params, name, i) pub.publications = [] lines += pub } @@ -980,7 +958,7 @@ class XMLService { if(!projectKeySet || !keys) return projects - def n = XMLService.calcImportedResearchProjects(projectKeySet,keys) + def n = calcImportedResearchProjects(projectKeySet,keys) for(i in 0..n-1){ String title = params[name+i+".projectName"] if(!title || title.isEmpty()) continue @@ -990,15 +968,18 @@ class XMLService { switch (k){ case "id": case "startYear": - case "endYear": pub."$k" = params[name+i+".$k"] as int + case "endYear": + def value = params[name+i+".$k"] + if(value!="") pub."$k" = value as int + else pub."$k" = null break //#if($researchProject) - case "members": pub."$k" = XMLService.extractNamesSet(params,name,i,k) + case "members": pub."$k" = extractNamesSet(params,name,i,k) break //#end //#if($funder) case "funders": - def funders = XMLService.extractFunders(params,name,i,k) + def funders = extractFunders(params,name,i,k) saveImportedFunders(funders, pub) break //#end @@ -1032,7 +1013,7 @@ class XMLService { for(i in 0..n-1){ String title = params[name+i+".tituloTese"] if(!title || title.isEmpty()) continue - def orientation = XMLService.fullImportedPublication(keys, params, name, i) + def orientation = fullImportedPublication(keys, params, name, i) orientation.orientador = user orientations += orientation } @@ -1042,7 +1023,7 @@ class XMLService { def saveImportedPublications(params, user) { //#if($Article) - def journals = extractImportedPublications("journals", params, XMLService.JOURNAL_KEYS) + def journals = extractImportedPublications("journals", params, JOURNAL_KEYS) save(journals) //#end @@ -1055,11 +1036,11 @@ class XMLService { def bookChapters = extractImportedPublications("bookChapters", params, BOOK_CHAPTER_KEYS) save(bookChapters) - def dissertation = extractDissertation(new Dissertacao(), "masterDissertation", params, DISSERTATION_KEYS) - if (dissertation) save([dissertation]) + def dissertation = extractImportedPublications("masterDissertation", params, DISSERTATION_KEYS) + save(dissertation) - def thesis = extractDissertation(new Tese(), "thesis", params, DISSERTATION_KEYS) - if (thesis) save([thesis]) + def thesis = extractImportedPublications("thesis", params, DISSERTATION_KEYS) + save(thesis) def conferences = extractImportedPublications("conferences", params, CONFERENCE_KEYS) save(conferences) From edc10917d394d7e02486ff6ec0dbb63a27a73af1 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Thu, 31 Jul 2014 15:00:59 -0300 Subject: [PATCH 32/54] =?UTF-8?q?Refatora=C3=A7=C3=A3o:=20Reuso=20de=20c?= =?UTF-8?q?=C3=B3digo=20atrav=C3=A9s=20de=20chamada=20de=20m=C3=A9todo;=20?= =?UTF-8?q?remo=C3=A7=C3=A3o=20de=20m=C3=A9todo=20n=C3=A3o=20mais=20necess?= =?UTF-8?q?=C3=A1rio.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/services/rgms/XMLService.groovy | 59 +++++----------------- 1 file changed, 13 insertions(+), 46 deletions(-) diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index 5c7deac9..121db219 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -800,7 +800,8 @@ class XMLService { return result } - private static extractFunders(params,name,i,k){ + //#if($funder) + private static extractFunders(params,name,i,k, project){ def result = [] def funders = params.keySet()?.findAll{ it.contains(name+i+".$k")}?.sort() if(!funders || funders=="null") return result @@ -814,10 +815,17 @@ class XMLService { if(value == "null") value = null funder."$fk" = value } - result += funder + + Funder savedFunder = Funder.findByCode(funder?.code) + if(savedFunder) { + project.addToFunders(savedFunder) + } else { + funder.save(flush:true) + project.addToFunders(funder) + } } - return result } + //#end private static calcImportedPublicationSize(importedKeys, keys){ def authors = importedKeys.findAll{ it.contains("authors")} @@ -899,9 +907,7 @@ class XMLService { break //#end //#if($funder) - case "funders": - def funders = extractFunders(params,name,i,k) - saveImportedFunders(funders, pub) + case "funders": extractFunders(params,name,i,k, pub) break //#end //#if($Orientation) @@ -962,32 +968,7 @@ class XMLService { for(i in 0..n-1){ String title = params[name+i+".projectName"] if(!title || title.isEmpty()) continue - - def pub = new ResearchProject() - keys.each{ k -> //"projectName","description", "status", "responsible", "startYear", "endYear", "members", "funders" - switch (k){ - case "id": - case "startYear": - case "endYear": - def value = params[name+i+".$k"] - if(value!="") pub."$k" = value as int - else pub."$k" = null - break - //#if($researchProject) - case "members": pub."$k" = extractNamesSet(params,name,i,k) - break - //#end - //#if($funder) - case "funders": - def funders = extractFunders(params,name,i,k) - saveImportedFunders(funders, pub) - break - //#end - default: def value = params[name+i+".$k"] - if(value == "null") value = null - pub."$k" = value - } - } + def pub = fullImportedPublication(keys, params, name, i) projects += pub } return projects @@ -1102,20 +1083,6 @@ class XMLService { } //#end - //#if($funder) - public def saveImportedFunders(funders, ResearchProject project){ - funders?.each(){ f -> - Funder funder = Funder.findByCode(f.code) - if (funder) { - project.addToFunders(funder) - } else { - f.save(flush:true) - project.addToFunders(f) - } - } - } - //#end - //#if($Orientation) def saveImportedOrientations(orientations){ orientations?.each{ orientation -> From 270fc95b2221d12f75d9de9dd6a235bfb0bf62d4 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Thu, 31 Jul 2014 16:04:27 -0300 Subject: [PATCH 33/54] =?UTF-8?q?Refatora=C3=A7=C3=A3o:=20Exclus=C3=A3o=20?= =?UTF-8?q?de=20m=C3=A9todos=20redundantes;=20inclus=C3=A3o=20de=20par?= =?UTF-8?q?=C3=A2metros=20em=20m=C3=A9todo=20para=20flexibilizar=20uso.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/services/rgms/XMLService.groovy | 135 +++++------------- .../XMLImportTestDataAndOperations.groovy | 10 +- 2 files changed, 43 insertions(+), 102 deletions(-) diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index 121db219..4bcc2035 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -827,7 +827,7 @@ class XMLService { } //#end - private static calcImportedPublicationSize(importedKeys, keys){ + private def countImportedPublications = { importedKeys, keys -> def authors = importedKeys.findAll{ it.contains("authors")} def authorsTotal = (authors) ? authors.size() : 0 def n = (importedKeys.size()-authorsTotal)/(keys.size()-1) @@ -884,7 +884,6 @@ class XMLService { } } - //esse método deveria funcionar com ResearchProject, mas não funciona private static fullImportedPublication(keys, params, name, i){ def pub = getObject(name, null) keys.each{ k -> @@ -922,7 +921,7 @@ class XMLService { return pub } - private def extractImportedPublications(name, params, keys){ + private def extractImportedPublications(name, params, keys, calcImportedPublicationSize, propertyTitle){ def pubs = [] def pubKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} @@ -930,7 +929,7 @@ class XMLService { def n = calcImportedPublicationSize(pubKeySet, keys) for(i in 0..n-1){ - String title = params[name+i+".title"] + String title = params[name+i+propertyTitle] if(!title || title.isEmpty()) continue def pub = fullImportedPublication(keys, params, name, i) pubs += pub @@ -938,43 +937,14 @@ class XMLService { return pubs } - //#if($researchLine) - private def extractResearchLines(name, params, keys){ - def lines = [] - def linesKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} - - if(!linesKeySet || !keys) return lines - - def n = linesKeySet.size()/keys.size() - for(i in 0..n-1){ - String title = params[name+i+".name"] - if(!title || title.isEmpty()) continue - def pub = fullImportedPublication(keys, params, name, i) - pub.publications = [] - lines += pub - } - return lines + //#if($researchLine || $Orientation) + private def countImportedElements = { importedKeys, keys -> + def n = importedKeys?.size()/keys?.size() } //#end //#if($researchProject) - private def extractResearchProjects(name, params, keys){ - def projects = [] - def projectKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} - - if(!projectKeySet || !keys) return projects - - def n = calcImportedResearchProjects(projectKeySet,keys) - for(i in 0..n-1){ - String title = params[name+i+".projectName"] - if(!title || title.isEmpty()) continue - def pub = fullImportedPublication(keys, params, name, i) - projects += pub - } - return projects - } - - private static calcImportedResearchProjects(importedKeys, keys){ + private def countImportedResearchProjects = { importedKeys, keys -> def funders = importedKeys.findAll{ it.contains("funders")} def fundersTotal = (funders) ? funders.size() : 0 def members = importedKeys.findAll{ it.contains("members")} @@ -983,62 +953,57 @@ class XMLService { } //#end - //#if(Orientation) - private def extractOrientations(name, params, keys, user){ - def orientations = [] - def orientationKeySet = params.keySet()?.findAll{ it.contains(name) && it.contains(".")} - - if(!orientationKeySet || !keys) return orientations - - def n = orientationKeySet.size()/keys.size() - for(i in 0..n-1){ - String title = params[name+i+".tituloTese"] - if(!title || title.isEmpty()) continue - def orientation = fullImportedPublication(keys, params, name, i) - orientation.orientador = user - orientations += orientation - } - return orientations - } - //#end - def saveImportedPublications(params, user) { + def propertyTitle = ".title" + //#if($Article) - def journals = extractImportedPublications("journals", params, JOURNAL_KEYS) + def journals = extractImportedPublications("journals", params, JOURNAL_KEYS, countImportedPublications, propertyTitle) + journals.each{ if(!it.members) it.members = [] } save(journals) //#end - def tools = extractImportedPublications("tools", params, TOOL_KEYS) + def tools = extractImportedPublications("tools", params, TOOL_KEYS, countImportedPublications, propertyTitle) + tools.each{ if(!it.members) it.members = [] } save(tools) - def books = extractImportedPublications("books", params, BOOK_KEYS) + def books = extractImportedPublications("books", params, BOOK_KEYS, countImportedPublications, propertyTitle) + books.each{ if(!it.members) it.members = [] } save(books) - def bookChapters = extractImportedPublications("bookChapters", params, BOOK_CHAPTER_KEYS) + def bookChapters = extractImportedPublications("bookChapters", params, BOOK_CHAPTER_KEYS, countImportedPublications, propertyTitle) + bookChapters.each{ if(!it.members) it.members = [] } save(bookChapters) - def dissertation = extractImportedPublications("masterDissertation", params, DISSERTATION_KEYS) + def dissertation = extractImportedPublications("masterDissertation", params, DISSERTATION_KEYS, countImportedPublications, propertyTitle) + dissertation.each{ if(!it.members) it.members = [] } save(dissertation) - def thesis = extractImportedPublications("thesis", params, DISSERTATION_KEYS) + def thesis = extractImportedPublications("thesis", params, DISSERTATION_KEYS, countImportedPublications, propertyTitle) + thesis.each{ if(!it.members) it.members = [] } save(thesis) - def conferences = extractImportedPublications("conferences", params, CONFERENCE_KEYS) + def conferences = extractImportedPublications("conferences", params, CONFERENCE_KEYS, countImportedPublications, propertyTitle) + conferences.each{ if(!it.members) it.members = [] } save(conferences) //#if($researchLine) - def researchLines = extractResearchLines("researchLines", params, RESEARCH_LINE_KEYS) + def researchLines = extractImportedPublications("researchLines", params, RESEARCH_LINE_KEYS, countImportedElements, ".name") + researchLines.each{ + it.publications = [] + if(!it.members) it.members = [] + } save(researchLines) //#end //#if($researchProject) - def researchProjects = extractResearchProjects("researchProjects", params, RESEARCH_PROJECT_KEYS) - saveImportedResearchProjects(researchProjects) + def researchProjects = extractImportedPublications("researchProjects", params, RESEARCH_PROJECT_KEYS, countImportedResearchProjects, ".projectName") + save(researchProjects) //#end //#if($Orientation) - def orientations = extractOrientations("orientations", params, ORIENTATION_KEYS, user) - saveImportedOrientations(orientations) + def orientations = extractImportedPublications("orientations", params, ORIENTATION_KEYS, countImportedElements, ".tituloTese") + orientations.each{ it.orientador = user } + save(orientations) //#end return 'default.xml.save.message' @@ -1046,7 +1011,6 @@ class XMLService { def save(publications) { publications?.each{ pub -> - if(!pub?.members) pub?.members = [] if(pub.id){ def originalPub = Publication.get(pub.id) originalPub.properties = pub.properties @@ -1058,6 +1022,7 @@ class XMLService { } } + /* Esse método deve ser chamado pelo controller para o caso de publicações importadas por telas específicas */ def saveImportedPubsOfType(pubs, type) { def objects = [] pubs?.each{ p -> @@ -1068,36 +1033,6 @@ class XMLService { return objects } - //#if($researchProject) - def saveImportedResearchProjects(researchProjects){ - researchProjects?.each { rp -> - if(rp.id){ - def originalRP = ResearchProject.get(rp.id) - originalRP.properties = rp.properties - originalRP.save(flush:true) - } - else{ - rp.save(flush:true) - } - } - } - //#end - - //#if($Orientation) - def saveImportedOrientations(orientations){ - orientations?.each{ orientation -> - if(orientation.id){ - def originalOrientation = Orientation.get(orientation.id) - originalOrientation.properties = orientation.properties - originalOrientation.save(flush:true) - } - else{ - orientation.save(flush:true) - } - } - } - //#end - //#if($researchLine) private static checkContResearch(List researchLine, int i, String researchName) { List researchs = ((Node) researchLine[i]).children() @@ -1118,5 +1053,5 @@ class XMLService { researchLine.name = getAttributeValueFromNode(basicData, "TITULO-DA-LINHA-DE-PESQUISA") return researchLine.name } - //end + //#end } diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index 71d80bd4..ad6a6d7e 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -179,8 +179,14 @@ class XMLImportTestDataAndOperations { def xmlservice = new XMLService() ResearchProject rp = new ResearchProject(ResearchProjectTestDadaAndOperations.researchProjects[0]) //#if($funder) - def funder1 = new Funder(FunderTestDataAndOperations.funder[0]) - xmlservice.saveImportedFunders([funder1], rp) + def funder = new Funder(FunderTestDataAndOperations.funder[0]) + Funder savedFunder = Funder.findByCode(funder?.code) + if(savedFunder) { + rp.addToFunders(savedFunder) + } else { + funder.save(flush:true) + rp.addToFunders(funder) + } //#end rp.save(flush: true) From 493c9d450bd381c7e62617360bf72a3920b06d97 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Thu, 31 Jul 2014 16:18:41 -0300 Subject: [PATCH 34/54] =?UTF-8?q?Ajuste=20no=20uso=20de=20diretivas=20de?= =?UTF-8?q?=20pr=C3=A9-processamento.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/domain/rgms/researchProject/ResearchProject.groovy | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/grails-app/domain/rgms/researchProject/ResearchProject.groovy b/grails-app/domain/rgms/researchProject/ResearchProject.groovy index e68e8ee0..69e8a91a 100644 --- a/grails-app/domain/rgms/researchProject/ResearchProject.groovy +++ b/grails-app/domain/rgms/researchProject/ResearchProject.groovy @@ -1,4 +1,3 @@ -//#if($researchProject) package rgms.researchProject class ResearchProject { @@ -10,7 +9,7 @@ class ResearchProject { int startYear int endYear - static hasMany = [funders:Funder, members:String] + static hasMany = [/*#if($funder)*/ funders:Funder, /*#end*/ members:String] static constraints = { projectName(maxSize: 300, nullable: false, blank: false, unique: true) @@ -30,4 +29,3 @@ class ResearchProject { } } -//#end From ddc836558c05d2f547781693f32892aeb56dd931 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Thu, 31 Jul 2014 18:39:30 -0300 Subject: [PATCH 35/54] =?UTF-8?q?Corre=C3=A7=C3=A3o=20em=20importa=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20research=20project=20(ainda=20h=C3=A1=20problema?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rgms/publication/XMLController.groovy | 2 +- grails-app/services/rgms/XMLService.groovy | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index 0922ab7d..f94e926d 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -174,7 +174,7 @@ class XMLController { private returnWithMessage = { String msg, String controller, publications -> //importacao via opcao XMLImport no menu da tela inicial do sistema if (controller == "Publication"){ - if(publications.isEmpty()) request.message = message(code: "xml.import.empty.message") + if(!publications || publications.isEmpty()) request.message = message(code: "xml.import.empty.message") else request.message = message(code: msg) render(view:"home", model:[publications:publications]) } diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index 4bcc2035..d87dc9dc 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -816,12 +816,15 @@ class XMLService { funder."$fk" = value } - Funder savedFunder = Funder.findByCode(funder?.code) - if(savedFunder) { - project.addToFunders(savedFunder) - } else { - funder.save(flush:true) - project.addToFunders(funder) + def projectFunder = project.funders?.find{ it.code = funder.code } + if(!projectFunder) { + Funder savedFunder = Funder.findByCode(funder?.code) + if (savedFunder) { + project.addToFunders(savedFunder) + } else { + funder.save(flush: true) + project.addToFunders(funder) + } } } } From cfd51aaa6721952f5c8c357cf21292b0f1c6b8a1 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Thu, 31 Jul 2014 19:24:17 -0300 Subject: [PATCH 36/54] =?UTF-8?q?Corre=C3=A7=C3=A3o=20simples=20em=20mensa?= =?UTF-8?q?gem=20de=20alerta=20ao=20importar=20publica=C3=A7=C3=A3o.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/controllers/rgms/publication/XMLController.groovy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index f94e926d..3a64f379 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -174,7 +174,9 @@ class XMLController { private returnWithMessage = { String msg, String controller, publications -> //importacao via opcao XMLImport no menu da tela inicial do sistema if (controller == "Publication"){ - if(!publications || publications.isEmpty()) request.message = message(code: "xml.import.empty.message") + if(msg == 'default.xml.import.message' && (!publications || publications.isEmpty())){ + request.message = message(code: "xml.import.empty.message") + } else request.message = message(code: msg) render(view:"home", model:[publications:publications]) } From 823e437a5f3aa25650630a819891c04524fef142 Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Fri, 1 Aug 2014 18:43:30 -0300 Subject: [PATCH 37/54] =?UTF-8?q?Refatora=C3=A7=C3=A3o:=20excluindo=20Rese?= =?UTF-8?q?archTestDataAndOperations,=20removendo=20redund=C3=A2ncia=20das?= =?UTF-8?q?=20features=20TestDataAndOperations,=20=20defini=C3=A7=C3=A3o?= =?UTF-8?q?=20de=20=20um=20padr=C3=A3o=20para=20as=20features=20do=20tipo?= =?UTF-8?q?=20TestDataAndOperations=20(NomeFeatureTestDataAndOperations)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/ArticleSteps.groovy | 8 +- test/cucumber/steps/BookChapterSteps.groovy | 4 +- test/cucumber/steps/ConferenciaSteps.groovy | 4 +- test/cucumber/steps/DissertacaoSteps.groovy | 4 +- test/cucumber/steps/FerramentaSteps.groovy | 3 +- test/cucumber/steps/ResearchGroupSteps.groovy | 24 +++--- test/cucumber/steps/ResearchLineSteps.groovy | 10 +-- .../steps/TechnicalReportSteps.groovy | 4 +- test/cucumber/steps/ThesisSteps.groovy | 4 +- test/cucumber/steps/VisitSteps.groovy | 37 ++++----- test/cucumber/steps/XMLImportSteps.groovy | 5 +- .../ResearchLineCreatePage.groovy | 6 +- test/functional/pages/visit/VisitPage.groovy | 6 +- ...y => FacebookTestDataAndOperations.groovy} | 2 +- ...> PublicationTestDataAndOperations.groovy} | 2 +- ...ResearchGroupTestDataAndOperations.groovy} | 2 +- .../ResearchLineTestDataAndOperations.groovy | 68 +++++++++++++++- .../steps/TestDataAndOperations.groovy | 6 +- .../TestDataAndOperationsResearchLine.groovy | 77 ------------------- ...oovy => VisitTestDataAndOperations.groovy} | 2 +- 20 files changed, 134 insertions(+), 144 deletions(-) rename test/functional/steps/{TestDataAndOperationsFacebook.groovy => FacebookTestDataAndOperations.groovy} (94%) rename test/functional/steps/{TestDataAndOperationsPublication.groovy => PublicationTestDataAndOperations.groovy} (93%) rename test/functional/steps/{TestDataAndOperationsResearchGroup.groovy => ResearchGroupTestDataAndOperations.groovy} (98%) delete mode 100644 test/functional/steps/TestDataAndOperationsResearchLine.groovy rename test/functional/steps/{TestDataAndOperationsVisit.groovy => VisitTestDataAndOperations.groovy} (99%) diff --git a/test/cucumber/steps/ArticleSteps.groovy b/test/cucumber/steps/ArticleSteps.groovy index 9030a33c..f05b34ed 100644 --- a/test/cucumber/steps/ArticleSteps.groovy +++ b/test/cucumber/steps/ArticleSteps.groovy @@ -8,8 +8,8 @@ import pages.PublicationsPage import rgms.publication.Periodico import rgms.tool.TwitterTool import steps.ArticleTestDataAndOperations -import steps.TestDataAndOperationsFacebook -import steps.TestDataAndOperationsPublication +import steps.FacebookTestDataAndOperations +import steps.PublicationTestDataAndOperations import static cucumber.api.groovy.EN.* @@ -247,7 +247,7 @@ Given(~'^I am at the Add Article Page$') { -> When(~'^I share the article entitled "([^"]*)" on facebook$') { String title -> - TestDataAndOperationsFacebook.ShareArticleOnFacebook(title) + FacebookTestDataAndOperations.ShareArticleOnFacebook(title) } //#end @@ -279,7 +279,7 @@ def Login() { */ Then(~'^I see my user listed as an author member of article by default$') { -> at ArticleCreatePage - assert TestDataAndOperationsPublication.containsUser(page.selectedMembers()) + assert PublicationTestDataAndOperations.containsUser(page.selectedMembers()) } diff --git a/test/cucumber/steps/BookChapterSteps.groovy b/test/cucumber/steps/BookChapterSteps.groovy index 1ef01632..0b0f7d33 100644 --- a/test/cucumber/steps/BookChapterSteps.groovy +++ b/test/cucumber/steps/BookChapterSteps.groovy @@ -5,7 +5,7 @@ import pages.PublicationsPage import pages.* import rgms.publication.BookChapter import steps.BookChapterTestDataAndOperations -import steps.TestDataAndOperationsPublication +import steps.PublicationTestDataAndOperations import static cucumber.api.groovy.EN.* @@ -70,7 +70,7 @@ And(~'^I still on the book chapter create page$') { -> Then(~'^I see my user listed as a member of book chapter by default$') { -> at BookChapterCreatePage - assert TestDataAndOperationsPublication.containsUser(page.selectedMembers()) + assert PublicationTestDataAndOperations.containsUser(page.selectedMembers()) } When(~'^I view the book chapter list$') { -> diff --git a/test/cucumber/steps/ConferenciaSteps.groovy b/test/cucumber/steps/ConferenciaSteps.groovy index f11e0542..6656e603 100644 --- a/test/cucumber/steps/ConferenciaSteps.groovy +++ b/test/cucumber/steps/ConferenciaSteps.groovy @@ -5,8 +5,8 @@ import pages.LoginPage import pages.PublicationsPage import rgms.member.Member import rgms.publication.Conferencia +import steps.PublicationTestDataAndOperations import steps.TestDataAndOperations -import steps.TestDataAndOperationsPublication import steps.ConferenciaTestDataAndOperations import static cucumber.api.groovy.EN.* @@ -101,7 +101,7 @@ Then(~'^I can remove one conferencia$') {-> Then(~'^I see my user listed as an author member of conferencia by default$') {-> at ConferenciaCreatePage - assert TestDataAndOperationsPublication.containsUser(page.selectedMembers()) + assert PublicationTestDataAndOperations.containsUser(page.selectedMembers()) } Then(~'^I am back at the publications and conferencias menu$') {-> diff --git a/test/cucumber/steps/DissertacaoSteps.groovy b/test/cucumber/steps/DissertacaoSteps.groovy index 03cd3324..3ee7dd85 100644 --- a/test/cucumber/steps/DissertacaoSteps.groovy +++ b/test/cucumber/steps/DissertacaoSteps.groovy @@ -4,8 +4,8 @@ import pages.DissertationPage import pages.DissertationShowPage import rgms.authentication.User import rgms.publication.Dissertacao +import steps.PublicationTestDataAndOperations import steps.TestDataDissertacao -import steps.TestDataAndOperationsPublication import static cucumber.api.groovy.EN.* @@ -142,7 +142,7 @@ When(~'^I upload a new dissertation "([^"]*)" with title "([^"]*)"$') { filenam Then(~'^I see my user listed as an author member of dissertation by default$') {-> at DissertationCreate - assert TestDataAndOperationsPublication.containsUser(page.selectedMembers()) + assert PublicationTestDataAndOperations.containsUser(page.selectedMembers()) } Then(~'^I see my school name as school of dissertation by default$') {-> diff --git a/test/cucumber/steps/FerramentaSteps.groovy b/test/cucumber/steps/FerramentaSteps.groovy index d19739b9..cc1b8a05 100644 --- a/test/cucumber/steps/FerramentaSteps.groovy +++ b/test/cucumber/steps/FerramentaSteps.groovy @@ -1,6 +1,7 @@ import pages.ferramenta.* import rgms.publication.Ferramenta import steps.FerramentaTestDataAndOperations +import steps.PublicationTestDataAndOperations import steps.TestDataAndOperationsPublication import static cucumber.api.groovy.EN.* @@ -121,7 +122,7 @@ Then(~'^I am still on ferramenta page$') {-> // new ferramenta filled with user data by default Then(~'^I see my user listed as an author member of ferramenta by default$') {-> at FerramentaCreatePage - assert TestDataAndOperationsPublication.containsUser(page.selectedMembers()) + assert PublicationTestDataAndOperations.containsUser(page.selectedMembers()) } // edit ferramenta diff --git a/test/cucumber/steps/ResearchGroupSteps.groovy b/test/cucumber/steps/ResearchGroupSteps.groovy index 4b558fd3..9c907ff5 100644 --- a/test/cucumber/steps/ResearchGroupSteps.groovy +++ b/test/cucumber/steps/ResearchGroupSteps.groovy @@ -1,4 +1,4 @@ -import steps.TestDataAndOperationsResearchGroup +import steps.ResearchGroupTestDataAndOperations import static cucumber.api.groovy.EN.* import pages.LoginPage import pages.PublicationsPage @@ -14,7 +14,7 @@ Given(~'^the system has no research group entitled "([^"]*)" stored in the syste } When(~'^I create a research group named "([^"]*)" with the description "([^"]*)"$') { String name, description -> - TestDataAndOperationsResearchGroup.createResearchGroup(name, description) + ResearchGroupTestDataAndOperations.createResearchGroup(name, description) } Then(~'^the research group "([^"]*)" is properly stored by the system$') { String name -> @@ -23,7 +23,7 @@ Then(~'^the research group "([^"]*)" is properly stored by the system$') { Strin } Given(~'^the system has a research group entitled "([^"]*)" with the description "([^"]*)" stored in the system$' ) { String name, description -> - TestDataAndOperationsResearchGroup.createResearchGroup(name, description) + ResearchGroupTestDataAndOperations.createResearchGroup(name, description) researchGroup = ResearchGroup.findByName(name) assert researchGroup != null } @@ -44,7 +44,7 @@ Given(~'^the system has no research group with no name stored in the system$') { } When(~'^I create a research group with no name and with the description "([^"]*)" $') { String arg1 -> - TestDataAndOperationsResearchGroup.createResearchGroup("", arg1) + ResearchGroupTestDataAndOperations.createResearchGroup("", arg1) } Then(~'^the research group is not stored in the system because it has no name$') { -> @@ -59,7 +59,7 @@ Then(~'^the research group with name "([^"]*)" is not stored in the system becau When(~'^I modify the research group entitled "([^"]*)" to "([^"]*)" and its description to "([^"]*)"$') { String oldName, String newName, String newDescription -> researchGroup = ResearchGroup.findByName(oldName) - TestDataAndOperationsResearchGroup.editResearchGroup(researchGroup, newName, newDescription) + ResearchGroupTestDataAndOperations.editResearchGroup(researchGroup, newName, newDescription) } Then(~'^the edited research group "([^"]*)" with description "([^"]*)" is properly stored in the system$') { String name, String description -> @@ -70,7 +70,7 @@ Then(~'^the edited research group "([^"]*)" with description "([^"]*)" is proper When(~'^I delete the research group entitled "([^"]*)"$') { String name -> researchGroup = ResearchGroup.findByName(name) - TestDataAndOperationsResearchGroup.deleteResearchGroup(researchGroup) + ResearchGroupTestDataAndOperations.deleteResearchGroup(researchGroup) } Then(~'^the research group "([^"]*)" is properly deleted of the system$') { String name -> @@ -79,7 +79,7 @@ Then(~'^the research group "([^"]*)" is properly deleted of the system$') { Stri } When(~'^I create a research group with no name and with the description "([^"]*)"$') { String description -> - TestDataAndOperationsResearchGroup.createResearchGroup("", description) + ResearchGroupTestDataAndOperations.createResearchGroup("", description) } Then(~'^the research group is not stored in the system because is invalid$') { @@ -89,7 +89,7 @@ Then(~'^the research group is not stored in the system because is invalid$') { } When(~'^I create a research group with name "([^"]*)" and with no description$') { String name -> - TestDataAndOperationsResearchGroup.createResearchGroup(name, "") + ResearchGroupTestDataAndOperations.createResearchGroup(name, "") } When(~'^I select the new research group option at research group list page$') { @@ -150,7 +150,7 @@ Then(~'^I can change the research group name to "([^"]*)" and save it$') { Strin When(~'^I modify the description of research group entitled "([^"]*)" to none$') { String oldName -> researchGroup = ResearchGroup.findByName(oldName) - TestDataAndOperationsResearchGroup.editResearchGroup(researchGroup, oldName, "") + ResearchGroupTestDataAndOperations.editResearchGroup(researchGroup, oldName, "") } Then(~'^the description of research group entitled "([^"]*)" is not none$') { String name -> @@ -160,7 +160,7 @@ Then(~'^the description of research group entitled "([^"]*)" is not none$') { St When(~'^I modify the name of research group entitled "([^"]*)" to none$') { String oldName -> researchGroup = ResearchGroup.findByName(oldName) - TestDataAndOperationsResearchGroup.editResearchGroup(researchGroup, "", researchGroup.getDescription()) + ResearchGroupTestDataAndOperations.editResearchGroup(researchGroup, "", researchGroup.getDescription()) } Then(~'^there is no research group entitled none$') { -> @@ -172,7 +172,7 @@ Then(~'^there is no research group entitled none$') { -> Given(~'^the system has a research group entitled "([^"]*)" with childof none$') { String name -> - TestDataAndOperationsResearchGroup.createResearchGroup(name, "description") + ResearchGroupTestDataAndOperations.createResearchGroup(name, "description") researchGroup = ResearchGroup.findByName(name) researchGroup.childOf = null researchGroup.save() @@ -183,7 +183,7 @@ Given(~'^the system has a research group entitled "([^"]*)" with childof none$') When(~'^I modify the childof of research group entitled "([^"]*)" to itself$') { String name -> researchGroup = ResearchGroup.findByName(name) - TestDataAndOperationsResearchGroup.editResearchGroupChildOf(researchGroup, researchGroup) + ResearchGroupTestDataAndOperations.editResearchGroupChildOf(researchGroup, researchGroup) } Then(~'^the childof of research group "([^"]*)" is none$') { String name -> diff --git a/test/cucumber/steps/ResearchLineSteps.groovy b/test/cucumber/steps/ResearchLineSteps.groovy index ce84ed8b..bf4a3c8c 100644 --- a/test/cucumber/steps/ResearchLineSteps.groovy +++ b/test/cucumber/steps/ResearchLineSteps.groovy @@ -12,14 +12,14 @@ import pages.ResearchLinePages.ResearchLineEditPage import static cucumber.api.groovy.EN.* Given(~'^the system has a research line named "([^"]*)" with a description "([^"]*)"$') { String name, description -> - TestDataAndOperationsResearchLine.insertsResearchLine(name, description) + ResearchLineTestDataAndOperations.insertsResearchLine(name, description) research_line = ResearchLine.findByName(name) assert research_line != null } When(~'^I remove the research line "([^"]*)"$') { String name -> research_line = ResearchLine.findByName(name) - TestDataAndOperationsResearchLine.deleteResearchLine(research_line.id) + ResearchLineTestDataAndOperations.deleteResearchLine(research_line.id) } Then(~'^the research line "([^"]*)" is properly removed by the system'){String name -> @@ -29,7 +29,7 @@ Then(~'^the research line "([^"]*)" is properly removed by the system'){String n When(~'^I update the research line "([^"]*)" with a description "([^"]*)"$') { String name, String description -> - TestDataAndOperationsResearchLine.updateResearchLine(name,description) + ResearchLineTestDataAndOperations.updateResearchLine(name,description) } Then(~'^the research line "([^"]*)" has the description updated to "([^"]*)"$'){String name, description -> @@ -43,7 +43,7 @@ Given(~'^the system has no research line named "([^"]*)"$') { String name -> } When(~'^I create the research line named "([^"]*)" with empty description$') { String name -> - TestDataAndOperationsResearchLine.createResearchLine(name) + ResearchLineTestDataAndOperations.createResearchLine(name) } Then(~'^the research line "([^"]*)" is not stored, because is invalid$'){String name -> @@ -52,7 +52,7 @@ Then(~'^the research line "([^"]*)" is not stored, because is invalid$'){String } When (~'I create the research line "([^"]*)" with description "([^"]*)" with no member assigned'){String name, description -> - TestDataAndOperationsResearchLine.createResearchLine(name) + ResearchLineTestDataAndOperations.createResearchLine(name) } Then (~'the research line "([^"]*)" is properly saved with no error'){String name -> research_line = ResearchLine.findByName(name) diff --git a/test/cucumber/steps/TechnicalReportSteps.groovy b/test/cucumber/steps/TechnicalReportSteps.groovy index 68a00bc4..8bdbc3b1 100644 --- a/test/cucumber/steps/TechnicalReportSteps.groovy +++ b/test/cucumber/steps/TechnicalReportSteps.groovy @@ -7,9 +7,9 @@ import pages.technicalReport.TechnicalReportShowPage import rgms.authentication.User import rgms.member.Member import rgms.publication.TechnicalReport +import steps.PublicationTestDataAndOperations import steps.TestDataAndOperations import steps.TechnicalReportTestDataAndOperations -import steps.TestDataAndOperationsPublication import static cucumber.api.groovy.EN.* @@ -132,7 +132,7 @@ When(~'^I click on "New TechnicalReport" option at Technical Report list$') {-> Then(~'^I see my user listed as an author member of technical report by default$') {-> at TechnicalReportCreatePage - assert TestDataAndOperationsPublication.containsUser(page.selectedMembers()) + assert PublicationTestDataAndOperations.containsUser(page.selectedMembers()) } Then(~'^I see my school name as institution of technical report by default$') {-> diff --git a/test/cucumber/steps/ThesisSteps.groovy b/test/cucumber/steps/ThesisSteps.groovy index 90129b91..8914c49d 100644 --- a/test/cucumber/steps/ThesisSteps.groovy +++ b/test/cucumber/steps/ThesisSteps.groovy @@ -6,7 +6,7 @@ import pages.thesis.ThesisCreatePage import pages.thesis.ThesisShowPage import rgms.authentication.User import rgms.publication.Tese -import steps.TestDataAndOperationsPublication +import steps.PublicationTestDataAndOperations import steps.ThesisTestDataAndOperations import static cucumber.api.groovy.EN.* @@ -79,7 +79,7 @@ When(~'^I select the new thesis option at the thesis page$') { -> */ Then(~'^I see my user listed as an author member of thesis by default$') { -> at ThesisCreatePage - assert TestDataAndOperationsPublication.containsUser(page.selectedMembers()) + assert PublicationTestDataAndOperations.containsUser(page.selectedMembers()) } Then(~'^I see my school name as school of thesis by default$') { -> diff --git a/test/cucumber/steps/VisitSteps.groovy b/test/cucumber/steps/VisitSteps.groovy index 05f5b49a..f42668f4 100644 --- a/test/cucumber/steps/VisitSteps.groovy +++ b/test/cucumber/steps/VisitSteps.groovy @@ -7,7 +7,7 @@ import pages.visit.VisitPage import pages.visit.VisitCreatePage import pages.visit.VisitShowPage import rgms.tool.TwitterTool -import steps.TestDataAndOperationsVisit +import steps.VisitTestDataAndOperations import static cucumber.api.groovy.EN.* @@ -16,7 +16,7 @@ Given(~'^the system has no visitor named "([^"]*)"$') { String name -> } When(~'^I create the visit for the visitor "([^"]*)" with initial date "([^"]*)"$') { String name, String date -> - TestDataAndOperationsVisit.createVisit(name, date, date) + VisitTestDataAndOperations.createVisit(name, date, date) } Then(~'^the visitor named "([^"]*)" is properly stored by the system$') { String name -> @@ -24,19 +24,19 @@ Then(~'^the visitor named "([^"]*)" is properly stored by the system$') { String } And(~'^the visit for the visitor "([^"]*)" with initial and final date equal to "([^"]*)" is properly stored by the system$') { String name, String date -> - assert TestDataAndOperationsVisit.searchVisit(name, date, date) != null + assert VisitTestDataAndOperations.searchVisit(name, date, date) != null } When(~'^I create the visit for the visitor "([^"]*)" with initial date "([^"]*)" and final date "([^"]*)"$') { String name, String initialDate, String finalDate -> - TestDataAndOperationsVisit.createVisit(name, initialDate, finalDate) + VisitTestDataAndOperations.createVisit(name, initialDate, finalDate) } And(~'^the visit for the visitor "([^"]*)" with initial date "([^"]*)" and final date "([^"]*)" is properly stored by the system$') { String name, String initialDate, String finalDate -> - assert TestDataAndOperationsVisit.searchVisit(name, initialDate, finalDate) != null + assert VisitTestDataAndOperations.searchVisit(name, initialDate, finalDate) != null } Given(~'^the system has visitor named "([^"]*)"$') { String name -> - TestDataAndOperationsVisit.createVisitor(name) + VisitTestDataAndOperations.createVisitor(name) assert Visitor.findByName(name) != null } @@ -44,8 +44,9 @@ Given(~'^the system has visitor named "([^"]*)"$') { String name -> * @author carloscemb */ And(~'^a visit for the visitor "([^"]*)" with initial date "([^"]*)" and final date "([^"]*)"$') { String name, String initialDate, String finalDate -> - TestDataAndOperationsVisit.createVisit(name, initialDate, finalDate) - assert TestDataAndOperationsVisit.searchVisit(name, initialDate, finalDate) != null + VisitTestDataAndOperations.createVisit(name, initialDate, finalDate) + assert VisitTestDataAndOperations + .searchVisit(name, initialDate, finalDate) != null } /** @@ -60,8 +61,8 @@ When(~"^I view the list of visits\$") {-> * @author carloscemb */ Then(~'^the list is returned with the visit of the visitor named "([^"]*)" with initial date "([^"]*)" and final date "([^"]*)"$') { String name, String initialDate, String finalDate -> - def visit = TestDataAndOperationsVisit.searchVisit(name, initialDate, finalDate) - assert TestDataAndOperationsVisit.containsVisit(visit) + def visit = VisitTestDataAndOperations.searchVisit(name, initialDate, finalDate) + assert VisitTestDataAndOperations.containsVisit(visit) } /** @@ -84,7 +85,7 @@ Given(~'^the visit of the visitor named "([^"]*)" with initial date "([^"]*)" an page.selectNewVisit() at VisitCreatePage page.fillVisitDetails() - visit = TestDataAndOperationsVisit.findVisit(name, initialDate, finalDate) + visit = VisitTestDataAndOperations.findVisit(name, initialDate, finalDate) assert visit != null } @@ -101,14 +102,14 @@ Then(~'^my resulting visits list contains the visit of the visitor named "([^"]* * @author carloscemb */ When(~'^I delete the visit of the visitor named "([^"]*)" with initial date "([^"]*)" and final date "([^"]*)"$') { String name, String initialDate, String finalDate -> - TestDataAndOperationsVisit.removeVisit(name, initialDate, finalDate) + VisitTestDataAndOperations.removeVisit(name, initialDate, finalDate) } /** * @author carloscemb */ Then(~'^the visit of the visitor named "([^"]*)" with initial date "([^"]*)" and final date "([^"]*)" is properly removed by the system$') { String name, String initialDate, String finalDate -> - assert TestDataAndOperationsVisit.searchVisit(name, initialDate, finalDate) == null + assert VisitTestDataAndOperations.searchVisit(name, initialDate, finalDate) == null } /** @@ -132,7 +133,7 @@ Then(~'the visit details are showed and I can select the option to remove$') {-> * @author carloscemb */ When(~'^I edit the visit of the visitor named "([^"]*)" with initial date "([^"]*)" and final date "([^"]*)" to the visitor named "([^"]*)"$') { String oldName, String oldInitialDate, String oldFinalDate, String newName -> - def updatedVisit = TestDataAndOperationsVisit.editVisit(oldName, oldInitialDate, oldFinalDate, newName) + def updatedVisit = VisitTestDataAndOperations.editVisit(oldName, oldInitialDate, oldFinalDate, newName) assert updatedVisit != null } @@ -140,7 +141,7 @@ When(~'^I edit the visit of the visitor named "([^"]*)" with initial date "([^"] * @author carloscemb */ Then(~'^the visit of the visitor named "([^"]*)" with initial date "([^"]*)" and final date "([^"]*)" is properly updated by the system$') { String name, String initialDate, String finalDate -> - assert TestDataAndOperationsVisit.searchVisit(name, initialDate, finalDate) == null + assert VisitTestDataAndOperations.searchVisit(name, initialDate, finalDate) == null } /** @@ -165,21 +166,21 @@ Then(~'I can select the "([^"]*)" option visit$') { String option -> * @author penc */ Then(~'^the visit for the visitor "([^"]*)" with initial date "([^"]*)" and final date "([^"]*)" is not stored by the system because it is invalid$') { String name, String initialDate, String finalDate -> - assert TestDataAndOperationsVisit.searchVisit(name, initialDate, finalDate) == null + assert VisitTestDataAndOperations.searchVisit(name, initialDate, finalDate) == null } /** * @author penc */ When(~'^I try to edit the visit of the visitor named "([^"]*)" with initial date "([^"]*)" and final date "([^"]*)" changing the final date to "([^"]*)"$') { String name, String initialDate, String oldFinalDate, String newFinalDate -> - TestDataAndOperationsVisit.editVisitChangeData(name, initialDate, oldFinalDate, newFinalDate) + VisitTestDataAndOperations.editVisitChangeData(name, initialDate, oldFinalDate, newFinalDate) } /** * @author penc */ Then(~'^the visit of the visitor named "([^"]*)" with initial date "([^"]*)" and final date "([^"]*)" is not properly updated by the system because it is invalid$') { String name, String initialDate, String finalDate -> - assert TestDataAndOperationsVisit.searchVisit(name, initialDate, finalDate) != null + assert VisitTestDataAndOperations.searchVisit(name, initialDate, finalDate) != null } diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index b1408a1c..8972ca3f 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -10,10 +10,9 @@ import rgms.publication.* import rgms.researchProject.ResearchProject import steps.ArticleTestDataAndOperations import steps.ConferenciaTestDataAndOperations +import steps.PublicationTestDataAndOperations import steps.ResearchLineTestDataAndOperations import steps.ResearchProjectTestDadaAndOperations -import steps.TestDataAndOperationsPublication -import steps.TestDataAndOperationsResearchLine import steps.XMLImportTestDataAndOperations import pages.XMLImportPage @@ -357,7 +356,7 @@ Given(~'^The system has some publications stored $'){ -> } When(~'^ I upload the file "([^"]*)" $') { String typeFile -> - TestDataAndOperationsPublication.uploadPublication(typeFile) + PublicationTestDataAndOperations.uploadPublication(typeFile) PublicationController controllerP = new PublicationController() status = controllerP.checkTypeFile(typeFile) assert status == false diff --git a/test/functional/pages/ResearchLinePages/ResearchLineCreatePage.groovy b/test/functional/pages/ResearchLinePages/ResearchLineCreatePage.groovy index f2e12db9..d5dcc796 100644 --- a/test/functional/pages/ResearchLinePages/ResearchLineCreatePage.groovy +++ b/test/functional/pages/ResearchLinePages/ResearchLineCreatePage.groovy @@ -1,6 +1,8 @@ package pages.ResearchLinePages import geb.Page +import steps.ResearchGroupTestDataAndOperations +import steps.ResearchLineTestDataAndOperations import steps.TestDataAndOperationsResearchLine @@ -16,7 +18,7 @@ class ResearchLineCreatePage extends Page { def fillResearchLineDetails() { def name = "Modelo Cascata Renovado" - def description = TestDataAndOperationsResearchLine.findResearchLineByName(name).description + def description = ResearchLineTestDataAndOperations.findResearchLineByNameOrientation(name).description $("form").name = name $("form").description = description assert $("form").name == name @@ -26,7 +28,7 @@ class ResearchLineCreatePage extends Page { def createsResearchLine(String name) { - def research = TestDataAndOperationsResearchLine.findResearchLineByName(name) + def research = ResearchLineTestDataAndOperations.findResearchLineByNameOrientation(name) $("form").name = research.name $("form").description = research.description $("input", name: "create").click() diff --git a/test/functional/pages/visit/VisitPage.groovy b/test/functional/pages/visit/VisitPage.groovy index 38febe0a..fdb0b18e 100644 --- a/test/functional/pages/visit/VisitPage.groovy +++ b/test/functional/pages/visit/VisitPage.groovy @@ -2,7 +2,7 @@ package pages.visit import geb.Page import pages.GetPageTitle -import steps.TestDataAndOperationsVisit +import steps.VisitTestDataAndOperations class VisitPage extends Page { @@ -36,7 +36,7 @@ class VisitPage extends Page { def selectViewVisit(name, initialDate, finalDate) { def visitRows = getVisitRows() - def testVisit = TestDataAndOperationsVisit.findVisit(name, initialDate, finalDate) + def testVisit = VisitTestDataAndOperations.findVisit(name, initialDate, finalDate) def dataInicio = testVisit.initialDate[0].format('dd/MM/yyyy') def showLink = visitRows.find('td').find([text:dataInicio]) showLink.click() @@ -49,7 +49,7 @@ class VisitPage extends Page { def visitRows = getVisitRows() def visitColumns = visitRows[row].find('td') - def testVisit = TestDataAndOperationsVisit.findVisit(name, initialDate, finalDate) + def testVisit = VisitTestDataAndOperations.findVisit(name, initialDate, finalDate) assert testVisit != null assert visitColumns[0].text() == testVisit.initialDate[0].format('dd/MM/yyyy') diff --git a/test/functional/steps/TestDataAndOperationsFacebook.groovy b/test/functional/steps/FacebookTestDataAndOperations.groovy similarity index 94% rename from test/functional/steps/TestDataAndOperationsFacebook.groovy rename to test/functional/steps/FacebookTestDataAndOperations.groovy index 48627a38..8adc3fd0 100644 --- a/test/functional/steps/TestDataAndOperationsFacebook.groovy +++ b/test/functional/steps/FacebookTestDataAndOperations.groovy @@ -10,7 +10,7 @@ import rgms.publication.PublicationController * Time: 22:47 * To change this template use File | Settings | File Templates. */ -class TestDataAndOperationsFacebook { +class FacebookTestDataAndOperations { static public void ShareArticleOnFacebook(String title){ def member = new Member() diff --git a/test/functional/steps/TestDataAndOperationsPublication.groovy b/test/functional/steps/PublicationTestDataAndOperations.groovy similarity index 93% rename from test/functional/steps/TestDataAndOperationsPublication.groovy rename to test/functional/steps/PublicationTestDataAndOperations.groovy index 4be55280..a1e9932d 100644 --- a/test/functional/steps/TestDataAndOperationsPublication.groovy +++ b/test/functional/steps/PublicationTestDataAndOperations.groovy @@ -5,7 +5,7 @@ import rgms.member.* import rgms.publication.PublicationController import rgms.publication.XMLController -class TestDataAndOperationsPublication { +class PublicationTestDataAndOperations { static public boolean containsUser(members){ def userData = User.findByUsername('admin')?.author?.id.toString() diff --git a/test/functional/steps/TestDataAndOperationsResearchGroup.groovy b/test/functional/steps/ResearchGroupTestDataAndOperations.groovy similarity index 98% rename from test/functional/steps/TestDataAndOperationsResearchGroup.groovy rename to test/functional/steps/ResearchGroupTestDataAndOperations.groovy index 1e20cb4d..1ff009a4 100644 --- a/test/functional/steps/TestDataAndOperationsResearchGroup.groovy +++ b/test/functional/steps/ResearchGroupTestDataAndOperations.groovy @@ -10,7 +10,7 @@ import rgms.member.ResearchGroupController * Time: 21:43 * To change this template use File | Settings | File Templates. */ -class TestDataAndOperationsResearchGroup { +class ResearchGroupTestDataAndOperations { static public void createResearchGroup(String name, description) { def researchGroupController = new ResearchGroupController() diff --git a/test/functional/steps/ResearchLineTestDataAndOperations.groovy b/test/functional/steps/ResearchLineTestDataAndOperations.groovy index 2a967e2c..e3b629f4 100644 --- a/test/functional/steps/ResearchLineTestDataAndOperations.groovy +++ b/test/functional/steps/ResearchLineTestDataAndOperations.groovy @@ -14,6 +14,70 @@ class ResearchLineTestDataAndOperations { [name: "Desenvolvimento Progressivo de Sistemas Complexos Orientados a Objetos", description: "O foco desta linha de pesquisa é a definição e aperfeiçoamento do processo de implementação (ou codificação) de aplicações orientadas a objetos complexas, de forma que tais aplicações possam ser mais facilmente implementadas, testadas, e adaptadas. Com isso pretendemos ajudar a aumentar a produtividade, reduzindo tempo e custos de desenvolvimento, dos engenheiros de software que usem ou venham a usar um processo de desenvolvimento orientado a objetos. Além disso, esperamos possibilitar a implementação de aplicações com níveis de confiabilidade, extensibilidade, e reusabilidade adequados para as necessidades de um mercado cada vez mais globalizado e competitivo."] ] + static researchLines = [ + [name: "IA Avancada", description: ""], + [name: "Redes Avancadas", description: "Redes de Computadores Avancadas"], + [name: "Teoria da informacao - Complexidade no espaco", description: "P=NP"], + [name: "Novo Padrao Arquitetural MVCE", description: "Nova arquitetura que promete revolucionar a web"], + [name: "Modelo Cascata Renovado", description: "Altera��o do modelo original"] + ] + + static public def insertsResearchLine(String name, description) { + def inserted = ResearchLine.findByName(name) + if (!inserted) { + // def research = TestDataAndOperationsResearchLine.findResearchLineByNameOrientation(name) + ResearchLine rl = new ResearchLine() + rl.setName(name) + rl.setDescription(description) + rl.save() + } + + } + + static public def findResearchLineByName(String name) { + researchLines.find { researchLine -> + researchLine.name == name + } + } + + static public void deleteResearchLine(def id) { + def res = new ResearchLineController() + res.params.id = id + res.request.setContent(new byte[1000]) // Could also vary the request content. + res.delete() + res.response.reset() + } + + static public void updateResearchLine(String name, String description) { + def res = new ResearchLineController() + def research_line = ResearchLine.findByName(name) + res.params.id = research_line.id + res.params.name = research_line.name + res.params.description = description + res.request.setContent(new byte[1000]) // Could also vary the request content. + res.update() + res.response.reset() + } + + static public void createResearchLine(String name) { + def cont = new ResearchLineController() + def research = findResearchLineByNameOrientation(name) + cont.params.name = research.name + cont.params.description = research.description + cont.request.setContent(new byte[1000]) // Could also vary the request content. + cont.create() + cont.save() + cont.response.reset() + } + + static public void listAllResearchLine(){ + def cont2 = new ResearchLineController() + cont2.request.setContent(new byte[1000]) // Could also vary the request content. + cont2.findAllResearchLine() + cont2.response.reset() + } + + static public void uploadResearchLine(filepath) { def cont = new XMLController() def xml = new File((String) filepath); @@ -31,14 +95,14 @@ class ResearchLineTestDataAndOperations { cont.response.reset() } - static public def findResearchLineByName(String name) { + static public def findResearchLineByNameOrientation(String name) { researchlines.find { orientation -> orientation.name == name } } static public boolean researchLineCompatibleTo(ResearchLine line, String name){ - def testResearchline = findResearchLineByName(name) + def testResearchline = findResearchLineByNameOrientation(name) def compatible = false if (testResearchline == null && name == null) { compatible = true diff --git a/test/functional/steps/TestDataAndOperations.groovy b/test/functional/steps/TestDataAndOperations.groovy index 789aedfb..e9776dac 100644 --- a/test/functional/steps/TestDataAndOperations.groovy +++ b/test/functional/steps/TestDataAndOperations.groovy @@ -54,7 +54,7 @@ class TestDataAndOperations { BibtexFile bibtexFile = bibtexFileController.transform(new File(path)) } - /* static public def findResearchLineByName(String name) { + /* static public def findResearchLineByNameOrientation(String name) { researchLines.find { researchLine -> researchLine.name == name } @@ -192,7 +192,7 @@ class TestDataAndOperations { /* static public void createResearchLine(String name) { def cont = new ResearchLineController() - def research = TestDataAndOperations.findResearchLineByName(name) + def research = TestDataAndOperations.findResearchLineByNameOrientation(name) cont.params.name = research.name cont.params.description = research.description cont.request.setContent(new byte[1000]) // Could also vary the request content. @@ -205,7 +205,7 @@ class TestDataAndOperations { >>>>>>> HEAD~5 def inserted = ResearchLine.findByName(name) if (!inserted) { - //def research = TestDataAndOperations.findResearchLineByName(name) + //def research = TestDataAndOperations.findResearchLineByNameOrientation(name) ResearchLine rl = new ResearchLine() rl.setName(name) rl.setDescription(description) diff --git a/test/functional/steps/TestDataAndOperationsResearchLine.groovy b/test/functional/steps/TestDataAndOperationsResearchLine.groovy deleted file mode 100644 index 4cabd5d3..00000000 --- a/test/functional/steps/TestDataAndOperationsResearchLine.groovy +++ /dev/null @@ -1,77 +0,0 @@ -package steps - -import rgms.publication.ResearchLine -import rgms.publication.ResearchLineController - -/** - * Created with IntelliJ IDEA. - * User: Flavia - * Date: 29/08/13 - * Time: 00:46 - * To change this template use File | Settings | File Templates. - */ -class TestDataAndOperationsResearchLine { - static researchLines = [ - [name: "IA Avancada", description: ""], - [name: "Redes Avancadas", description: "Redes de Computadores Avancadas"], - [name: "Teoria da informacao - Complexidade no espaco", description: "P=NP"], - [name: "Novo Padrao Arquitetural MVCE", description: "Nova arquitetura que promete revolucionar a web"], - [name: "Modelo Cascata Renovado", description: "Altera��o do modelo original"] - ] - - static public def insertsResearchLine(String name, description) { - def inserted = ResearchLine.findByName(name) - if (!inserted) { - // def research = TestDataAndOperationsResearchLine.findResearchLineByName(name) - ResearchLine rl = new ResearchLine() - rl.setName(name) - rl.setDescription(description) - rl.save() - } - - } - - static public def findResearchLineByName(String name) { - researchLines.find { researchLine -> - researchLine.name == name - } - } - - static public void deleteResearchLine(def id) { - def res = new ResearchLineController() - res.params.id = id - res.request.setContent(new byte[1000]) // Could also vary the request content. - res.delete() - res.response.reset() - } - - static public void updateResearchLine(String name, String description) { - def res = new ResearchLineController() - def research_line = ResearchLine.findByName(name) - res.params.id = research_line.id - res.params.name = research_line.name - res.params.description = description - res.request.setContent(new byte[1000]) // Could also vary the request content. - res.update() - res.response.reset() - } - - static public void createResearchLine(String name) { - def cont = new ResearchLineController() - def research = TestDataAndOperationsResearchLine.findResearchLineByName(name) - cont.params.name = research.name - cont.params.description = research.description - cont.request.setContent(new byte[1000]) // Could also vary the request content. - cont.create() - cont.save() - cont.response.reset() - } - - static public void listAllResearchLine(){ - def cont2 = new ResearchLineController() - cont2.request.setContent(new byte[1000]) // Could also vary the request content. - cont2.findAllResearchLine() - cont2.response.reset() - } - -} diff --git a/test/functional/steps/TestDataAndOperationsVisit.groovy b/test/functional/steps/VisitTestDataAndOperations.groovy similarity index 99% rename from test/functional/steps/TestDataAndOperationsVisit.groovy rename to test/functional/steps/VisitTestDataAndOperations.groovy index ccc72389..7fb6994a 100644 --- a/test/functional/steps/TestDataAndOperationsVisit.groovy +++ b/test/functional/steps/VisitTestDataAndOperations.groovy @@ -4,7 +4,7 @@ import rgms.visit.Visit import rgms.visit.VisitController import rgms.visit.Visitor -class TestDataAndOperationsVisit { +class VisitTestDataAndOperations { //#if ( $visit ) From 85fa25520a4c1f4dfe0cb145a8698aa27c0a0818 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Mon, 4 Aug 2014 22:28:57 -0300 Subject: [PATCH 38/54] =?UTF-8?q?Ajuste=20por=20conta=20de=20refatora?= =?UTF-8?q?=C3=A7=C3=A3o=20realizada=20por=20Kamilla:=20exclus=C3=A3o=20de?= =?UTF-8?q?=20comandos=20import=20inv=C3=A1lidos.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/ArticleSteps.groovy | 2 -- test/cucumber/steps/FerramentaSteps.groovy | 1 - test/cucumber/steps/ResearchLineSteps.groovy | 1 - 3 files changed, 4 deletions(-) diff --git a/test/cucumber/steps/ArticleSteps.groovy b/test/cucumber/steps/ArticleSteps.groovy index a9ae6c09..1abdf1f0 100644 --- a/test/cucumber/steps/ArticleSteps.groovy +++ b/test/cucumber/steps/ArticleSteps.groovy @@ -8,8 +8,6 @@ import pages.PublicationsPage import rgms.publication.Periodico import rgms.tool.TwitterTool import steps.ArticleTestDataAndOperations -import steps.TestDataAndOperationsFacebook -import steps.TestDataAndOperationsPublication import static cucumber.api.groovy.EN.* diff --git a/test/cucumber/steps/FerramentaSteps.groovy b/test/cucumber/steps/FerramentaSteps.groovy index cc1b8a05..3afba41a 100644 --- a/test/cucumber/steps/FerramentaSteps.groovy +++ b/test/cucumber/steps/FerramentaSteps.groovy @@ -2,7 +2,6 @@ import pages.ferramenta.* import rgms.publication.Ferramenta import steps.FerramentaTestDataAndOperations import steps.PublicationTestDataAndOperations -import steps.TestDataAndOperationsPublication import static cucumber.api.groovy.EN.* diff --git a/test/cucumber/steps/ResearchLineSteps.groovy b/test/cucumber/steps/ResearchLineSteps.groovy index bf4a3c8c..2c7eacc6 100644 --- a/test/cucumber/steps/ResearchLineSteps.groovy +++ b/test/cucumber/steps/ResearchLineSteps.groovy @@ -1,7 +1,6 @@ import rgms.publication.ResearchLine import steps.ResearchLineTestDataAndOperations import steps.TestDataAndOperations -import steps.TestDataAndOperationsResearchLine import pages.ResearchLinePages.ResearchLineCreatePage import pages.ResearchLinePages.ResearchLinePage import pages.LoginPage From bf9c56046b676ef1f765bdecebc5ac54e917d8dc Mon Sep 17 00:00:00 2001 From: thaisabr Date: Mon, 4 Aug 2014 22:34:51 -0300 Subject: [PATCH 39/54] =?UTF-8?q?Refatora=C3=A7=C3=A3o:=20Cria=C3=A7=C3=A3?= =?UTF-8?q?o=20de=20m=C3=A9todo=20para=20encapsular=20trechos=20de=20c?= =?UTF-8?q?=C3=B3digo=20repetidos;=20quebra=20de=20m=C3=A9todo=20em=20m?= =?UTF-8?q?=C3=A9todos=20menores=20para=20facilitar=20o=20entendimento.=20?= =?UTF-8?q?Corre=C3=A7=C3=A3o=20de=20problema=20na=20verifica=C3=A7=C3=A3o?= =?UTF-8?q?=20de=20total=20de=20publica=C3=A7=C3=B5es=20existentes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/XMLImportSteps.groovy | 66 ++++++------------- .../XMLImportTestDataAndOperations.groovy | 22 +++++-- 2 files changed, 36 insertions(+), 52 deletions(-) diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index bca46927..9ccf40f4 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -32,9 +32,10 @@ int researchProjectsTotal Given(~'^the system has some publications stored$') { -> TestDataAndOperations.loginController(this,user) + publicationsTotal = Publication.findAll().size() XMLImportTestDataAndOperations.initializePublicationDB() - publicationsTotal = 4 - assert Publication.findAll().size() == publicationsTotal + assert Publication.findAll().size() == publicationsTotal+4 + publicationsTotal = Publication.findAll().size() } Given(~'^the system has no journal article entitled "([^"]*)" with journal "([^"]*)" authored by me$'){ pubName, journalName -> @@ -45,12 +46,8 @@ When(~'^I upload the file "([^"]*)" that contains a journal article entitled "([ String path = XMLImportTestDataAndOperations.configureFileName(filename) assert XMLImportTestDataAndOperations.fileContainsJournal(path, authorName, pubName, journalName) - File importedFile = new File("xmlimported.xml") - importedFile.setText("") - assert importedFile.length()==0 xmlController = new XMLController() - XMLImportTestDataAndOperations.uploadPublications(xmlController,path) - assert importedFile.length()>0 + assert XMLImportTestDataAndOperations.uploadXmlFile(xmlController, path) } Then(~'^no new publication is stored by the system$'){ -> @@ -72,10 +69,8 @@ Then(~'^the previously stored publications do not change$'){ -> assert ArticleTestDataAndOperations.compatibleTo(journal2, journal2.title) //caso do sistema já ter um dado periodico - if(publicationsTotal == 5){ - def journal3 = Periodico.findByTitle(ArticleTestDataAndOperations.articles[6].title) - assert ArticleTestDataAndOperations.compatibleTo(journal3, journal3.title) - } + def journal3 = Periodico.findByTitle(ArticleTestDataAndOperations.articles[6].title) + if(journal3) assert ArticleTestDataAndOperations.compatibleTo(journal3, journal3.title) } Then(~'^the system outputs a list of imported publications that contains the journal article entitled "([^"]*)" with status "([^"]*)"$'){ pubName, status -> @@ -88,7 +83,7 @@ Given(~'^the system has a journal article entitled "([^"]*)" with journal "([^"] TestDataAndOperations.loginController(this, user) XMLImportTestDataAndOperations.initializePublicationDB() XMLImportTestDataAndOperations.addJournalPublication(pubName, journalName) - publicationsTotal = 5 + publicationsTotal = Publication.findAll().size() assert Periodico.findByTitleAndJournal(pubName, journalName).authors.contains(authorName) } @@ -97,12 +92,8 @@ When(~'^I upload the file "([^"]*)" that contains a conference article entitled assert XMLImportTestDataAndOperations.fileContainsConference(path, authorName, pubName, confName) assert !Conferencia.findByBooktitleAndTitle(pubName, confName)?.authors?.contains(authorName) - File importedFile = new File("xmlimported.xml") - importedFile.setText("") - assert importedFile.length()==0 xmlController = new XMLController() - XMLImportTestDataAndOperations.uploadPublications(xmlController,path) - assert importedFile.length()>0 + assert XMLImportTestDataAndOperations.uploadXmlFile(xmlController, path) } Then(~'^the system outputs a list of imported publications that contains the conference article entitled "([^"]*)" with status "([^"]*)"$'){ pubName, status -> @@ -118,12 +109,8 @@ When(~'^I upload the file "([^"]*)" that also contains a journal article entitle Periodico dbPub = ArticleTestDataAndOperations.findArticleByTitleAndAuthor(pubName, authorName) assert ArticleTestDataAndOperations.compatibleTo(dbPub, pubName) - File importedFile = new File("xmlimported.xml") - importedFile.setText("") - assert importedFile.length()==0 xmlController = new XMLController() - XMLImportTestDataAndOperations.uploadPublications(xmlController, path) - assert importedFile.length()>0 + assert XMLImportTestDataAndOperations.uploadXmlFile(xmlController, path) } Then(~'^the system outputs a list of imported publications that does not contain the journal article entitled "([^"]*)"$'){ pubName -> @@ -136,7 +123,7 @@ Given(~'^the system has a journal article entitled "([^"]*)" with journal "([^"] TestDataAndOperations.loginController(this, user) XMLImportTestDataAndOperations.initializePublicationDB() XMLImportTestDataAndOperations.addJournalPublication(pubName, journalName) - publicationsTotal = 5 + publicationsTotal = Publication.findAll().size() def journal = Periodico.findByTitleAndJournal(pubName, journalName) assert journal.pages == pages assert journal.authors.contains(authorName) @@ -147,12 +134,8 @@ When(~'^I upload the file "([^"]*)" that contains a journal article entitled "([ String path = XMLImportTestDataAndOperations.configureFileName(filename) assert XMLImportTestDataAndOperations.fileContainsJournalWithPages(path, authorName, pubName, journalName, pages) - File importedFile = new File("xmlimported.xml") - importedFile.setText("") - assert importedFile.length()==0 xmlController = new XMLController() - XMLImportTestDataAndOperations.uploadPublications(xmlController,path) - assert importedFile.length()>0 + assert XMLImportTestDataAndOperations.uploadXmlFile(xmlController, path) } When(~'^I click on "([^"]*)" at the "([^"]*)" Page without selecting a xml file$'){ option, xmlPage -> @@ -169,21 +152,18 @@ Then(~'^the system outputs an error message$') { -> //#if ($ResearchProject) Given(~'^the system has some research projects stored$'){ -> TestDataAndOperations.loginController(this, user) + researchProjectsTotal = ResearchProject.findAll().size() XMLImportTestDataAndOperations.initializeResearchProjectDB() - researchProjectsTotal = 2 - assert ResearchProject.findAll().size() == researchProjectsTotal + assert ResearchProject.findAll().size() == researchProjectsTotal+2 + researchProjectsTotal = ResearchProject.findAll().size() } When(~'^I upload the file "([^"]*)" that contains a research project named as "([^"]*)"$'){ filename, projectName -> String path = XMLImportTestDataAndOperations.configureFileName(filename) assert XMLImportTestDataAndOperations.fileContainsResearchProject(path, projectName, authorName) - File importedFile = new File("xmlimported.xml") - importedFile.setText("") - assert importedFile.length()==0 xmlController = new XMLController() - XMLImportTestDataAndOperations.uploadPublications(xmlController,path) - assert importedFile.length()>0 + assert XMLImportTestDataAndOperations.uploadXmlFile(xmlController, path) } Then(~'^no new research project is stored by the system$'){ -> @@ -207,7 +187,7 @@ Then(~'^the system outputs a list of imported research projects that contains th Given(~'^the system has a research project named as "([^"]*)", among several research projects$'){ projectName -> TestDataAndOperations.loginController(this, user) XMLImportTestDataAndOperations.initializeResearchProjectDB() - researchProjectsTotal = 2 + researchProjectsTotal = ResearchProject.findAll().size() def project = ResearchProject.findByProjectName(projectName) assert project.members.contains(authorName) || project.responsible==authorName } @@ -217,12 +197,8 @@ When(~'^I upload the file "([^"]*)" that also contains a research project named String path = XMLImportTestDataAndOperations.configureFileName(filename) assert XMLImportTestDataAndOperations.fileContainsResearchProject(path, projectName, authorName) - File importedFile = new File("xmlimported.xml") - importedFile.setText("") - assert importedFile.length()==0 xmlController = new XMLController() - XMLImportTestDataAndOperations.uploadPublications(xmlController,path) - assert importedFile.length()>0 + assert XMLImportTestDataAndOperations.uploadXmlFile(xmlController, path) } Then(~'^the system outputs a list of imported research projects that does not contain the one named as "([^"]*)"$'){ projectName -> @@ -235,7 +211,7 @@ Given(~'^the system has a research project named as "([^"]*)" with status "([^"] projectName, projectStatus -> TestDataAndOperations.loginController(this, user) XMLImportTestDataAndOperations.initializeResearchProjectDB() - researchProjectsTotal = 2 + researchProjectsTotal = ResearchProject.findAll().size() def project = ResearchProject.findByProjectNameAndStatus(projectName, projectStatus) assert project.members.contains(authorName) || project.responsible==authorName } @@ -245,12 +221,8 @@ When(~'^I upload the file "([^"]*)" that also contains a research project named String path = XMLImportTestDataAndOperations.configureFileName(filename) assert XMLImportTestDataAndOperations.fileContainsResearchProjectWithStatus(path, projectName, authorName, projectStatus) - File importedFile = new File("xmlimported.xml") - importedFile.setText("") - assert importedFile.length()==0 xmlController = new XMLController() - XMLImportTestDataAndOperations.uploadPublications(xmlController,path) - assert importedFile.length()>0 + assert XMLImportTestDataAndOperations.uploadXmlFile(xmlController, path) } //#end diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index ad6a6d7e..918e3f2d 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -166,7 +166,7 @@ class XMLImportTestDataAndOperations { List specificResearchs = fileName.depthFirst().findAll{ it.name() == 'TITULO-DA-LINHA-DE-PESQUISA' } for (research in specificResearchs) { if(research.equals(researchLineName)){ - TestDataAndOperationsResearchLine.createResearchLine(researchLineName) + ResearchLineTestDataAndOperations.createResearchLine(researchLineName) return true } } @@ -235,25 +235,37 @@ class XMLImportTestDataAndOperations { //#end static getUser(){ + def adminRole = createAdminRole() + createUser(adminRole) + return User.findByUsername('paulo')?.author?.name + } + + private static createAdminRole(){ def adminRole = Role.findByName("Administrator") if (!adminRole) { adminRole = new Role(name: 'Administrator') adminRole.addToPermissions("*:*") adminRole.save() } + return adminRole + } + private static createUser(adminRole){ def user = new User(username: 'paulo', passwordHash: new Sha256Hash("paulo").toHex(),enabled: true) def member = new Member(name: "Paulo Henrique Monteiro Borba",email: "phmb@cin.ufpe.br", status: "Professor", university: "UFPE") - adminRole.addToUsers(user) adminRole.save() - member.save() - user.author = member user.save() - return User.findByUsername('paulo')?.author?.name + } + + static uploadXmlFile(xmlController, path){ + File importedFile = new File("xmlimported.xml") + importedFile.setText("") + XMLImportTestDataAndOperations.uploadPublications(xmlController,path) + importedFile.length()>0 } } From c5293f93d55945e75a5184878f5a42e0c0eca407 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Mon, 4 Aug 2014 22:36:05 -0300 Subject: [PATCH 40/54] =?UTF-8?q?Corre=C3=A7=C3=A3o=20em=20refatora=C3=A7?= =?UTF-8?q?=C3=A3o=20realizada=20por=20Kamilla:=20comando=20import=20inv?= =?UTF-8?q?=C3=A1lido.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/ResearchLinePages/ResearchLineCreatePage.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/test/functional/pages/ResearchLinePages/ResearchLineCreatePage.groovy b/test/functional/pages/ResearchLinePages/ResearchLineCreatePage.groovy index d5dcc796..4c0a1cd8 100644 --- a/test/functional/pages/ResearchLinePages/ResearchLineCreatePage.groovy +++ b/test/functional/pages/ResearchLinePages/ResearchLineCreatePage.groovy @@ -3,7 +3,6 @@ package pages.ResearchLinePages import geb.Page import steps.ResearchGroupTestDataAndOperations import steps.ResearchLineTestDataAndOperations -import steps.TestDataAndOperationsResearchLine class ResearchLineCreatePage extends Page { From feb97ae1952658e9c702862b01841b657b4a45ff Mon Sep 17 00:00:00 2001 From: thaisabr Date: Tue, 5 Aug 2014 00:00:57 -0300 Subject: [PATCH 41/54] =?UTF-8?q?Ajuste=20em=20refatora=C3=A7=C3=A3o=20rea?= =?UTF-8?q?lizada=20por=20Kamilla?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/ArticleSteps.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/cucumber/steps/ArticleSteps.groovy b/test/cucumber/steps/ArticleSteps.groovy index 1abdf1f0..f05b34ed 100644 --- a/test/cucumber/steps/ArticleSteps.groovy +++ b/test/cucumber/steps/ArticleSteps.groovy @@ -8,6 +8,8 @@ import pages.PublicationsPage import rgms.publication.Periodico import rgms.tool.TwitterTool import steps.ArticleTestDataAndOperations +import steps.FacebookTestDataAndOperations +import steps.PublicationTestDataAndOperations import static cucumber.api.groovy.EN.* From f288f39e285e4296dffd3d90e3f41efc8a2cc0b1 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Tue, 5 Aug 2014 01:11:26 -0300 Subject: [PATCH 42/54] =?UTF-8?q?Refatora=C3=A7=C3=A3o:=20Extra=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20m=C3=A9todo=20para=20evitar=20replica=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20c=C3=B3digo.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grails-app/services/rgms/XMLService.groovy | 83 +++++++--------------- 1 file changed, 26 insertions(+), 57 deletions(-) diff --git a/grails-app/services/rgms/XMLService.groovy b/grails-app/services/rgms/XMLService.groovy index d87dc9dc..d95a3c74 100644 --- a/grails-app/services/rgms/XMLService.groovy +++ b/grails-app/services/rgms/XMLService.groovy @@ -486,6 +486,29 @@ class XMLService { return status } + //#if($Orientation || $researchLine || $researchProject) + static checkItemStatus(itemDB, item){ + def status = PUB_STATUS_DUPLICATED + + def missingPropertiesDB = itemDB.properties.findAll{it.key != 'id' && !it.value}.keySet() + def missingProperties = item.properties.findAll{it.key != 'id' && !it.value}.keySet() + if(missingPropertiesDB != missingProperties){ + status = PUB_STATUS_TO_UPDATE + } + + def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) + def detailsPropertiesDB = itemDB.properties.findAll{!(it.key in missingPropertiesTotal)} + def detailsDB = detailsPropertiesDB.findAll{it.key!='id'} + def detailsProperties = item.properties.findAll{!(it.key in missingPropertiesTotal)} + def details = detailsProperties.findAll{it.key!='id'} + if(detailsDB != details){ + status = PUB_STATUS_CONFLICTED + } + + return status + } + //#end + static Node parseReceivedFile(MultipartHttpServletRequest request) { MultipartHttpServletRequest mpr = (MultipartHttpServletRequest) request; MultipartFile f = (MultipartFile) mpr.getFile("file"); @@ -575,25 +598,7 @@ class XMLService { if(!orientation) return null def status = PUB_STATUS_STABLE def orientationDB = Orientation.findByOrientadorAndTituloTese(user, orientation.tituloTese) - - if(orientationDB){ - status = PUB_STATUS_DUPLICATED - - def missingPropertiesDB = orientationDB.properties.findAll{it.key != 'id' && !it.value}.keySet() - def missingProperties = orientation.properties.findAll{it.key != 'id' && !it.value}.keySet() - if(missingPropertiesDB != missingProperties){ - status = PUB_STATUS_TO_UPDATE - } - - def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) - def detailsPropertiesDB = orientationDB.properties.findAll{!(it.key in missingPropertiesTotal)} - def detailsDB = detailsPropertiesDB.findAll{it.key!='id'} - def detailsProperties = orientation.properties.findAll{!(it.key in missingPropertiesTotal)} - def details = detailsProperties.findAll{it.key!='id'} - if(detailsDB != details){ - status = PUB_STATUS_CONFLICTED - } - } + if(orientationDB) status = checkItemStatus(orientationDB, orientation) return [status:status, id:orientationDB?.id] } @@ -648,25 +653,7 @@ class XMLService { if(!researchLine) return null def status = PUB_STATUS_STABLE def rlDB = ResearchLine.findByName(researchLine.name) - - if(rlDB){ - status = PUB_STATUS_DUPLICATED - - def missingPropertiesDB = rlDB.properties.findAll{it.key != 'id' && !it.value}.keySet() - def missingProperties = researchLine.properties.findAll{it.key != 'id' && !it.value}.keySet() - if(missingPropertiesDB != missingProperties){ - status = PUB_STATUS_TO_UPDATE - } - - def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) - def detailsPropertiesDB = rlDB.properties.findAll{!(it.key in missingPropertiesTotal)} - def detailsDB = detailsPropertiesDB.findAll{it.key!='id'} - def detailsProperties = researchLine.properties.findAll{!(it.key in missingPropertiesTotal)} - def details = detailsProperties.findAll{it.key!='id'} - if(detailsDB != details){ - status = PUB_STATUS_CONFLICTED - } - } + if(rlDB) status = checkItemStatus(rlDB, researchLine) return [status:status, id:rlDB?.id] } //#end @@ -695,25 +682,7 @@ class XMLService { if(!researchProject) return null def status = PUB_STATUS_STABLE def researchProjectDB = ResearchProject.findByProjectName(researchProject.projectName) - - if(researchProjectDB){ - status = PUB_STATUS_DUPLICATED - def missingPropertiesDB = researchProjectDB.properties.findAll{it.key != 'id' && !it.value}.keySet() - def missingProperties = researchProject.properties.findAll{it.key != 'id' && !it.value}.keySet() - if(missingPropertiesDB != missingProperties){ - status = PUB_STATUS_TO_UPDATE - } - - def missingPropertiesTotal = calculateTotalMissingProperties(missingPropertiesDB, missingProperties) - def detailsPropertiesDB = researchProjectDB.properties.findAll{!(it.key in missingPropertiesTotal)} - def detailsDB = detailsPropertiesDB.findAll{it.key!='id'} - def detailsProperties = researchProject.properties.findAll{!(it.key in missingPropertiesTotal)} - def details = detailsProperties.findAll{it.key!='id'} - if(detailsDB != details){ - status = PUB_STATUS_CONFLICTED - } - } - + if(researchProjectDB) status = checkItemStatus(researchProjectDB, researchProject) return [status:status, id:researchProjectDB?.id] } From f0223033c6f1ede9a86543f2cee021143ce6df38 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Tue, 5 Aug 2014 01:17:22 -0300 Subject: [PATCH 43/54] =?UTF-8?q?Ajuste=20em=20altera=C3=A7=C3=B5es=20real?= =?UTF-8?q?izadas=20por=20Kamilla.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/ArticleSteps.groovy | 273 ++++++++++++++++++++---- 1 file changed, 235 insertions(+), 38 deletions(-) diff --git a/test/cucumber/steps/ArticleSteps.groovy b/test/cucumber/steps/ArticleSteps.groovy index f05b34ed..1f4617c5 100644 --- a/test/cucumber/steps/ArticleSteps.groovy +++ b/test/cucumber/steps/ArticleSteps.groovy @@ -1,10 +1,5 @@ -//#if($Article) -import pages.ArticlePages.ArticleCreatePage -import pages.ArticlePages.ArticleEditPage -import pages.ArticlePages.ArticleShowPage -import pages.ArticlePages.ArticlesPage -import pages.LoginPage -import pages.PublicationsPage +import pages.ArticlePages.* +import pages.* import rgms.publication.Periodico import rgms.tool.TwitterTool import steps.ArticleTestDataAndOperations @@ -29,9 +24,7 @@ Then(~'^the article "([^"]*)" is properly stored by the system$') { String title When(~'^I create the article "([^"]*)" with file name "([^"]*)" with the "([^"]*)" field blank$') { String title, String filename, String field -> ArticleTestDataAndOperations.createArticle(title, filename) def article = ArticleTestDataAndOperations.findArticleByTitle(title) - assert article.{ - field - } == null + assert article.{field} == null } Then(~'^the article "([^"]*)" is not stored by the system because it is invalid$') { String title -> @@ -45,15 +38,16 @@ Then(~'^the article "([^"]*)" is not stored twice$') { String title -> // which is changed by the system during the file upload. } -When(~'^I select the new article option at the article page$') { -> +When(~'^I select the new article option at the article page$') {-> selectNewArticleInArticlesPage() } -Then(~'^I can fill the article details$') { -> +Then(~'^I can fill the article details$') {-> at ArticleCreatePage page.fillArticleDetails() } + /** * @author Guilherme */ @@ -101,7 +95,7 @@ When(~'^I edit the article title from "([^"]*)" to "([^"]*)"$') { String oldtitl /** * @author Guilherme */ -When(~"^I view the article list\$") { -> +When(~"^I view the article list\$") {-> articles = Periodico.findAll() assert articles != null } @@ -159,7 +153,7 @@ Then(~'my resulting articles list contains "([^"]*)"$') { String title -> * @author Guilherme */ -When(~'I select the option to remove in show page$') { -> +When(~'I select the option to remove in show page$') {-> at ArticleShowPage page.select('input', 'delete') } @@ -178,13 +172,14 @@ Then(~'^I am at Article show page$') { -> at ArticleShowPage } + //#if( $Twitter ) Given(~'^I am logged as "([^"]*)"$') { String userName -> to LoginPage at LoginPage page.fillLoginData(userName, "adminadmin") } -Given(~'^I am at the Article Page$') { -> +Given (~'^I am at the Article Page$'){-> addPage() } @@ -205,7 +200,7 @@ Then(~'^A tweet is added to my twitter account regarding the new article "([^"]* assert TwitterTool.consult(articleTitle) } -Then(~'No tweet should be post about "([^"]*)"$') { String article -> +Then (~'No tweet should be post about "([^"]*)"$'){ String article -> assert !TwitterTool.consult(null) } @@ -213,7 +208,6 @@ Then(~'No tweet should be post about "([^"]*)"$') { String article -> Then(~'^No twitte should be post about "([^"]*)"$') { String articleTitle -> assert !TwitterTool.consult(articleTitle) } - //#end //#if( $Facebook ) @@ -235,7 +229,7 @@ Then(~'^No facebook message was posted$') { -> assert true } -Given(~'^I am at the Add Article Page$') { -> +Given(~'^I am at the Add Article Page$') { -> at PublicationsPage page.select("Periodico") to ArticlesPage @@ -249,10 +243,9 @@ Given(~'^I am at the Add Article Page$') { -> When(~'^I share the article entitled "([^"]*)" on facebook$') { String title -> FacebookTestDataAndOperations.ShareArticleOnFacebook(title) } - //#end -def selectNewArticleInArticlesPage() { +def selectNewArticleInArticlesPage(){ at ArticlesPage page.selectNewArticle() @@ -260,24 +253,20 @@ def selectNewArticleInArticlesPage() { } -def periodicoNoExist(String title) { +def periodicoNoExist(String title){ return Periodico.findByTitle(title) == null } -def Login() { - +def Login(){ to LoginPage at LoginPage - page.fillLoginData("admin", "adminadmin") - - } /** * @author carloscemb */ -Then(~'^I see my user listed as an author member of article by default$') { -> +Then(~'^I see my user listed as an author member of article by default$') {-> at ArticleCreatePage assert PublicationTestDataAndOperations.containsUser(page.selectedMembers()) } @@ -290,7 +279,7 @@ When(~'^I upload the articles of "([^"]*)"$') { String arg1 -> finalSize = Periodico.findAll().size() assert initialSize < finalSize } -Then(~'^the system has all the articles of the xml file$') { -> +Then(~'^the system has all the articles of the xml file$') {-> assert Periodico.findByTitle("A System For Translating Executable VDM Specifications Into Lazy ML") != null assert Periodico.findByTitle("From VDM Specifications To Functional Prototypes") != null assert Periodico.findByTitle("Basic laws of ROOL: an object-oriented language") != null @@ -315,35 +304,153 @@ Then(~'^the system has all the articles of the xml file$') { -> assert Periodico.findByTitle("A Theory of Software Product Line Refinement") != null assert Periodico.findByTitle("The Crosscutting Impact of the AOSD Brazilian Research Community (accepted)") != null } -And(~'^I select the upload button at the article page$') { -> +And(~'^I select the upload button at the article page$') {-> at ArticlesPage page.uploadWithoutFile() } -Then(~'^I\'m still on article page$') { -> +Then(~'^I\'m still on article page$') {-> at ArticlesPage } -And(~'^the articles are not stored by the system$') { -> +And(~'^the articles are not stored by the system$') {-> at ArticlesPage page.checkIfArticlesListIsEmpty() } -Given(~'^the system has some articles stored$') { -> +Given(~'^the system has some articles stored$') {-> initialSize = Periodico.findAll().size() } + +Given(~'^I am at the articles page$'){-> + Login() + at PublicationsPage + page.select("Periodico") + at ArticlesPage +} + +And(~'^the article "([^"]*)" is stored in the system with file name "([^"]*)"$') { String title, filename -> + at ArticlesPage + page.selectNewArticle() + at ArticleCreatePage + page.fillArticleDetails(ArticleTestDataAndOperations.path() + filename, title) + page.selectCreateArticle() + assert !periodicoNoExist(title) +} + //#if($Report) -When(~"^I view the report of articles\$") { -> - articles = Periodico.findAll() +When(~'^the system reports the existing articles$') {-> + articles = ArticleTestDataAndOperations.reportArticles() assert articles != null } -Then(~'the report of articles contains "([^"]*)"$') { String title -> - articles = Periodico.findAll() +Then(~'^the system report contains "([^"]*)" article$') { String title -> + articles = ArticleTestDataAndOperations.reportArticles() assert ArticleTestDataAndOperations.containsArticle(title, articles) } + +When(~'^I select to view the report of articles$') {-> + to ArticlesPage + at ArticlesPage + page.selectViewReports() +} + +Then(~'^my resulting report of articles contains "([^"]*)"$') { String title -> + at ArticlesReportPage + page.checkArticleAtReport(title,0) +} //#end +Given(~'^the system has article entitled "([^"]*)" with file name "([^"]*)" dated on "([^"]*)"$'){String title, filename, date-> + ArticleTestDataAndOperations.createArticle(title, filename, date, null) + assert Periodico.findByTitle(title) != null +} + +When(~'^the system orders the article list by title$') {-> + articlesSorted = Periodico.listOrderByTitle(order: "asc") + assert ArticleTestDataAndOperations.isSorted(articlesSorted, "title") + +} + +When(~'^the system orders the article list by publication date$') {-> + articlesSorted = Periodico.listOrderByPublicationDate(order: "asc") + assert ArticleTestDataAndOperations.isSorted(articlesSorted, "publication date") +} + +Then(~'^the system article list content is not modified$') {-> + assert Periodico.findAll().size() == 2 + assert !periodicoNoExist('Modularity analysis of use case implementations') + assert !periodicoNoExist('A theory of software product line refinement') +} + +Given(~'^the system has some articles created$'){-> + at ArticlesPage + page.selectNewArticle() + at ArticleCreatePage + page.fillArticleDetails(ArticleTestDataAndOperations.path() + 'MACI.pdf', 'Modularity analysis of use case implementations',"17","10","2013") + page.selectCreateArticle() + assert !periodicoNoExist('Modularity analysis of use case implementations') + to ArticlesPage + page.selectNewArticle() + at ArticleCreatePage + page.fillArticleDetails(ArticleTestDataAndOperations.path() + 'TCS-1401.pdf', 'A theory of software product line refinement',"12","11","2012") + page.selectCreateArticle() + assert !periodicoNoExist('A theory of software product line refinement') + to ArticlesPage +} + +When(~'^I select to view the list of articles$') {-> + at ArticlesPage + page.selectViewArticleList() +} + +And(~'^I select to order the list of articles by "([^"]*)"$') {String sortType-> + at ArticlesPage + page.selectOrderBy(sortType) +} + +Then(~'^my article list shows the articles ordered by "([^"]*)"$') {String sortType-> + at ArticlesPage + page.checkOrderedBy(sortType); +} + +Given(~'^the system has some articles authored by "([^"]*)"$'){String authorName-> + ArticleTestDataAndOperations.createArticle('A theory of software product line refinement', 'TCSOS.pdf', null, 'Paulo Borba') + ArticleTestDataAndOperations.createArticle('Modularity analysis of use case implementations', 'MACI.pdf') + assert (!periodicoNoExist('A theory of software product line refinement') && !periodicoNoExist('Modularity analysis of use case implementations')) +} + +When(~'^the system filter the articles authored by author "([^"]*)"$') {String authorName-> + articlesFiltered = ArticleTestDataAndOperations.findAllByAuthor(authorName) + assert ArticleTestDataAndOperations.isFiltered(articlesFiltered,authorName) +} + +And(~'^I create some articles authored by "([^"]*)"$') {String authorName-> + at ArticlesPage + page.selectNewArticle() + at ArticleCreatePage + page.fillArticleDetails(ArticleTestDataAndOperations.path() + 'MACI.pdf', 'Modularity analysis of use case implementations', authorName) + page.selectCreateArticle() + assert !periodicoNoExist('Modularity analysis of use case implementations') + to ArticlesPage + page.selectNewArticle() + at ArticleCreatePage + page.fillArticleDetails(ArticleTestDataAndOperations.path() + 'TCS-1401.pdf', 'A theory of software product line refinement') + page.selectCreateArticle() + assert !periodicoNoExist('A theory of software product line refinement') + to ArticlesPage +} + +And(~'^I select to filter the list of articles by author "([^"]*)"$') {String authorName-> + at ArticlesPage + page.fillAndSelectFilter(authorName) +} + +Then(~'^my article list shows only the articles authored by "([^"]*)"$') {String authorName-> + at ArticlesPage + assert page.checkFilteredBy(authorName); +} + //Funcoes Auxiliares -def addPage() { +def addPage(){ Login() at PublicationsPage page.select("Periodico") @@ -353,4 +460,94 @@ def addPage() { def f = new File(path) println "exist Path?" + f.exists() } -//#end \ No newline at end of file + + +def addNewArticleWeb(title, filename){ + selectNewArticleInArticlesPage() + page.fillArticleDetails(ArticleTestDataAndOperations.path() + filename, title) + page.selectCreateArticle() + assert !periodicoNoExist(title) + to ArticlesPage + at ArticlesPage +} + +Given(~'^the system has 3 articles entitled "([^"]*)" with file name "([^"]*)", "([^"]*)" with file name "([^"]*)" and "([^"]*)" with file name "([^"]*)"$') {String title1, filename1, title2, filename2, title3, filename3 -> + ArticleTestDataAndOperations.createArticle(title1, filename1,null,null) + ArticleTestDataAndOperations.createArticle(title2, filename2,null,null) + ArticleTestDataAndOperations.createArticle(title3, filename3,null,null) + assert Periodico.findByTitle(title1) != null + assert Periodico.findByTitle(title2) != null + assert Periodico.findByTitle(title3) != null +} + +When(~'^I remove the articles "([^"]*)" and "([^"]*)"$') { String title1, title2 -> + + ArticleTestDataAndOperations.removeMultiplesArticles(title1, title2) + + def testDeleteArticle1 = Periodico.findByTitle(title1) + def testDeleteArticle2 = Periodico.findByTitle(title2) + assert testDeleteArticle1 == null + assert testDeleteArticle2 == null +} + +Then(~'^the system removes the articles "([^"]*)" and "([^"]*)"$') { String title1, title2 -> + assert periodicoNoExist(title1) + assert periodicoNoExist(title2) +} + +And(~'^the system contains the "([^"]*)" article$') { String title1 -> + assert Periodico.findByTitle(title1) != null +} + +And(~'^I create 3 articles entitled "([^"]*)" with file name "([^"]*)", "([^"]*)" with file name "([^"]*)" and "([^"]*)" with file name "([^"]*)"$') { String title1, filename1, title2, filename2, title3, filename3 -> + addNewArticleWeb(title1, filename1) + addNewArticleWeb(title2, filename2) + addNewArticleWeb(title3, filename3) +} + +And(~'I select to remove the selected articles$') {-> + at ArticlesPage + page.selectRemoveMultipleArticles() +} + +Given(~'^I am at the new article page$'){-> + Login() + at PublicationsPage + page.select("Periodico") + selectNewArticleInArticlesPage() +} + +When(~'^I fill all article information except the title field$') {-> + at ArticleCreatePage + page.fillArticleDetailsExceptTitle() +} + +And(~'^I select to create the article$') {-> + at ArticleCreatePage + page.selectCreateArticle() +} + +When(~'^I create the article with filename "([^"]*)" and with the title field blank$') {String filename -> + ArticleTestDataAndOperations.createArticleWithoutTitle(filename) + def article = ArticleTestDataAndOperations.findArticleByFilename(filename) + assert article.{title} == null +} + +Given(~'^the system has no article without title and with filename "([^"]*)"$') {String filename -> + def article = Periodico.findByFile(filename) + assert article == null +} + +Then(~'^the article with blank title and with filename "([^"]*)" field is not stored by the system$') {String filename -> + def article = Periodico.findByFile(filename) + assert article == null +} + +Then(~'^an error message is showed for the title field$') { -> + assert (page.readFlashMessage() != null) +} + +And(~'^I mark multiple articles to be removed$') {-> + at ArticlesPage + page.markArticles() +} \ No newline at end of file From db9229255fb1b479052ef0d1f480c73c6c0c8c29 Mon Sep 17 00:00:00 2001 From: thaisabr Date: Wed, 6 Aug 2014 02:56:06 -0300 Subject: [PATCH 44/54] =?UTF-8?q?Corre=C3=A7=C3=A3o=20em=20testes=20devido?= =?UTF-8?q?=20=C3=A0s=20mudan=C3=A7as=20realizadas=20em=20XMLController.gr?= =?UTF-8?q?oovy,=20TeseOrDissertacao.groovy=20e=20Ferramenta.groovy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/Dissertacao.feature | 2 ++ test/cucumber/Ferramenta.feature | 2 ++ test/cucumber/Orientation.feature | 2 ++ test/cucumber/steps/ArticleSteps.groovy | 2 +- test/cucumber/steps/BookChapterSteps.groovy | 3 ++- test/cucumber/steps/BookSteps.groovy | 3 ++- test/cucumber/steps/ConferenciaSteps.groovy | 2 +- test/cucumber/steps/DissertacaoSteps.groovy | 4 ++-- test/cucumber/steps/FerramentaSteps.groovy | 2 +- test/cucumber/steps/ResearchLineSteps.groovy | 4 ++-- test/functional/steps/ArticleTestDataAndOperations.groovy | 4 +++- test/functional/steps/BookChapterTestDataAndOperations.groovy | 4 +++- test/functional/steps/BookTestDataAndOperations.groovy | 4 +++- test/functional/steps/ConferenciaTestDataAndOperations.groovy | 4 +++- test/functional/steps/FerramentaTestDataAndOperations.groovy | 4 +++- .../functional/steps/ResearchLineTestDataAndOperations.groovy | 4 +++- test/functional/steps/TestDataDissertacao.groovy | 4 +++- 17 files changed, 38 insertions(+), 16 deletions(-) diff --git a/test/cucumber/Dissertacao.feature b/test/cucumber/Dissertacao.feature index 98a20dd2..030cf302 100644 --- a/test/cucumber/Dissertacao.feature +++ b/test/cucumber/Dissertacao.feature @@ -17,6 +17,8 @@ Feature: Dissertation Tests When I create the dissertation "Dissertation without school" with file name "Dissertationwithoutschool.txt" without school Then the system has no dissertation entitled "Dissertation without school" + #Esse cenário não mais se aplica porque address não mais é obrigatório, já que essa informação não existe no lattes + @ignore Scenario: new dissertation without address Given the system has no dissertation entitled "Dissertation without address" When I create the dissertation "Dissertation without address" with file name "Dissertationwithoutaddress.txt" without address diff --git a/test/cucumber/Ferramenta.feature b/test/cucumber/Ferramenta.feature index 1db4f31c..3399eed2 100644 --- a/test/cucumber/Ferramenta.feature +++ b/test/cucumber/Ferramenta.feature @@ -5,6 +5,8 @@ Feature: Ferramenta # Controller tests + #Esse cenário não mais se aplica porque website se tornou opcional (dado que a informação nem sempre está disponível no xml) + @ignore Scenario: new ferramenta without website Given the system has no ferramenta entitled "Target" When I create the ferramenta "Target" with file name "target.pdf" without its website diff --git a/test/cucumber/Orientation.feature b/test/cucumber/Orientation.feature index 6ca3f5e4..38c3de0e 100644 --- a/test/cucumber/Orientation.feature +++ b/test/cucumber/Orientation.feature @@ -66,6 +66,8 @@ Feature: orientations Then The orientation "Hexa2" is properly removed by the system #if ($XMLImport) + #O teste falha porque o usuário é admin e o nome do admin não bate com o nome do autor do lattes. + @ignore Scenario: upload orientation with a file Given the system has some orientations stored When I upload a new orientation "testelattes.xml" diff --git a/test/cucumber/steps/ArticleSteps.groovy b/test/cucumber/steps/ArticleSteps.groovy index 1f4617c5..ef2b2331 100644 --- a/test/cucumber/steps/ArticleSteps.groovy +++ b/test/cucumber/steps/ArticleSteps.groovy @@ -275,7 +275,7 @@ Then(~'^I see my user listed as an author member of article by default$') {-> When(~'^I upload the articles of "([^"]*)"$') { String arg1 -> String path = "test" + File.separator + "functional" + File.separator + "steps" + File.separator + arg1 initialSize = Periodico.findAll().size() - ArticleTestDataAndOperations.uploadArticle(path) + ArticleTestDataAndOperations.uploadArticle(path, this) finalSize = Periodico.findAll().size() assert initialSize < finalSize } diff --git a/test/cucumber/steps/BookChapterSteps.groovy b/test/cucumber/steps/BookChapterSteps.groovy index 0b0f7d33..19f4e1a8 100644 --- a/test/cucumber/steps/BookChapterSteps.groovy +++ b/test/cucumber/steps/BookChapterSteps.groovy @@ -6,6 +6,7 @@ import pages.* import rgms.publication.BookChapter import steps.BookChapterTestDataAndOperations import steps.PublicationTestDataAndOperations +import steps.TestDataAndOperations import static cucumber.api.groovy.EN.* @@ -128,7 +129,7 @@ Given(~'^the system has some book chapters stored$') { -> When(~'^I upload the book chapters of "([^"]*)"$') { filename -> String path = "test" + File.separator + "functional" + File.separator + "steps" + File.separator + filename initialSize = BookChapter.findAll().size() - BookChapterTestDataAndOperations.uploadBookChapter(path) + BookChapterTestDataAndOperations.uploadBookChapter(path, this) finalSize = BookChapter.findAll().size() assert initialSize < finalSize } diff --git a/test/cucumber/steps/BookSteps.groovy b/test/cucumber/steps/BookSteps.groovy index 45aae835..84303453 100644 --- a/test/cucumber/steps/BookSteps.groovy +++ b/test/cucumber/steps/BookSteps.groovy @@ -12,6 +12,7 @@ import pages.LoginPage import pages.PublicationsPage import rgms.publication.Book import steps.BookTestDataAndOperations +import steps.TestDataAndOperations import static cucumber.api.groovy.EN.* @@ -63,7 +64,7 @@ Given(~'^the system has no books stored$') { -> When(~'^I upload the books of "([^"]*)"$') { filename -> initialSize = Book.findAll().size() - BookTestDataAndOperations.uploadBook(filename) + BookTestDataAndOperations.uploadBook(filename, this) finalSize = Book.findAll().size() assert initialSize < finalSize } diff --git a/test/cucumber/steps/ConferenciaSteps.groovy b/test/cucumber/steps/ConferenciaSteps.groovy index 6656e603..1aab6a1d 100644 --- a/test/cucumber/steps/ConferenciaSteps.groovy +++ b/test/cucumber/steps/ConferenciaSteps.groovy @@ -122,7 +122,7 @@ Given(~'^the system has some conferencias stored$') {-> When(~'^I upload the conferencias of "([^"]*)"$') { filename -> String path = "test" + File.separator + "functional" + File.separator + "steps" + File.separator + filename initialSize = Conferencia.findAll().size() - ConferenciaTestDataAndOperations.uploadConferencias(path) + ConferenciaTestDataAndOperations.uploadConferencias(path, this) finalSize = Conferencia.findAll().size() assert initialSize < finalSize } diff --git a/test/cucumber/steps/DissertacaoSteps.groovy b/test/cucumber/steps/DissertacaoSteps.groovy index 3ee7dd85..124197f1 100644 --- a/test/cucumber/steps/DissertacaoSteps.groovy +++ b/test/cucumber/steps/DissertacaoSteps.groovy @@ -112,7 +112,7 @@ Then(~'^I\'m still on dissertation page$') {-> When(~'^I upload a new dissertation "([^"]*)"$') { filename -> String path = new File(".").getCanonicalPath() + File.separator + "test" + File.separator + "functional" + File.separator + "steps" + File.separator + filename inicialSize = Dissertacao.findAll().size() - TestDataDissertacao.uploadDissertacao(path) + TestDataDissertacao.uploadDissertacao(path, this) finalSize = Dissertacao.findAll().size() assert inicialSize < finalSize //para funcionar é necessario que tenha um FilePath válido @@ -133,7 +133,7 @@ Given(~'^the system has some dissertation stored$'){-> When(~'^I upload a new dissertation "([^"]*)" with title "([^"]*)"$') { filename, String title -> String path = new File(".").getCanonicalPath() + File.separator + "test" + File.separator + "functional" + File.separator + "steps" + File.separator + filename inicialSize = Dissertacao.findAll().size() - TestDataDissertacao.uploadDissertacao(path) + TestDataDissertacao.uploadDissertacao(path, this) finalSize = Dissertacao.findAll().size() assert inicialSize When(~'^I upload a new ferramenta "([^"]*)"$') { filename -> inicialSize = Ferramenta.findAll().size() def path = new File(".").getCanonicalPath() + File.separator + "test" + File.separator + "files" + File.separator - FerramentaTestDataAndOperations.uploadFerramenta(path + filename) + FerramentaTestDataAndOperations.uploadFerramenta(path + filename, this) finalSize = Ferramenta.findAll().size() assert inicialSize < finalSize //para funcionar é necessario que tenha um FilePath válido diff --git a/test/cucumber/steps/ResearchLineSteps.groovy b/test/cucumber/steps/ResearchLineSteps.groovy index 2c7eacc6..22af032f 100644 --- a/test/cucumber/steps/ResearchLineSteps.groovy +++ b/test/cucumber/steps/ResearchLineSteps.groovy @@ -114,10 +114,10 @@ Given(~'^the system has some research line stored$'){ -> When(~'^I upload new research lines from the file "([^"]*)"$') { filename -> def path = new File(".").getCanonicalPath() + File.separator + "test" + File.separator + "files" + File.separator - ResearchLineTestDataAndOperations.uploadResearchLine(path + filename) + ResearchLineTestDataAndOperations.uploadResearchLine(path + filename, this) } -Then(~'^the system has more reseach lines now$'){ -> +Then(~'^the system has more research lines now$'){ -> TestDataAndOperations.logoutController(this) finalSize = ResearchLine.findAll().size() assert (finalSize - initialSize) == 5 //If all researchlines was imported, we will have 5 more than before diff --git a/test/functional/steps/ArticleTestDataAndOperations.groovy b/test/functional/steps/ArticleTestDataAndOperations.groovy index ffa95554..b681cbd6 100644 --- a/test/functional/steps/ArticleTestDataAndOperations.groovy +++ b/test/functional/steps/ArticleTestDataAndOperations.groovy @@ -43,12 +43,14 @@ class ArticleTestDataAndOperations { } } - static public void uploadArticle(filename) { + static public void uploadArticle(filename, className) { + TestDataAndOperations.loginController(className) def cont = new XMLController() def xml = new File(filename); def records = new XmlParser() cont.saveJournals(records.parse(xml)) cont.response.reset() + TestDataAndOperations.logoutController(className) } static public boolean compatibleTo(article, title) { diff --git a/test/functional/steps/BookChapterTestDataAndOperations.groovy b/test/functional/steps/BookChapterTestDataAndOperations.groovy index f4a0a0c7..83f759c9 100644 --- a/test/functional/steps/BookChapterTestDataAndOperations.groovy +++ b/test/functional/steps/BookChapterTestDataAndOperations.groovy @@ -44,12 +44,14 @@ class BookChapterTestDataAndOperations { return compatible } - static public void uploadBookChapter(String filename) { + static public void uploadBookChapter(String filename, className) { + TestDataAndOperations.loginController(className) def cont = new XMLController() def xml = new File(filename); def records = new XmlParser() cont.saveBookChapters(records.parse(xml)); cont.response.reset() + TestDataAndOperations.logoutController(className) } static public void createBookChapter(String title, String filename) { diff --git a/test/functional/steps/BookTestDataAndOperations.groovy b/test/functional/steps/BookTestDataAndOperations.groovy index a6a8f379..f9fa7d63 100644 --- a/test/functional/steps/BookTestDataAndOperations.groovy +++ b/test/functional/steps/BookTestDataAndOperations.groovy @@ -54,13 +54,15 @@ class BookTestDataAndOperations { return updatedBook } - static public void uploadBook(String filename) { + static public void uploadBook(String filename, className) { + TestDataAndOperations.loginController(className) def cont = new XMLController() String path = "test" + File.separator + "functional" + File.separator + "steps" + File.separator + filename def xml = new File(path); def records = new XmlParser() cont.saveBook(records.parse(xml)); cont.response.reset() + TestDataAndOperations.logoutController(className) } static public boolean bookCompatibleTo(book, String title) { diff --git a/test/functional/steps/ConferenciaTestDataAndOperations.groovy b/test/functional/steps/ConferenciaTestDataAndOperations.groovy index d2d0da27..e2004510 100644 --- a/test/functional/steps/ConferenciaTestDataAndOperations.groovy +++ b/test/functional/steps/ConferenciaTestDataAndOperations.groovy @@ -62,12 +62,14 @@ class ConferenciaTestDataAndOperations { } } - static public void uploadConferencias(filename) { + static public void uploadConferencias(filename, className) { + TestDataAndOperations.loginController(className) def cont = new XMLController() def xml = new File(filename); def records = new XmlParser() cont.saveConferencias(records.parse(xml)); cont.response.reset() + TestDataAndOperations.logoutController(className) } } diff --git a/test/functional/steps/FerramentaTestDataAndOperations.groovy b/test/functional/steps/FerramentaTestDataAndOperations.groovy index a9f8285b..f09da2e8 100644 --- a/test/functional/steps/FerramentaTestDataAndOperations.groovy +++ b/test/functional/steps/FerramentaTestDataAndOperations.groovy @@ -62,12 +62,14 @@ class FerramentaTestDataAndOperations { cont.response.reset() } - static public void uploadFerramenta(filepath) { + static public void uploadFerramenta(filepath, className) { + TestDataAndOperations.loginController(className) def cont = new XMLController() def xml = new File((String) filepath); def records = new XmlParser() cont.saveTools(records.parse(xml)); cont.response.reset() + TestDataAndOperations.logoutController(className) } static public Ferramenta getFerramenta(title){ diff --git a/test/functional/steps/ResearchLineTestDataAndOperations.groovy b/test/functional/steps/ResearchLineTestDataAndOperations.groovy index e3b629f4..a6ed5adb 100644 --- a/test/functional/steps/ResearchLineTestDataAndOperations.groovy +++ b/test/functional/steps/ResearchLineTestDataAndOperations.groovy @@ -78,12 +78,14 @@ class ResearchLineTestDataAndOperations { } - static public void uploadResearchLine(filepath) { + static public void uploadResearchLine(filepath, className) { + TestDataAndOperations.loginController(className) def cont = new XMLController() def xml = new File((String) filepath); def records = new XmlParser() cont.saveResearchLine(records.parse(xml)); cont.response.reset() + TestDataAndOperations.logoutController(className) } static public void createResearchLine(int position){ diff --git a/test/functional/steps/TestDataDissertacao.groovy b/test/functional/steps/TestDataDissertacao.groovy index a7e034fa..b41e55d8 100644 --- a/test/functional/steps/TestDataDissertacao.groovy +++ b/test/functional/steps/TestDataDissertacao.groovy @@ -43,12 +43,14 @@ class TestDataDissertacao return updatedarticle } - static public void uploadDissertacao(filename) { + static public void uploadDissertacao(filename, className) { + TestDataAndOperations.loginController(className) def cont = new XMLController() def xml = new File(filename); def records = new XmlParser() cont.saveDissertations(records.parse(xml)); cont.response.reset() + TestDataAndOperations.logoutController(className) } static public void removeDissertacao(String title) { From 81a64ffa9230fbaecec23a60ee59b0fd43eb3d0f Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Wed, 6 Aug 2014 15:19:52 -0300 Subject: [PATCH 45/54] =?UTF-8?q?Corre=C3=A7=C3=A3o=20Metodo=20de=20Busca?= =?UTF-8?q?=20Linha=20de=20Pesquisa=20por=20Member?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nome do método teve que ser modificado para tornar a compreensão do código mais claro. --- test/cucumber/steps/XMLImportSteps.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index 9ccf40f4..38f7fd79 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -239,7 +239,7 @@ Given(~'^ the system has some research lines stored$'){ Given(~'^ the system has no research line named as "([^"]*)" associated with me $') { String nameOfResearch -> xmlImport = new ResearchLineController() def autor = xmlImport.findByActor(this) - List listaDeVerificacao = xmlImport.findResearchByActor(autor, nameOfResearch) + List listaDeVerificacao = xmlImport.findAllResearchByMember(autor, nameOfResearch) assert listaDeVerificacao.size() == 0 } @@ -281,7 +281,7 @@ Given(~'^ the system has some research lines stored $'){ Given(~'^ the system has no research line named as "([^"]*)" associated with me $'){ nameOfResearch -> xmlImport = new ResearchLineController() def autor = xmlImport.findByActor(this) - List listaDeVerificacao = xmlImport.findResearchByActor(autor, nameOfResearch) + List listaDeVerificacao = xmlImport.findAllResearchByMember(autor, nameOfResearch) assert listaDeVerificacao.size() == 0 } From 49b667d6179d9d96ad35cf4288fc10a6e0b447a7 Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Thu, 7 Aug 2014 03:23:17 -0300 Subject: [PATCH 46/54] =?UTF-8?q?Corre=C3=A7=C3=A3o:=20removendo=20redund?= =?UTF-8?q?=C3=A2ncia?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/XMLImportSteps.groovy | 3 +-- test/functional/pages/XMLImportPage.groovy | 6 ------ 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index 38f7fd79..b124db71 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -311,10 +311,9 @@ Then(~'^ the research line named as "([^"]*)" with status "([^"]*)" is removed f } Then(~'^ the previously stored research lines do not change $'){ - ResearchLineController controller = new XMLController() def lista = ResearchLine.findAll() - status = controller.statusChanged(lista) //se true é porque modificou, se false é porque nada foi modificado + status = controller.statusChanged(lista) assert status == false } //#end diff --git a/test/functional/pages/XMLImportPage.groovy b/test/functional/pages/XMLImportPage.groovy index a10bc77c..27cfbf6e 100644 --- a/test/functional/pages/XMLImportPage.groovy +++ b/test/functional/pages/XMLImportPage.groovy @@ -41,10 +41,4 @@ class XMLImportPage extends Page { gp.getMessage("não é possível transferir arquivos deste tipo") } } - - def invalidXML(){ - GetPageTitle gp = new GetPageTitle() - return gp.msg('default.xml.parserror.message') == $("div", class: "message", role: "status").text() - - } } From 5443388aba2c25b7ac74537774c4b102bb28f19f Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Thu, 7 Aug 2014 03:41:29 -0300 Subject: [PATCH 47/54] =?UTF-8?q?Corre=C3=A7=C3=A3o:=20Cen=C3=A1rio=20web,?= =?UTF-8?q?=20upload=20de=20arquivo=20XML=20invalido=20(upload=20de=20arqu?= =?UTF-8?q?ivo=20pdf)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/XMLImportSteps.groovy | 21 +++++++++++---------- test/functional/pages/XMLImportPage.groovy | 11 ++++++----- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index b124db71..7ac0734e 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -288,33 +288,33 @@ Given(~'^ the system has no research line named as "([^"]*)" associated with me Given(~'^the file "([^"]*)", which contains a research line named as "([^"]*)", is uploaded $'){ String theFile, researchLineName -> TestDataAndOperations.uploadPublications(theFile) status = XMLImportTestDataAndOperations.extractSpecificResearchLineFromFile(theFile, researchLineName) - assert status == true + assert status } When(~'^ I confirm the import of the research line named as "([^"]*)" with status "([^"]*)" $'){ String nameOfResearch, status -> ResearchLineController controller = new ResearchLineController() statusController = controller.checkSavedResearchByDescription(nameOfResearch, status) - assert statusController == true + assert statusController } Then(~'^ the research line named as "([^"]*)" is stored by the system $'){ String research -> list = ResearchLine.findAll() ResearchLineController controller = new ResearchLineController() statusSave = controller.checkIfResearchLineExists(research,list) - assert statusSave == true + assert statusSave } Then(~'^ the research line named as "([^"]*)" with status "([^"]*)" is removed from the list of imported research lines $'){ String nameOfResearch, status -> ResearchLineController cont = new ResearchLineController() check = cont.checkDeletedResearchByDescription(nameOfResearch, status) - assert check == true + assert check } Then(~'^ the previously stored research lines do not change $'){ ResearchLineController controller = new XMLController() def lista = ResearchLine.findAll() status = controller.statusChanged(lista) - assert status == false + assert !status } //#end @@ -330,7 +330,7 @@ When(~'^ I upload the file "([^"]*)" $') { String typeFile -> PublicationTestDataAndOperations.uploadPublication(typeFile) PublicationController controllerP = new PublicationController() status = controllerP.checkTypeFile(typeFile) - assert status == false + assert !status } Then(~'^ no publication is stored by the system $') { -> @@ -341,8 +341,8 @@ Then(~'^ no publication is stored by the system $') { -> Then(~'^ And previusly stored publications do not change $'){-> PublicationController contr = new PublicationController() def lista = Publication.findAll() - result = contr.statusChanged(lista) - assert result == false + resultChanged = contr.statusChanged(lista) + assert !resultChanged TestDataAndOperations.logoutController(this) } @@ -350,7 +350,7 @@ Then(~'^ And previusly stored publications do not change $'){-> Given(~'^I am at the "Import XML File" page$'){ -> to LoginPage at LoginPage - page.add("admin", "adminadmin") + page.fillLoginDataOnly("admin", "adminadmin") to XMLImportPage } @@ -366,7 +366,7 @@ When(~' I upload the file "([^"]*)"$'){ String file -> Then(~'^ the system outputs an error message$'){ -> at XMLImportPage - assert page.invalidXML() + assert page.hasErrorUploadFile() } Then(~'^ no new publication is stored by the system$'){ -> @@ -398,4 +398,5 @@ Then(~'^ no new publication is stored by the system$'){ -> Then(~'^ the previously stored publications do not change$'){ to XMLImportPage at XMLImportPage + assert page.uploadFile(new File('/test/files/cv.pdf')) } \ No newline at end of file diff --git a/test/functional/pages/XMLImportPage.groovy b/test/functional/pages/XMLImportPage.groovy index 27cfbf6e..a8ca7e58 100644 --- a/test/functional/pages/XMLImportPage.groovy +++ b/test/functional/pages/XMLImportPage.groovy @@ -33,12 +33,13 @@ class XMLImportPage extends Page { return gp.msg('default.xml.parserror.message') == $("div", class: "message", role: "status").text() } - def uploadFile(File file){ + def uploadFile(file){ GetPageTitle gp = new GetPageTitle() - if((file.canRead())&&(file.hasProperty(".xml"))){ - $('input.save').click() - }else{ - gp.getMessage("não é possível transferir arquivos deste tipo") + if(file.hasProperty("xml")){ + $('input[type=file]').value(file) + $('input[type=submit]').click() + gp.msg('default.xml.save.message') == $("div", class: "message", role: "status").text() } + gp.msg('default.xml.saveerror.message') == $("div", class: "message", role: "status").text() } } From 4827477f985b714d8d1cbda9c95193571d63a165 Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Thu, 7 Aug 2014 03:54:29 -0300 Subject: [PATCH 48/54] =?UTF-8?q?Corre=C3=A7=C3=A3o:=20metodo=20configureF?= =?UTF-8?q?ileName(filename),=20e=20aumentando=20o=20escopo=20de=20(Resear?= =?UTF-8?q?ch=20Line)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/XMLImportSteps.groovy | 4 +--- test/functional/steps/XMLImportTestDataAndOperations.groovy | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index 7ac0734e..a0cc7bab 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -268,10 +268,8 @@ Then(~'^ the previously stored research lines do not change $'){ ResearchLineController controller = new XMLController() def lista = ResearchLine.findAll() status = controller.statusChanged(lista) //se true é porque modificou, se false é porque nada foi modificado - assert status == false + assert !status } -//#end - Given(~'^ the system has some research lines stored $'){ ResearchLineTestDataAndOperations.createResearchLine(0) diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index 918e3f2d..04486700 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -23,6 +23,7 @@ class XMLImportTestDataAndOperations { static configureFileName(filename){ String path = "test" + File.separator + "files" + File.separator + filename + return path } static addJournalPublication(pubName, journalName){ From fb8bb715c893f9d275c7273e7df5126055d73368 Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Fri, 8 Aug 2014 01:53:12 -0300 Subject: [PATCH 49/54] =?UTF-8?q?Refatora=C3=A7=C3=A3o:=20nome=20m=C3=A9to?= =?UTF-8?q?do?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/rgms/publication/ResearchLineController.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grails-app/controllers/rgms/publication/ResearchLineController.groovy b/grails-app/controllers/rgms/publication/ResearchLineController.groovy index a79c56a2..6514ca1a 100644 --- a/grails-app/controllers/rgms/publication/ResearchLineController.groovy +++ b/grails-app/controllers/rgms/publication/ResearchLineController.groovy @@ -270,7 +270,7 @@ class ResearchLineController { listagem.put(member, research) } } - return listagem.size() + return listagem } def checkIfResearchLineExists(researchName, list){ From d8e1a06729cc9c92ddb17bdb4498185b462a52c8 Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Fri, 8 Aug 2014 02:02:25 -0300 Subject: [PATCH 50/54] =?UTF-8?q?Corre=C3=A7=C3=A3o=20da=20refatora=C3=A7?= =?UTF-8?q?=C3=A3o=20nome=20m=C3=A9todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/rgms/publication/ResearchLineController.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grails-app/controllers/rgms/publication/ResearchLineController.groovy b/grails-app/controllers/rgms/publication/ResearchLineController.groovy index 6514ca1a..8b167597 100644 --- a/grails-app/controllers/rgms/publication/ResearchLineController.groovy +++ b/grails-app/controllers/rgms/publication/ResearchLineController.groovy @@ -249,7 +249,7 @@ class ResearchLineController { [researchLineInstanceList: lista] } - def findByActor(member){ + def findResearchByMember(member){ ArrayList lista = new ArrayList() Member actor = new Member() for(research in ResearchLine.getAll()) From 52a2095d0fa12c137aea5b86e6613aac1725da96 Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Fri, 8 Aug 2014 02:12:38 -0300 Subject: [PATCH 51/54] =?UTF-8?q?Refatora=C3=A7=C3=A3o:=20removendo=20m?= =?UTF-8?q?=C3=A9todos=20redundantes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../publication/PublicationController.groovy | 16 ---------------- .../publication/ResearchLineController.groovy | 12 ------------ 2 files changed, 28 deletions(-) diff --git a/grails-app/controllers/rgms/publication/PublicationController.groovy b/grails-app/controllers/rgms/publication/PublicationController.groovy index 2c2d8b6a..fdce8b12 100644 --- a/grails-app/controllers/rgms/publication/PublicationController.groovy +++ b/grails-app/controllers/rgms/publication/PublicationController.groovy @@ -161,20 +161,4 @@ class PublicationController { // return statusLine.getStatusCode(); } //#end - - def static checkTypeFile(file){ - file.hasProperty(".xml") - } - - def statusChanged(def lista){ - def sizeList = Publication.findAll() - def sizeI = sizeList.size() - def sizeF = lista.size() - - def resultado = Math.max(sizeI, sizeF) - if(sizeF.compareTo(resultado)){ - return true - } - return false - } } diff --git a/grails-app/controllers/rgms/publication/ResearchLineController.groovy b/grails-app/controllers/rgms/publication/ResearchLineController.groovy index 8b167597..e4f4e6cb 100644 --- a/grails-app/controllers/rgms/publication/ResearchLineController.groovy +++ b/grails-app/controllers/rgms/publication/ResearchLineController.groovy @@ -283,18 +283,6 @@ class ResearchLineController { return false } - def statusChanged(def lista){ - def sizeList = findAllResearchLine() - def sizeI = sizeList.size() - def sizeF = lista.size() - - def resultado = Math.max(sizeI, sizeF) - if(sizeF.compareTo(resultado)){ - return true - } - return false - } - def checkSavedResearchByDescription(nameOfResearch, status){ HashMap lista = findAllResearchLine() for(int i; i< lista.size(); i++){ From 1f496bd18d9680b4aace7af11aa6902ee168e26d Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Fri, 8 Aug 2014 11:58:56 -0300 Subject: [PATCH 52/54] =?UTF-8?q?Corre=C3=A7=C3=A3o:=20removendo=20Researc?= =?UTF-8?q?hLineController=20de=20alguns=20passos=20de=20XMLImportStep,=20?= =?UTF-8?q?e=20definindo=20alguns=20m=C3=A9todos=20de=20checagem=20no=20pr?= =?UTF-8?q?oprio=20step.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/XMLImportSteps.groovy | 103 ++++++++-------------- 1 file changed, 39 insertions(+), 64 deletions(-) diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index a0cc7bab..8bc2ec41 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -5,12 +5,15 @@ import pages.DissertationPage import pages.LoginPage import pages.OrientationPages.OrientationsPage import pages.ResearchLinePages.ResearchLinePage +import rgms.XMLService import rgms.authentication.User +import rgms.member.Member import rgms.publication.* import rgms.researchProject.ResearchProject import steps.ArticleTestDataAndOperations import steps.ConferenciaTestDataAndOperations import steps.PublicationTestDataAndOperations +import steps.ResearchGroupTestDataAndOperations import steps.ResearchLineTestDataAndOperations import steps.ResearchProjectTestDadaAndOperations import steps.XMLImportTestDataAndOperations @@ -227,66 +230,24 @@ When(~'^I upload the file "([^"]*)" that also contains a research project named //#end -// #if($ResearchLine) -Given(~'^ the system has some research lines stored$'){ - TestDataAndOperations.loginController(this) - ResearchLineTestDataAndOperations.createResearchLine(0) - ResearchLineTestDataAndOperations.createResearchLine(1) - ResearchLineTestDataAndOperations.createResearchLine(2) - initialSize = ResearchLine.findAll().size() -} - -Given(~'^ the system has no research line named as "([^"]*)" associated with me $') { String nameOfResearch -> - xmlImport = new ResearchLineController() - def autor = xmlImport.findByActor(this) - List listaDeVerificacao = xmlImport.findAllResearchByMember(autor, nameOfResearch) - assert listaDeVerificacao.size() == 0 -} - -When(~'^I upload the file "([^"]*)" which contains a research line named as "([^"]*)" $'){ String research, file -> - TestDataAndOperations.uploadPublications(file) - String path = "test" + File.separator + "files" + File.separator + file - xmlImport2 = XMLImportTestDataAndOperations.checkResearchLineFromFile(file, research) - assert xmlImport2 != 0 -} - -Then(~'^the system outputs a list of imported research lines which contains the one named as "([^"]*)" with status "([^"]*)" $'){ String researchOfLine, status -> - - xmlImport2 = new ResearchLineController() - //O metodo retorna todos as research que estao com status stable - lista = xmlImport2.findAllResearchLine() - assert xmlImport2.checkIfResearchLineExists(researchOfLine,lista) -} - -Then(~'^ no new research line is stored by the system $'){ - finalSize = ResearchLine.findAll().size() - assert initialSize == finalSize - -} - -Then(~'^ the previously stored research lines do not change $'){ - ResearchLineController controller = new XMLController() - def lista = ResearchLine.findAll() - status = controller.statusChanged(lista) //se true é porque modificou, se false é porque nada foi modificado - assert !status -} - Given(~'^ the system has some research lines stored $'){ ResearchLineTestDataAndOperations.createResearchLine(0) ResearchLineTestDataAndOperations.createResearchLine(1) - initialSize = ResearchLine.findAll().size() } + Given(~'^ the system has no research line named as "([^"]*)" associated with me $'){ nameOfResearch -> - xmlImport = new ResearchLineController() - def autor = xmlImport.findByActor(this) - List listaDeVerificacao = xmlImport.findAllResearchByMember(autor, nameOfResearch) - assert listaDeVerificacao.size() == 0 + /*PublicationController publicationController = new PublicationController() + def research = ResearchLine.findByName(nameOfResearch)*/ + def puclicationMember = publicationController.getLoggedMember() + def publi = Publication.findByResearchLineAndMembers(research, puclicationMember) + assert publi == null } Given(~'^the file "([^"]*)", which contains a research line named as "([^"]*)", is uploaded $'){ String theFile, researchLineName -> - TestDataAndOperations.uploadPublications(theFile) - status = XMLImportTestDataAndOperations.extractSpecificResearchLineFromFile(theFile, researchLineName) - assert status + file = XMLImportTestDataAndOperations.configureFileName(theFile) + def findPubli = ResearchLine.findByName(researchLineName) + TestDataAndOperations.uploadPublications(file) + assert Publication.findByFileAndResearchLine(file, findPubli) != null } When(~'^ I confirm the import of the research line named as "([^"]*)" with status "([^"]*)" $'){ String nameOfResearch, status -> @@ -303,16 +264,14 @@ Then(~'^ the research line named as "([^"]*)" is stored by the system $'){ Strin } Then(~'^ the research line named as "([^"]*)" with status "([^"]*)" is removed from the list of imported research lines $'){ String nameOfResearch, status -> - ResearchLineController cont = new ResearchLineController() - check = cont.checkDeletedResearchByDescription(nameOfResearch, status) - assert check - + def research = ResearchLine.findByNameAndDescription(nameOfResearch, status) + ResearchLineTestDataAndOperations.deleteResearchLine(research.id) } + Then(~'^ the previously stored research lines do not change $'){ - ResearchLineController controller = new XMLController() - def lista = ResearchLine.findAll() - status = controller.statusChanged(lista) - assert !status + def researchs = ResearchLine.findAll() + def lista = Publication.findAllByResearchLineInList(researchs) + assert statusChanged(lista) } //#end @@ -326,9 +285,8 @@ Given(~'^The system has some publications stored $'){ -> When(~'^ I upload the file "([^"]*)" $') { String typeFile -> PublicationTestDataAndOperations.uploadPublication(typeFile) - PublicationController controllerP = new PublicationController() - status = controllerP.checkTypeFile(typeFile) - assert !status + def statusFile = checkTypeFile(typeFile) + assert !statusFile } Then(~'^ no publication is stored by the system $') { -> @@ -344,7 +302,6 @@ Then(~'^ And previusly stored publications do not change $'){-> TestDataAndOperations.logoutController(this) } - Given(~'^I am at the "Import XML File" page$'){ -> to LoginPage at LoginPage @@ -397,4 +354,22 @@ Then(~'^ the previously stored publications do not change$'){ to XMLImportPage at XMLImportPage assert page.uploadFile(new File('/test/files/cv.pdf')) +} + + +//Auxiliares +def statusChanged(def lista){ + def sizeList = Publication.findAll() + def sizeI = sizeList.size() + def sizeF = lista.size() + + def resultado = Math.max(sizeI, sizeF) + if(sizeF.compareTo(resultado)){ + return true + } + return false +} + +def static checkTypeFile(file){ + file.hasProperty("xml") } \ No newline at end of file From 14cc2336951dec44e5106e5a649acae6e0c7f12b Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Fri, 8 Aug 2014 12:06:03 -0300 Subject: [PATCH 53/54] =?UTF-8?q?Corre=C3=A7=C3=A3o:=20removendo=20Researc?= =?UTF-8?q?hLineController=20de=20alguns=20passos=20de=20XMLImportStep,=20?= =?UTF-8?q?e=20definindo=20alguns=20m=C3=A9todos=20de=20checagem=20no=20pr?= =?UTF-8?q?oprio=20step.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cucumber/steps/XMLImportSteps.groovy | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index 8bc2ec41..2a77d825 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -236,11 +236,10 @@ Given(~'^ the system has some research lines stored $'){ } Given(~'^ the system has no research line named as "([^"]*)" associated with me $'){ nameOfResearch -> - /*PublicationController publicationController = new PublicationController() - def research = ResearchLine.findByName(nameOfResearch)*/ - def puclicationMember = publicationController.getLoggedMember() - def publi = Publication.findByResearchLineAndMembers(research, puclicationMember) - assert publi == null + + def puclicationMember = ResearchLineTestDataAndOperations.findResearchLineByNameOrientation(user) + assert ResearchLineTestDataAndOperations.researchLineCompatibleTo(puclicationMember, nameOfResearch) + } Given(~'^the file "([^"]*)", which contains a research line named as "([^"]*)", is uploaded $'){ String theFile, researchLineName -> From 088b9725ef31cb8b3f527a9a1a871e13aa71fff4 Mon Sep 17 00:00:00 2001 From: Kamillakrdoso Date: Tue, 12 Aug 2014 20:38:42 -0300 Subject: [PATCH 54/54] =?UTF-8?q?Corre=C3=A7=C3=A3o:=20cen=C3=A1rio=20rese?= =?UTF-8?q?arch=20line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../publication/ResearchLineController.groovy | 5 ++-- .../rgms/publication/XMLController.groovy | 1 - test/cucumber/steps/XMLImportSteps.groovy | 25 ++++++++----------- .../ResearchLineTestDataAndOperations.groovy | 13 +++++++++- .../XMLImportTestDataAndOperations.groovy | 5 ++-- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/grails-app/controllers/rgms/publication/ResearchLineController.groovy b/grails-app/controllers/rgms/publication/ResearchLineController.groovy index e4f4e6cb..df830441 100644 --- a/grails-app/controllers/rgms/publication/ResearchLineController.groovy +++ b/grails-app/controllers/rgms/publication/ResearchLineController.groovy @@ -266,7 +266,7 @@ class ResearchLineController { def findAllResearchByMember(member, research){ HashMap listagem = new HashMap() for(researchL in ResearchLine.getAll()){ - if((researchL.equals(research)) && (researchL.getMembers().contains(member))){ + if((researchL.getName().equals(research)) && (researchL.getMembers().contains(member))){ listagem.put(member, research) } } @@ -274,9 +274,8 @@ class ResearchLineController { } def checkIfResearchLineExists(researchName, list){ - List listCheck = list for (research in list) { - if (research.equals(researchName)) { + if (research.getName().equals(researchName)) { return true } } diff --git a/grails-app/controllers/rgms/publication/XMLController.groovy b/grails-app/controllers/rgms/publication/XMLController.groovy index 3a64f379..022f894a 100644 --- a/grails-app/controllers/rgms/publication/XMLController.groovy +++ b/grails-app/controllers/rgms/publication/XMLController.groovy @@ -199,5 +199,4 @@ class XMLController { flash.message = message(code: msg) redirect(uri: '/') } - } diff --git a/test/cucumber/steps/XMLImportSteps.groovy b/test/cucumber/steps/XMLImportSteps.groovy index 2a77d825..c3a233dd 100644 --- a/test/cucumber/steps/XMLImportSteps.groovy +++ b/test/cucumber/steps/XMLImportSteps.groovy @@ -8,6 +8,7 @@ import pages.ResearchLinePages.ResearchLinePage import rgms.XMLService import rgms.authentication.User import rgms.member.Member +import rgms.member.MemberController import rgms.publication.* import rgms.researchProject.ResearchProject import steps.ArticleTestDataAndOperations @@ -229,24 +230,22 @@ When(~'^I upload the file "([^"]*)" that also contains a research project named } //#end - +// #if($ResearchLine) Given(~'^ the system has some research lines stored $'){ ResearchLineTestDataAndOperations.createResearchLine(0) ResearchLineTestDataAndOperations.createResearchLine(1) } -Given(~'^ the system has no research line named as "([^"]*)" associated with me $'){ nameOfResearch -> - - def puclicationMember = ResearchLineTestDataAndOperations.findResearchLineByNameOrientation(user) - assert ResearchLineTestDataAndOperations.researchLineCompatibleTo(puclicationMember, nameOfResearch) - +Given(~'^ the system has no research line named as "([^"]*)" associated with me $'){ String nameOfResearch -> + def member = Member.findByName(user) + assert ResearchLineTestDataAndOperations.checkSpecificResearchForMember(member, nameOfResearch) } Given(~'^the file "([^"]*)", which contains a research line named as "([^"]*)", is uploaded $'){ String theFile, researchLineName -> - file = XMLImportTestDataAndOperations.configureFileName(theFile) - def findPubli = ResearchLine.findByName(researchLineName) - TestDataAndOperations.uploadPublications(file) - assert Publication.findByFileAndResearchLine(file, findPubli) != null + def file = XMLImportTestDataAndOperations.configureFileName(theFile) + XMLImportTestDataAndOperations.uploadXmlFile(new XMLController(), file) + def find = ResearchLine.findByName(researchLineName) + assert Publication.findByFileAndResearchLine(file, find) != null } When(~'^ I confirm the import of the research line named as "([^"]*)" with status "([^"]*)" $'){ String nameOfResearch, status -> @@ -272,7 +271,7 @@ Then(~'^ the previously stored research lines do not change $'){ def lista = Publication.findAllByResearchLineInList(researchs) assert statusChanged(lista) } -//#end + Given(~'^The system has some publications stored $'){ -> @@ -294,10 +293,8 @@ Then(~'^ no publication is stored by the system $') { -> } Then(~'^ And previusly stored publications do not change $'){-> - PublicationController contr = new PublicationController() def lista = Publication.findAll() - resultChanged = contr.statusChanged(lista) - assert !resultChanged + assert statusChanged(lista) TestDataAndOperations.logoutController(this) } diff --git a/test/functional/steps/ResearchLineTestDataAndOperations.groovy b/test/functional/steps/ResearchLineTestDataAndOperations.groovy index a6ed5adb..de9423cf 100644 --- a/test/functional/steps/ResearchLineTestDataAndOperations.groovy +++ b/test/functional/steps/ResearchLineTestDataAndOperations.groovy @@ -1,5 +1,6 @@ package steps +import rgms.member.Member import rgms.publication.ResearchLine import rgms.publication.ResearchLineController import rgms.publication.XMLController @@ -31,7 +32,6 @@ class ResearchLineTestDataAndOperations { rl.setDescription(description) rl.save() } - } static public def findResearchLineByName(String name) { @@ -116,4 +116,15 @@ class ResearchLineTestDataAndOperations { } return compatible } + + static public boolean checkSpecificResearchForMember(Member member, String nameResearch){ + def researchController = new ResearchLineController() + def researchL = researchController.findAllResearchByMember(member, nameResearch) + for (eachResearch in researchL){ + if((eachResearch.getValue().equals(nameResearch)) && (eachResearch.getKey().equals(member.getName()))){ + return true + } + } + return false + } } diff --git a/test/functional/steps/XMLImportTestDataAndOperations.groovy b/test/functional/steps/XMLImportTestDataAndOperations.groovy index 04486700..64a1c1f2 100644 --- a/test/functional/steps/XMLImportTestDataAndOperations.groovy +++ b/test/functional/steps/XMLImportTestDataAndOperations.groovy @@ -158,16 +158,15 @@ class XMLImportTestDataAndOperations { newResearch = XMLService.checkContResearch(researchLine, i, researchName) researchs.add(newResearch) } - return researchs.size() } - static extractSpecificResearchLineFromFile(fileName, researchLineName){ + static extractSpecificResearchLineFromFile(fileName,researchLineName){ getRootNode(fileName) List specificResearchs = fileName.depthFirst().findAll{ it.name() == 'TITULO-DA-LINHA-DE-PESQUISA' } for (research in specificResearchs) { if(research.equals(researchLineName)){ - ResearchLineTestDataAndOperations.createResearchLine(researchLineName) + ResearchLineTestDataAndOperations.createResearchLine(researchLineName.name) return true } }