Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.idea/
/target/
/tags
/src/main/resources/output.csv
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Volunteers

## Participants :

- [Nicolas Laroche](https://github.com/Laroche-Nicolas/)
- [Guillaume Chateauroux](https://github.com/chxGuillaume/)
- [Julien Seixas](https://github.com/MrZyr0/)
- [Geoffrey Parrier](https://github.com/geoffreyparrier/)

![](https://britishcivilwars.ncl.ac.uk/assets/game-runway/images/confetti.gif)
3 changes: 2 additions & 1 deletion src/main/java/App.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import org.example.volunteers.Cleaner;
import org.example.volunteers.Volunteer;
import org.example.volunteers.utils.FieldsSanitizer;

import java.io.FileWriter;
import java.io.IOException;
Expand All @@ -25,7 +26,7 @@ public static void main(String[] args) throws IOException {
.map(tokens -> new Volunteer(tokens.get(0), tokens.get(1), tokens.get(2), tokens.get(3), tokens.get(4)))
.collect(toList());

List<Volunteer> outputVolunteers = Cleaner.cleanUp(inputVolunteers);
List<Volunteer> outputVolunteers = Cleaner.cleanUp(inputVolunteers, new FieldsSanitizer());

PrintWriter writer = new PrintWriter(new FileWriter("src/main/resources/output.csv"));
outputVolunteers.forEach(writer::println);
Expand Down
62 changes: 60 additions & 2 deletions src/main/java/org/example/volunteers/Cleaner.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,70 @@
package org.example.volunteers;

import org.example.volunteers.utils.FieldsSanitizer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;

public class Cleaner {
public static List<Volunteer> cleanUp(List<Volunteer> volunteers) {
public static List<Volunteer> cleanUp(List<Volunteer> volunteers, FieldsSanitizer fieldsSanitizer) {
HashMap<String, Volunteer> volunteersFullName = new HashMap<>();
HashMap<String, Volunteer> volunteersNickName = new HashMap<>();

List<Volunteer> result = new ArrayList<>();

// This function should contain your dark magic.
// For now, it simply returns a copy of the initial list.
return new ArrayList<>(volunteers);
for (Volunteer volunteer : volunteers) {
volunteer.firstName = fieldsSanitizer.clearName(volunteer.firstName);
volunteer.lastName = fieldsSanitizer.clearName(volunteer.lastName);
volunteer.eMail = fieldsSanitizer.clearEmail(volunteer.eMail);
volunteer.phone = fieldsSanitizer.clearPhone(volunteer.phone);
}

for (Volunteer volunteer : volunteers) {
String fullName = volunteer.firstName.toLowerCase() + volunteer.lastName.toLowerCase();

if (fullName.equals("")) {
Volunteer originalVolunteer = volunteersNickName.get(volunteer.nickName.toLowerCase());

if (originalVolunteer == null) {
volunteersNickName.put(volunteer.nickName.toLowerCase(), volunteer);
result.add(volunteer);
} else {
processDuplicate(originalVolunteer, volunteer);
}
} else {
Volunteer originalVolunteer = volunteersFullName.get(fullName);

if (originalVolunteer == null) {
volunteersFullName.put(fullName, volunteer);
result.add(volunteer);
} else {
processDuplicate(originalVolunteer, volunteer);
}
}
}

return result;
}

private static void processDuplicate(Volunteer original, Volunteer duplicate) {
if (!original.eMail.contains(duplicate.eMail)) {
original.eMail += getSeparator(original.eMail) + duplicate.eMail;
}

if (!original.phone.contains(duplicate.phone)) {
original.phone += getSeparator(original.phone) + duplicate.phone;
}

if (!original.nickName.contains(duplicate.nickName)) {
original.nickName += getSeparator(original.nickName) + duplicate.nickName;
}
}

private static String getSeparator(String str) {
return (!str.equals("") ? ";" : "");
}
}
26 changes: 13 additions & 13 deletions src/main/java/org/example/volunteers/Volunteer.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
import static java.util.stream.Collectors.joining;

public final class Volunteer {
public final String firstName;
public final String lastName;
public final String nickName;
public final String eMail;
public final String phone;
public String firstName;
public String lastName;
public String nickName;
public String eMail;
public String phone;

public Volunteer(
String firstName,
String lastName,
String nickName,
String eMail,
String phone
String firstName,
String lastName,
String nickName,
String eMail,
String phone
) {
this.firstName = firstName;
this.lastName = lastName;
Expand All @@ -27,8 +27,8 @@ public Volunteer(

@Override
public String toString() {
return Arrays.stream(new String[]{firstName,lastName,nickName,eMail,phone})
.map(attribute -> String.format("\"%s\"", attribute))
.collect(joining(";"));
return Arrays.stream(new String[]{firstName, lastName, nickName, eMail, phone})
.map(attribute -> String.format("\"%s\"", attribute))
.collect(joining(";"));
}
}
38 changes: 38 additions & 0 deletions src/main/java/org/example/volunteers/utils/FieldsSanitizer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.example.volunteers.utils;

import java.text.Normalizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FieldsSanitizer {

public String clearName(String name) {
return name.replaceAll("[^\\p{L} ,.'-]", "");
}

public String clearPhone(String phone) {
String result = phone.replaceAll("[^\\d.]|\\.", "");

result = result.substring(Math.max(result.length() - 9, 0));

return result.length() == 9 ? "0" + result : "";
}

public String clearEmail(String email) {
if (email == null || email.isEmpty()) {
return "";
}

String normalizedEmail = Normalizer.normalize(email.toLowerCase(), Normalizer.Form.NFD);
String result = normalizedEmail.replaceAll("\\p{M}", "");

String mailRegex = "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])";

Matcher mailMatcher = Pattern.compile(mailRegex).matcher(result);
if (mailMatcher.find()) {
return mailMatcher.group();
}

return "";
}
}
25 changes: 25 additions & 0 deletions src/test/java/org/example/volunteers/FieldsSanitizerTesting.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.example.volunteers;

import org.example.volunteers.utils.FieldsSanitizer;

public class FieldsSanitizerTesting extends FieldsSanitizer {
public Integer clearNameCount = 0;
public Integer clearPhoneCount = 0;
public Integer clearEmailCount = 0;

public String clearName(String name) {
clearNameCount += 1;
return super.clearName(name);
}

public String clearPhone(String phone) {
clearPhoneCount += 1;
return super.clearPhone(phone);
}

public String clearEmail(String email) {
clearEmailCount += 1;
return super.clearEmail(email);
}

}
134 changes: 134 additions & 0 deletions src/test/java/org/example/volunteers/TestCleaner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package org.example.volunteers;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class TestCleaner {

// Integration test
@Test
public void shouldCallEachFieldSanitizerForEachVolunteers() {
volunteers.add(new Volunteer("leo", "duff", "duff", "leo.duff@voleur.ytb", "+330069696969"));
FieldsSanitizerTesting fieldsSanitizer = new FieldsSanitizerTesting();

Cleaner.cleanUp(volunteers, fieldsSanitizer);

Assertions.assertEquals(2, fieldsSanitizer.clearNameCount);
Assertions.assertEquals(1, fieldsSanitizer.clearPhoneCount);
Assertions.assertEquals(1, fieldsSanitizer.clearEmailCount);
}

// Unit tests
@Test
public void shouldRegroupMails() {
addVolunteers(
new Volunteer("leo", "duff", "duff", "leo.duff@voleur.ytb", "+330069696969"),
new Volunteer("LEO", "duff", "duff", "leo.duff@voleur.twitch", "+330069696969"),
new Volunteer("leo", "DUFF", "duff", "leo.duff@voleur.dailmotion", "+330069696969")
);

List<Volunteer> expectedVolunteers = new ArrayList<>();
expectedVolunteers.add(new Volunteer("leo", "duff", "duff", "leo.duff@voleur.ytb;leo.duff@voleur.twitch;leo.duff@voleur.dailmotion", "0069696969"));

whenCleaningVolunteers();
thenExpectedListIs(expectedVolunteers);
}

@Test
public void shouldRegroupNickNames() {
addVolunteers(
new Volunteer("leo", "duff", "duff", "leo.duff@voleur.ytb", "+330069696969"),
new Volunteer("LEO", "duff", "miguel", "leo.duff@voleur.ytb", "+330069696969"),
new Volunteer("leo", "DUFF", "judo", "leo.duff@voleur.ytb", "+330069696969")
);

List<Volunteer> expectedVolunteers = new ArrayList<>();
expectedVolunteers.add(new Volunteer("leo", "duff", "duff;miguel;judo", "leo.duff@voleur.ytb", "0069696969"));

whenCleaningVolunteers();
thenExpectedListIs(expectedVolunteers);
}

@Test
public void shouldRegroupPhones() {
addVolunteers(
new Volunteer("leo", "duff", "duff", "leo.duff@voleur.ytb", "+330069696969"),
new Volunteer("LEO", "duff", "duff", "leo.duff@voleur.ytb", "+330069696969"),
new Volunteer("leo", "DUFF", "duff", "leo.duff@voleur.ytb", "+330069696968")
);

List<Volunteer> expectedVolunteers = new ArrayList<>();
expectedVolunteers.add(new Volunteer("leo", "duff", "duff", "leo.duff@voleur.ytb", "0069696969;0069696968"));

whenCleaningVolunteers();
thenExpectedListIs(expectedVolunteers);
}

@Test
public void shouldRegroupEmptyNickNames() {
addVolunteers(
new Volunteer("leo", "duff", "", "leo.duff@voleur.ytb", "+330069696969"),
new Volunteer("LEO", "duff", "miguel", "leo.duff@voleur.ytb", "+330069696969"),
new Volunteer("leo", "DUFF", "judo", "leo.duff@voleur.ytb", "+330069696969")
);

List<Volunteer> expectedVolunteers = new ArrayList<>();
expectedVolunteers.add(new Volunteer("leo", "duff", "miguel;judo", "leo.duff@voleur.ytb", "0069696969"));

whenCleaningVolunteers();
thenExpectedListIs(expectedVolunteers);
}

@Test
public void shouldRegroupPhonesOnNickName() {
addVolunteers(
new Volunteer("", "", "roche", "example@voleur.ytb", "+330069696967"),
new Volunteer("", "", "miguel", "example@voleur.ytb", "+330069696969"),
new Volunteer("", "", "roche", "example@voleur.ytb", "+330069696968")
);

List<Volunteer> expectedVolunteers = new ArrayList<>();
expectedVolunteers.add(new Volunteer("", "", "roche", "example@voleur.ytb", "0069696967;0069696968"));
expectedVolunteers.add(new Volunteer("", "", "miguel", "example@voleur.ytb", "0069696969"));

whenCleaningVolunteers();
thenExpectedListIs(expectedVolunteers);
}

@Test
public void shouldRegroupEmailsOnNickName() {
addVolunteers(
new Volunteer("", "", "roche", "example@voleur.ytb", "+330069696968"),
new Volunteer("", "", "miguel", "example@voleur.ytb", "+330069696969"),
new Volunteer("", "", "roche", "example@voleur.twitch", "+330069696968")
);

List<Volunteer> expectedVolunteers = new ArrayList<>();
expectedVolunteers.add(new Volunteer("", "", "roche", "example@voleur.ytb;example@voleur.twitch", "0069696968"));
expectedVolunteers.add(new Volunteer("", "", "miguel", "example@voleur.ytb", "0069696969"));

whenCleaningVolunteers();
thenExpectedListIs(expectedVolunteers);
}

private void addVolunteers(Volunteer... volunteers) {
Collections.addAll(this.volunteers, volunteers);
}

private void whenCleaningVolunteers() {
FieldsSanitizerTesting fieldsSanitizer = new FieldsSanitizerTesting();
cleared = Cleaner.cleanUp(volunteers, fieldsSanitizer);
}

private void thenExpectedListIs(List<Volunteer> expected) {
Assertions.assertEquals(expected.toString(), cleared.toString());
}

private final List<Volunteer> volunteers = new ArrayList<>();
private List<Volunteer> cleared = new ArrayList<>();

}
41 changes: 41 additions & 0 deletions src/test/java/org/example/volunteers/TestFieldsSanitizerEmail.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.example.volunteers;

import org.example.volunteers.utils.FieldsSanitizer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class TestFieldsSanitizerEmail {
FieldsSanitizer fieldsSanitizer = new FieldsSanitizer();


@Test
public void shouldRemoveAccents() {
Assertions.assertEquals("leo.dupuis@quiches.lardon", fieldsSanitizer.clearEmail("léo.dupuis@quiches.lardon"));
}

@Test
public void shouldLowerEmail() {
Assertions.assertEquals("leo.dupuis@quiches.lardon", fieldsSanitizer.clearEmail("Leo.Dupuis@quiches.lardon"));
}

@Test
public void shouldRemoveLastDotWhenExist() {
Assertions.assertEquals("leo.dupuis@quiches.lardon", fieldsSanitizer.clearEmail("Leo.Dupuis@quiches.lardon."));
}

@Test
public void shouldReturnEmptyStringIfInvalid() {
Assertions.assertEquals("", fieldsSanitizer.clearEmail("leo.dupuis@quiches"));
Assertions.assertEquals("", fieldsSanitizer.clearEmail("leo.dupuis"));
}

@Test
public void shouldReturnEmptyStringForEmptyField() {
Assertions.assertEquals("", fieldsSanitizer.clearEmail(""));
}

@Test
public void shouldReturnEmailWhenFieldIsValid() {
Assertions.assertEquals("leo.dupuis@quiches.ovh", fieldsSanitizer.clearEmail("leo.dupuis@quiches.ovh"));
}
}
Loading