Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
nikos dimitrakas 2022-04-22 14:56:50 +02:00
commit 597b753fc1
32 changed files with 355 additions and 136 deletions

@ -149,6 +149,7 @@ public class CoreModule extends AbstractModule {
install(new MailModule());
install(new ForumModule());
Multibinder.newSetBinder(binder(), UserImportService.class);
Multibinder.newSetBinder(binder(), UserSearchService.class);
}
}

@ -6,8 +6,6 @@ import io.opentracing.Tracer;
import io.opentracing.noop.NoopTracerFactory;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPIImpl;
import se.su.dsv.scipro.daisyExternal.impl.ExternalImporterDaisyImpl;
import se.su.dsv.scipro.daisyExternal.impl.ImporterTransactionsImpl;
public class DaisyExternalModule extends PrivateModule {
@Override
@ -16,12 +14,7 @@ public class DaisyExternalModule extends PrivateModule {
requireBinding(Key.get(String.class, Names.named("daisy.api.username")));
requireBinding(Key.get(String.class, Names.named("daisy.api.password")));
bind(ExternalImporter.class).to(ExternalImporterDaisyImpl.class);
bind(ImporterTransactions.class).to(ImporterTransactionsImpl.class);
bind(DaisyAPI.class).to(DaisyAPIImpl.class).in(Scopes.SINGLETON);
expose(ExternalImporter.class);
expose(ImporterTransactions.class);
expose(DaisyAPI.class);
expose(Tracer.class);
}

@ -1,22 +0,0 @@
package se.su.dsv.scipro.daisyExternal;
import se.su.dsv.scipro.system.ResearchArea;
import se.su.dsv.scipro.system.User;
import java.util.Optional;
import java.util.Set;
/**
* Specifies interaction of an importing service component.
*/
public interface ExternalImporter {
Optional<User> importUser(final String userName);
void importSupervisorUnits();
void importSupervisorActiveStatus();
Set<ResearchArea> importResearchAreasForSupervisor(User supervisor);
}

@ -71,4 +71,6 @@ public interface DaisyAPI {
List<CourseRegistration> getCourseRegistrationsForStudent(int studentId);
List<Employee> listEmployees(int departmentId);
OrganisationalUnit orgunit(int unitId);
}

@ -411,6 +411,16 @@ public class DaisyAPIImpl implements DaisyAPI {
.get(new GenericType<>(){});
}
@Override
public OrganisationalUnit orgunit(final int unitId) {
return () -> target()
.path("orgunit")
.path(Integer.toString(unitId))
.path("researchAreas")
.request(MediaType.APPLICATION_JSON_TYPE)
.get(new GenericType<>(){});
}
private WebTarget program() {
return target()
.path(PROGRAM);

@ -0,0 +1,9 @@
package se.su.dsv.scipro.daisyExternal.http;
import se.su.dsv.scipro.io.dto.ResearchArea;
import java.util.List;
public interface OrganisationalUnit {
List<ResearchArea> getResearchAreas();
}

@ -4,5 +4,5 @@ import java.util.List;
public interface ResearchAreaService extends GenericService<ResearchArea, Long> {
List<ResearchArea> findNotDeleted();
ResearchArea updateExternalResearchArea(Long id, String name);
ResearchArea updateExternalResearchArea(Long id, String name, boolean active);
}

@ -14,7 +14,7 @@ public class ResearchAreaServiceImpl extends AbstractServiceImpl<ResearchArea,Lo
}
@Override
public ResearchArea updateExternalResearchArea(Long identifier, String name) {
public ResearchArea updateExternalResearchArea(Long identifier, String name, final boolean active) {
ResearchArea ra = new ResearchArea();
if (identifier != null) {
ResearchArea existing = findOne(QResearchArea.researchArea.identifier.eq(identifier));
@ -25,6 +25,7 @@ public class ResearchAreaServiceImpl extends AbstractServiceImpl<ResearchArea,Lo
}
}
ra.setTitle(name);
ra.setDeleted(!active);
return save(ra);
}

@ -0,0 +1,46 @@
package se.su.dsv.scipro.system;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public interface UserImportService {
Optional<User> importUser(final String userName);
List<ImportableUser> search(String searchTerm);
void importUser(ImportableUser importableUser);
Set<ResearchArea> importResearchAreasForSupervisor(User supervisor);
class ImportableUser implements Serializable {
private final String firstName;
private final String lastName;
private final String email;
private final Integer id;
public ImportableUser(String firstName, String lastName, String email, Integer id) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.id = id;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getEmail() {
return email;
}
public Integer getId() {
return id;
}
}
}

@ -21,7 +21,7 @@ public class ResearchAreaServiceImplIntegrationTest extends IntegrationTest {
Long identifier = 1L;
ResearchArea researchArea = createResearchArea(identifier, "Some title");
ResearchArea created = researchAreaService.updateExternalResearchArea(researchArea.getIdentifier(), researchArea.getTitle());
ResearchArea created = researchAreaService.updateExternalResearchArea(researchArea.getIdentifier(), researchArea.getTitle(), !researchArea.isDeleted());
assertEquals(1, researchAreaService.count());
assertNotNull(created);
assertNotNull(created.getId());
@ -35,7 +35,7 @@ public class ResearchAreaServiceImplIntegrationTest extends IntegrationTest {
String title = "New title";
ResearchArea updated = researchAreaService.updateExternalResearchArea(old.getIdentifier(), title);
ResearchArea updated = researchAreaService.updateExternalResearchArea(old.getIdentifier(), title, !old.isDeleted());
assertEquals(title, updated.getTitle());
assertEquals(1, researchAreaService.count());

@ -0,0 +1,12 @@
package se.su.dsv.scipro.daisyExternal;
/**
* Specifies interaction of an importing service component.
*/
public interface ExternalImporter {
void importSupervisorUnits();
void importSupervisorActiveStatus();
}

@ -16,16 +16,18 @@ import se.su.dsv.scipro.security.auth.roles.Roles;
import se.su.dsv.scipro.springdata.services.UnitService;
import se.su.dsv.scipro.system.ResearchArea;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.system.UserImportService;
import se.su.dsv.scipro.system.UserService;
import javax.inject.Inject;
import javax.ws.rs.ClientErrorException;
import java.util.*;
import java.util.stream.Collectors;
/**
* Importer implementation, uses standard HTTP requests and Json-wrappers.
*/
public class ExternalImporterDaisyImpl implements ExternalImporter {
public class ExternalImporterDaisyImpl implements ExternalImporter, UserImportService {
public static final Integer DSV = 4;
private static final Logger LOGGER = LoggerFactory.getLogger(ExternalImporterDaisyImpl.class);
@ -47,6 +49,33 @@ public class ExternalImporterDaisyImpl implements ExternalImporter {
this.importerTransactions = importerTransactions;
}
@Override
public List<ImportableUser> search(String searchTerm) {
return client.findByPersonnummer(searchTerm)
.stream()
.filter(person -> !person.isDeceased())
.map(this::toImportableUser)
.collect(Collectors.toList());
}
private ImportableUser toImportableUser(Person person) {
return new DaisyImportableUser(person);
}
private static class DaisyImportableUser extends ImportableUser {
public DaisyImportableUser(Person person) {
super(person.getFirstName(), person.getLastName(), person.getEmail(), person.getId());
}
}
@Override
public void importUser(ImportableUser importableUser) {
if (importableUser instanceof DaisyImportableUser) {
client.findPersonById(importableUser.getId())
.ifPresent(this::importPerson);
}
}
@Override
public Optional<User> importUser(final String userName) {
try {

@ -168,7 +168,7 @@ public class ImporterTransactionsImpl implements ImporterTransactions {
.collect(Collectors.toList());
for (ResearchArea daisyResearchArea : researchAreas) {
se.su.dsv.scipro.system.ResearchArea researchArea =
researchAreaService.updateExternalResearchArea(daisyResearchArea.getId(), daisyResearchArea.getNameEn());
researchAreaService.updateExternalResearchArea(daisyResearchArea.getId(), daisyResearchArea.getNameEn(), daisyResearchArea.isActive());
employee.addResearchArea(researchArea);
}
employee.getResearchAreas()

@ -2,16 +2,24 @@ package se.su.dsv.scipro.integration.daisy;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.servlet.ServletModule;
import se.su.dsv.scipro.daisyExternal.ExternalImporter;
import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
import se.su.dsv.scipro.daisyExternal.impl.ExternalImporterDaisyImpl;
import se.su.dsv.scipro.daisyExternal.impl.ImporterTransactionsImpl;
import se.su.dsv.scipro.integration.daisy.workers.ProjectExporter;
import se.su.dsv.scipro.integration.daisy.workers.UserImportWorker;
import se.su.dsv.scipro.io.ExternalExporter;
import se.su.dsv.scipro.io.impl.ExternalExporterDaisyImpl;
import se.su.dsv.scipro.match.IdeaCreationJudge;
import se.su.dsv.scipro.system.UserImportService;
import se.su.dsv.scipro.system.UserSearchService;
public class DaisyModule extends ServletModule {
@Override
protected void configureServlets() {
bind(ExternalImporter.class).to(ExternalImporterDaisyImpl.class);
bind(ImporterTransactions.class).to(ImporterTransactionsImpl.class);
Multibinder<IdeaCreationJudge> judges = Multibinder.newSetBinder(binder(), IdeaCreationJudge.class);
judges.addBinding().to(Daisy.class);
@ -20,6 +28,9 @@ public class DaisyModule extends ServletModule {
bind(ProjectExporter.class);
bind(DaisyWorkerInitialization.class).asEagerSingleton();
Multibinder.newSetBinder(binder(), UserImportService.class)
.addBinding().to(ExternalImporterDaisyImpl.class);
Multibinder.newSetBinder(binder(), UserSearchService.class)
.addBinding().to(DaisyUserSearchService.class);
}

@ -12,9 +12,11 @@ import se.su.dsv.scipro.daisyExternal.http.Semester;
import se.su.dsv.scipro.io.dto.Person;
import se.su.dsv.scipro.io.dto.Program;
import se.su.dsv.scipro.io.dto.ProgramAdmission;
import se.su.dsv.scipro.io.dto.ResearchArea;
import se.su.dsv.scipro.io.dto.Thesis;
import se.su.dsv.scipro.match.ProgramService;
import se.su.dsv.scipro.system.MergeService;
import se.su.dsv.scipro.system.ResearchAreaService;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.system.UserService;
import se.su.dsv.scipro.system.Username;
@ -40,6 +42,7 @@ public class UserImportWorker extends AbstractWorker {
private final MergeService mergeService;
private final ProgramService programService;
private final Provider<EntityManager> entityManagerProvider;
private final ResearchAreaService researchAreaService;
@Inject
public UserImportWorker(DaisyAPI daisyAPI,
@ -49,7 +52,9 @@ public class UserImportWorker extends AbstractWorker {
final MergeService mergeService,
UserService userService,
ProgramService programService,
Provider<EntityManager> entityManagerProvider) {
Provider<EntityManager> entityManagerProvider,
final ResearchAreaService researchAreaService)
{
this.daisyAPI = daisyAPI;
this.importerTransactions = importerTransactions;
this.externalImporter = externalImporter;
@ -58,6 +63,7 @@ public class UserImportWorker extends AbstractWorker {
this.mergeService = mergeService;
this.programService = programService;
this.entityManagerProvider = entityManagerProvider;
this.researchAreaService = researchAreaService;
}
@Override
@ -67,6 +73,7 @@ public class UserImportWorker extends AbstractWorker {
LOGGER.info("Starting execution for scheduled remote user import of all remote supervisors");
final Map<Integer, Program> programCache = new HashMap<>();
importResearchAreas();
importUserDetails(programCache);
importSupervisors(programCache);
importSupervisorUnits();
@ -81,6 +88,15 @@ public class UserImportWorker extends AbstractWorker {
executionTimeSecs % 60);
}
private void importResearchAreas() {
List<ResearchArea> daisyResearchAreas = daisyAPI
.orgunit(DSV)
.getResearchAreas();
for (ResearchArea daisyResearchArea : daisyResearchAreas) {
researchAreaService.updateExternalResearchArea(daisyResearchArea.getId(), daisyResearchArea.getName(), daisyResearchArea.isActive());
}
}
private void importUserDetails(final Map<Integer, Program> programCache) {
for (final User user : userService.findAll()) {
if (user.getIdentifier() != null) {

@ -0,0 +1,50 @@
package se.su.dsv.scipro.daisyExternal.impl;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.io.dto.Person;
import se.su.dsv.scipro.springdata.services.UnitService;
import se.su.dsv.scipro.system.UserImportService;
import se.su.dsv.scipro.system.UserService;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ExternalImporterDaisyImplTest {
@Mock
private DaisyAPI daisyAPI;
@Mock
private UserService userService;
@Mock
private UnitService unitService;
@Mock
private ImporterTransactions importerTransactions;
@Test
void search_excludes_deceased_people() {
Person einstein = new Person();
einstein.setLastName("Einstein");
einstein.setDeceased(true);
Person obama = new Person();
obama.setLastName("Obama");
obama.setDeceased(false);
when(daisyAPI.findByPersonnummer(anyString())).thenReturn(List.of(einstein, obama));
ExternalImporterDaisyImpl importerDaisy = new ExternalImporterDaisyImpl(userService, unitService, daisyAPI, importerTransactions);
List<UserImportService.ImportableUser> importableUsers = importerDaisy.search("abc");
assertEquals(1, importableUsers.size());
assertEquals("Obama", importableUsers.get(0).getLastName());
}
}

@ -13,6 +13,7 @@ import se.su.dsv.scipro.io.dto.Person;
import se.su.dsv.scipro.io.dto.Thesis;
import se.su.dsv.scipro.match.ProgramService;
import se.su.dsv.scipro.system.MergeService;
import se.su.dsv.scipro.system.ResearchAreaService;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.system.UserService;
@ -39,13 +40,25 @@ public class UserImportWorkerTest {
private ProgramService programService;
@Mock
private EntityManager entityManager;
@Mock
private ResearchAreaService researchAreaService;
private UserImportWorker worker;
@BeforeEach
public void setUp() {
when(userService.findAll()).thenReturn(Collections.emptyList());
worker = new UserImportWorker(daisyAPI, importerTransactions, externalImporter, NoopTracerFactory.create(), mergeService, userService, programService, () -> entityManager);
when(daisyAPI.orgunit(anyInt())).thenReturn(Collections::emptyList);
worker = new UserImportWorker(
daisyAPI,
importerTransactions,
externalImporter,
NoopTracerFactory.create(),
mergeService,
userService,
programService,
() -> entityManager,
researchAreaService);
}
@Test

@ -22,7 +22,7 @@
<!-- Dependency versions -->
<slf4j.version>1.7.36</slf4j.version>
<log4j2.version>2.17.1</log4j2.version>
<wicket.version>9.8.0</wicket.version>
<wicket.version>9.9.0</wicket.version>
<hibernate.version>5.6.7.Final</hibernate.version>
<querydsl.version>5.0.0</querydsl.version>
<javax.servlet.version>3.0.1</javax.servlet.version>
@ -160,7 +160,7 @@
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.13.2.20220324</version>
<version>2.13.2.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>

@ -50,7 +50,7 @@
<wicket:fragment wicket:id="footerLinks">
<ul class="list-unstyled">
<li wicket:id="links">
<a href="#" wicket:id="link">[label]</a>
<a href="#" wicket:id="link" target="_blank">[label]</a>
</li>
</ul>
</wicket:fragment>

@ -11,6 +11,8 @@
<div class="modal-body" wicket:id="body">
<!-- Dialog content -->
</div>
<div class="modal-footer" wicket:id="footer">
</div>
</div>
</div>
</wicket:panel>

@ -26,6 +26,10 @@ public class ModalWindowPlus extends Panel {
WebMarkupContainer content = new WebMarkupContainer("body");
content.setOutputMarkupId(true);
add(content);
WebMarkupContainer footer = new WebMarkupContainer("footer");
footer.setVisible(false);
footer.setOutputMarkupPlaceholderTag(true);
add(footer);
}
@Override
@ -51,6 +55,12 @@ public class ModalWindowPlus extends Panel {
target.appendJavaScript(String.format("$('#%s').modal('hide');", getMarkupId()));
}
public void refresh(AjaxRequestTarget target) {
target.add(get("title"));
target.add(get("body"));
target.add(get("footer"));
}
public String getContentId() {
return "body";
}
@ -63,4 +73,10 @@ public class ModalWindowPlus extends Panel {
public void setContent(Function<String, Component> newComponent) {
setContent(newComponent.apply(getContentId()));
}
public void setFooter(Function<String, Component> newFooter) {
final Component component = newFooter.apply("footer");
component.setOutputMarkupPlaceholderTag(true);
replace(component);
}
}

@ -1,21 +1,22 @@
package se.su.dsv.scipro.loginlogout.pages;
import se.su.dsv.scipro.basepages.PublicPage;
import se.su.dsv.scipro.daisyExternal.ExternalImporter;
import se.su.dsv.scipro.security.auth.Authorization;
import se.su.dsv.scipro.session.SciProSession;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.system.UserImportService;
import se.su.dsv.scipro.system.UserService;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import java.util.Optional;
import java.util.Set;
@Authorization(requiresLoggedInUser = false)
public class SSOPage extends PublicPage {
@Inject
private ExternalImporter externalImporter;
private Set<UserImportService> userImportServices;
@Inject
private UserService userService;
@ -29,12 +30,14 @@ public class SSOPage extends PublicPage {
continueToOriginalDestination();
setResponsePage(getApplication().getHomePage());
} else {
Optional<User> optionalUser = externalImporter.importUser(remoteUserName);
if (optionalUser.isPresent()) {
SciProSession.get().setUser(optionalUser.get());
continueToOriginalDestination();
setResponsePage(getApplication().getHomePage());
for (UserImportService userImportService : userImportServices) {
Optional<User> optionalUser = userImportService.importUser(remoteUserName);
if (optionalUser.isPresent()) {
SciProSession.get().setUser(optionalUser.get());
continueToOriginalDestination();
setResponsePage(getApplication().getHomePage());
break;
}
}
}
}

@ -6,9 +6,11 @@
Before you are able to create ideas you must select your research areas.
</p>
<div wicket:id="update_research_areas"></div>
<a wicket:id="create_idea" class="btn btn-link">
Continue to idea creation
</a>
<wicket:link>
<a href="SupervisorIdeaSubmissionPage.html" class="btn btn-link">
Continue to idea creation
</a>
</wicket:link>
</wicket:panel>
</body>
</html>

@ -54,6 +54,9 @@
<div wicket:id="keywords"></div>
</div>
</div>
<wicket:fragment wicket:id="close_fragment">
<button class="btn btn-link" data-bs-dismiss="modal">Close</button>
</wicket:fragment>
<fieldset class="mb-3">
<legend>Publish <span wicket:id="publishInfo"></span></legend>

@ -9,6 +9,7 @@ import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.*;
import org.apache.wicket.markup.html.panel.ComponentFeedbackPanel;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.*;
import org.apache.wicket.model.util.ListModel;
@ -148,8 +149,10 @@ public class SupervisorEditIdeaPanel extends Panel {
@Override
protected void onResearchAreasUpdated(final AjaxRequestTarget target) {
target.add(areaContainer);
modalWindow.refresh(target);
}
});
modalWindow.setFooter(CloseFragment::new);
modalWindow.show(target);
}
});
@ -194,6 +197,19 @@ public class SupervisorEditIdeaPanel extends Panel {
});
}
private class CloseFragment extends Fragment {
public CloseFragment(final String id) {
super(id, "close_fragment", SupervisorEditIdeaPanel.IdeaForm.this);
}
@Override
protected void onConfigure() {
super.onConfigure();
final User supervisor = getUser().getObject();
setVisible(!supervisor.getActiveResearchAreas().isEmpty());
}
}
private KeywordFilterPanel getKeywordsPanel() {
keywordsPanel = new KeywordFilterPanel("keywords", new SetModel<>(selectableKeywords), new ListModel<>(new ArrayList<>(selectedKeywords))) {
@Override

@ -72,6 +72,20 @@
</wicket:fragment>
<wicket:fragment wicket:id="select_research_area_fragment">
<p>
Before you are able to create ideas you must select your research areas.
</p>
<div wicket:id="update_research_areas"></div>
</wicket:fragment>
<wicket:fragment wicket:id="select_research_area_footer_fragment">
<wicket:link>
<a href="SupervisorIdeaSubmissionPage.html" class="btn btn-link">
Continue to idea creation
</a>
</wicket:link>
</wicket:fragment>
</wicket:panel>
</body>
</html>

@ -12,6 +12,7 @@ import org.apache.wicket.extensions.markup.html.repeater.data.table.export.Abstr
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.EnumChoiceRenderer;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.model.*;
@ -61,7 +62,8 @@ public class SupervisorMyIdeasPanel extends Panel {
public void onClick(AjaxRequestTarget target) {
if (supervisor.getObject().getResearchAreas().stream().allMatch(ResearchArea::isDeleted)) {
modalWindowPlus.setTitle(new ResourceModel("select.research.area"));
modalWindowPlus.setContent(id -> new SelectResearchAreaPanel(id, supervisor));
modalWindowPlus.setContent(SelectResearchAreaFragment::new);
modalWindowPlus.setFooter(SelectResearchAreaFooterFragment::new);
modalWindowPlus.show(target);
}
else {
@ -75,6 +77,30 @@ public class SupervisorMyIdeasPanel extends Panel {
addDataTable();
}
private class SelectResearchAreaFragment extends Fragment {
public SelectResearchAreaFragment(final String id) {
super(id, "select_research_area_fragment", SupervisorMyIdeasPanel.this);
add(new UpdateResearchAreasPanel("update_research_areas", supervisor) {
@Override
protected void onResearchAreasUpdated(final AjaxRequestTarget target) {
modalWindowPlus.refresh(target);
}
});
}
}
private class SelectResearchAreaFooterFragment extends Fragment {
public SelectResearchAreaFooterFragment(final String id) {
super(id, "select_research_area_footer_fragment", SupervisorMyIdeasPanel.this);
}
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(!supervisor.getObject().getActiveResearchAreas().isEmpty());
}
}
private void addDialog() {
modalWindowPlus = new ModalWindowPlus("dialog");
add(modalWindowPlus);

@ -7,11 +7,11 @@ import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import se.su.dsv.scipro.daisyExternal.ExternalImporter;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
import se.su.dsv.scipro.system.ResearchArea;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.system.UserImportService;
import javax.inject.Inject;
import java.util.Set;
@ -21,7 +21,7 @@ public class UpdateResearchAreasPanel extends Panel {
@Inject
GeneralSystemSettingsService generalSystemSettingsService;
@Inject
ExternalImporter externalImporter;
Set<UserImportService> userImportServices;
public UpdateResearchAreasPanel(final String id, final IModel<User> supervisor) {
super(id, supervisor);
@ -36,7 +36,10 @@ public class UpdateResearchAreasPanel extends Panel {
add(new AjaxLink<>("sync", supervisor) {
@Override
public void onClick(AjaxRequestTarget target) {
Set<ResearchArea> imported = externalImporter.importResearchAreasForSupervisor(getModelObject());
Set<ResearchArea> imported = userImportServices.stream()
.map(uis -> uis.importResearchAreasForSupervisor(getModelObject()))
.flatMap(Set::stream)
.collect(Collectors.toSet());
if (imported.stream().allMatch(ResearchArea::isDeleted)) {
error(getString("no.research.areas.found"));
}

@ -10,27 +10,25 @@ import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.io.dto.Person;
import se.su.dsv.scipro.system.UserImportService;
import se.su.dsv.scipro.system.UserImportService.ImportableUser;
import se.su.dsv.scipro.system.UserService;
import javax.inject.Inject;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class AdminImportUserPage extends AbstractAdminUsersPage {
@Inject
private DaisyAPI daisyAPI;
private Set<UserImportService> userImportServices;
@Inject
private UserService userService;
@Inject
private ImporterTransactions importerTransactions;
private final IModel<String> searchTermModel = Model.of("");
private final IModel<List<Person>> daisyPeopleModel = Model.ofList(Collections.emptyList());
private final IModel<List<ImportableUser>> importablePeopleModel = Model.ofList(Collections.emptyList());
public AdminImportUserPage() {
add(new FeedbackPanel("feedback"));
@ -42,32 +40,32 @@ public class AdminImportUserPage extends AbstractAdminUsersPage {
@Override
protected void onSubmit() {
final List<Person> byPersonnummer = daisyAPI.findByPersonnummer(searchTermModel.getObject());
daisyPeopleModel.setObject(byPersonnummer);
List<ImportableUser> byPersonnummer = userImportServices.stream()
.map(uis -> uis.search(searchTermModel.getObject()))
.flatMap(List::stream)
.collect(Collectors.toList());
importablePeopleModel.setObject(byPersonnummer);
}
});
add(new ListView<>("importCandidates", daisyPeopleModel) {
add(new ListView<>("importCandidates", importablePeopleModel) {
@Override
protected void populateItem(final ListItem<Person> item) {
final IModel<Person> person = item.getModel();
item.add(new Label("firstName", person.map(Person::getFirstName)));
item.add(new Label("lastName", person.map(Person::getLastName)));
item.add(new Label("email", person.map(Person::getEmail)));
protected void populateItem(final ListItem<ImportableUser> item) {
final IModel<ImportableUser> importableUser = item.getModel();
item.add(new Label("firstName", importableUser.map(ImportableUser::getFirstName)));
item.add(new Label("lastName", importableUser.map(ImportableUser::getLastName)));
item.add(new Label("email", importableUser.map(ImportableUser::getEmail)));
item.add(new Link<>("import", item.getModel()) {
@Override
protected void onConfigure() {
super.onConfigure();
final Person person = getModelObject();
final boolean alreadyImported = userService.findByExternalIdentifier(person.getId()) != null;
final ImportableUser importableUser1 = getModelObject();
final boolean alreadyImported = userService.findByExternalIdentifier(importableUser1.getId()) != null;
if (alreadyImported) {
setEnabled(false);
setBody(new ResourceModel("person.already.imported", "Already imported"));
} else if (person.isDeceased()) {
setEnabled(false);
setBody(new ResourceModel("person.is.deceased", "The person is deceased"));
} else if (person.getEmail() == null) {
} else if (importableUser1.getEmail() == null) {
setEnabled(false);
setBody(new ResourceModel("person.lacks.email", "Must specify an e-mail address in Daisy"));
} else {
@ -78,7 +76,10 @@ public class AdminImportUserPage extends AbstractAdminUsersPage {
@Override
public void onClick() {
importerTransactions.importStudent(getModelObject(), new HashMap<>());
ImportableUser toImport = getModelObject();
for (UserImportService userImportService : userImportServices) {
userImportService.importUser(toImport);
}
getPage().success(getString("person.imported", getModel()));
}
});

@ -20,7 +20,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
import se.su.dsv.scipro.activityplan.*;
import se.su.dsv.scipro.admin.pages.AdminStartPage;
import se.su.dsv.scipro.checklist.*;
import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.daisyExternal.http.PhotoResult;
import se.su.dsv.scipro.data.enums.DateStyle;
@ -294,7 +293,7 @@ public abstract class SciProTest {
@Mock
protected GradeCalculatorService gradeCalculatorService;
@Mock
protected ImporterTransactions importerTransactions;
protected UserImportService userImportService;
@Mock
protected Reporter reporter;
@ -411,6 +410,8 @@ public abstract class SciProTest {
@Override
protected void configure() {
Multibinder.newSetBinder(binder(), Lifecycle.class);
Multibinder.newSetBinder(binder(), UserImportService.class)
.addBinding().toInstance(userImportService);
bindConstant().annotatedWith(Names.named("profile")).to("TEST");
for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
for (Field f : c.getDeclaredFields()) {

@ -3,10 +3,9 @@ package se.su.dsv.scipro.user;
import org.apache.wicket.util.tester.FormTester;
import org.junit.jupiter.api.Test;
import se.su.dsv.scipro.SciProTest;
import se.su.dsv.scipro.io.dto.Person;
import se.su.dsv.scipro.system.UserImportService;
import java.util.Collections;
import java.util.HashMap;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -15,38 +14,21 @@ public class AdminImportUserPageTest extends SciProTest {
@Test
public void search() {
final String searchTerm = "199506";
final String firstName = "John";
final String lastName = "Doe";
final Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setEmail("john@example.com");
person.setDeceased(false);
when(daisyAPI.findByPersonnummer(searchTerm)).thenReturn(Collections.singletonList(person));
UserImportService.ImportableUser importableUser = fakeImportableUser(searchTerm, "john@example.com");
tester.startPage(AdminImportUserPage.class);
final FormTester formTester = tester.newFormTester("form");
formTester.setValue("searchTerm", searchTerm);
formTester.submit();
tester.assertLabel(path("importCandidates", "0", "firstName"), firstName);
tester.assertLabel(path("importCandidates", "0", "firstName"), importableUser.getFirstName());
}
@Test
public void cant_imported_people_without_email() {
final String searchTerm = "199506";
final String firstName = "John";
final String lastName = "Doe";
final Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setEmail(null);
person.setDeceased(false);
when(daisyAPI.findByPersonnummer(searchTerm)).thenReturn(Collections.singletonList(person));
fakeImportableUser(searchTerm, null);
tester.startPage(AdminImportUserPage.class);
final FormTester formTester = tester.newFormTester("form");
@ -56,42 +38,10 @@ public class AdminImportUserPageTest extends SciProTest {
tester.assertDisabled(path("importCandidates", "0", "import"));
}
@Test
public void cant_imported_deceased_people() {
final String searchTerm = "199506";
final String firstName = "John";
final String lastName = "Doe";
final Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setEmail("john@example.com");
person.setDeceased(true);
when(daisyAPI.findByPersonnummer(searchTerm)).thenReturn(Collections.singletonList(person));
tester.startPage(AdminImportUserPage.class);
final FormTester formTester = tester.newFormTester("form");
formTester.setValue("searchTerm", searchTerm);
formTester.submit();
tester.assertDisabled(path("importCandidates", "0", "import"));
}
@Test
public void import_person() {
final String searchTerm = "199506";
final String firstName = "John";
final String lastName = "Doe";
final Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setEmail("john@example.com");
person.setDeceased(false);
when(daisyAPI.findByPersonnummer(searchTerm)).thenReturn(Collections.singletonList(person));
UserImportService.ImportableUser importableUser = fakeImportableUser(searchTerm, "john@example.com");
tester.startPage(AdminImportUserPage.class);
final FormTester formTester = tester.newFormTester("form");
@ -100,6 +50,17 @@ public class AdminImportUserPageTest extends SciProTest {
tester.clickLink(path("importCandidates", "0", "import"));
verify(importerTransactions).importStudent(person, new HashMap<>());
verify(userImportService).importUser(importableUser);
}
private UserImportService.ImportableUser fakeImportableUser(String searchTerm, String email) {
final String firstName = "John";
final String lastName = "Doe";
UserImportService.ImportableUser importableUser =
new UserImportService.ImportableUser(firstName, lastName, email, 42);
when(userImportService.search(searchTerm)).thenReturn(Collections.singletonList(importableUser));
return importableUser;
}
}