Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Group 2: Refactoring and documentation of ImportCSV.java #19

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@RestController
@CrossOrigin(exposedHeaders = "errors, content-type")
Expand All @@ -26,114 +26,198 @@ public class ImportCSV {
@Autowired
private ClinicService clinicService;


/** importPets: Parses a csv message and adds/deletes pets from the clinicService
* @param csv
* @return ResponseEntity<List<Pet>>
*/
@PreAuthorize("hasRole(@roles.OWNER_ADMIN)")
@RequestMapping(value = "importPets",
method = RequestMethod.POST,
consumes = "text/plain",
produces = "application/json")
public ResponseEntity<List<Pet>> importPets(@RequestBody String csv) {

int i = 0;
List<Pet> pets = new LinkedList<Pet>();
Pet pet;

do {
pet = new Pet();

String field = "";
while (i < csv.length() && csv.charAt(i) != ';') {
field += csv.charAt(i++);
// the method has to renamed - it doesn't just import, it also deletes
List<Pet> petsToAdd = new LinkedList<>();
List<Pet> petsToDelete = new LinkedList<>();
for (String line : csv.split("\\r?\\n")) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could think about extracting the most contents of importPets to a new method, so importPets is only responsible about handling the request and the new method takes care of orchestrating the parsing and so on (probably you would have done this with more time :) )

String[] fields = line.split(";");

if (fields.length < 5) {
return errorResponse("not enough values");
}
i++;

pet.setName(field);
Pet pet = new Pet();
pet.setName(fields[0]);
SettingResponse settingResponse;

field = "";
while (i < csv.length() && csv.charAt(i) != ';') {
field += csv.charAt(i++);
}
i++;

try {
pet.setBirthDate((new SimpleDateFormat("yyyy-MM-dd")).parse(field));
} catch (ParseException e) {
HttpHeaders headers = new HttpHeaders();
headers.add("errors", "date " + field + " not valid");
return new ResponseEntity<List<Pet>>(headers, HttpStatus.BAD_REQUEST);
settingResponse = setDateOfBirth(pet, fields[1]);
if (!settingResponse.isSuccessful) {
return errorResponse(settingResponse.errorMessage);
}

field = "";
while (i < csv.length() && csv.charAt(i) != ';') {
field += csv.charAt(i++);
}
i++;

if (pet != null) {
ArrayList<PetType> ts = (ArrayList<PetType>) clinicService.findPetTypes();
for (int j = 0; j < ts.size(); j++) {
if (ts.get(j).getName().toLowerCase().equals(field)) {
pet.setType(ts.get(j));
break;
}
}
settingResponse = setPetType(pet, fields[2]);
if (!settingResponse.isSuccessful) {
return errorResponse(settingResponse.errorMessage);
}

field = "";
while (i < csv.length() && (csv.charAt(i) != ';' && csv.charAt(i) != '\n')) {
field += csv.charAt(i++);
settingResponse = setOwner(pet, fields[3]);
if (!settingResponse.isSuccessful) {
return errorResponse(settingResponse.errorMessage);
}

if (pet != null) {
String owner = field;
List<Owner> matchingOwners = clinicService.findAllOwners()
.stream()
.filter(o -> o.getLastName().equals(owner))
.collect(Collectors.toList());

if (matchingOwners.size() == 0) {
HttpHeaders headers = new HttpHeaders();
headers.add("errors", "Owner not found");
return new ResponseEntity<List<Pet>>(headers, HttpStatus.BAD_REQUEST);
}
if (matchingOwners.size() > 1) {
HttpHeaders headers = new HttpHeaders();
headers.add("errors", "Owner not unique");
return new ResponseEntity<List<Pet>>(headers, HttpStatus.BAD_REQUEST);
}
pet.setOwner(matchingOwners.iterator().next());
String action = fields[4];
if (action.equalsIgnoreCase("add")) {
petsToAdd.add(pet);
} else if (action.equalsIgnoreCase("delete")) {
petsToDelete.add(pet);
} else {
return errorResponse("invalid action");
}
}

for (Pet p : petsToAdd) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice to also see such a solution ... you are the first ones so far that moved this functionality to end if all pets can be parsed...
it can get a bit tricky then to keep the same order of execution as intended by the original csv

clinicService.savePet(p);
}

for (Pet p : petsToDelete) {
deletePet(p);
}

return new ResponseEntity<>(
Stream.concat(
petsToAdd.stream(),
petsToDelete.stream()
).collect(Collectors.toList()),
HttpStatus.OK
);
}

if (csv.charAt(i) == ';') {
i++;

field = "";
while (i < csv.length() && csv.charAt(i) != '\n') {
field += csv.charAt(i++);
}

if (field.toLowerCase().equals("add")) {
clinicService.savePet(pet);
} else {
for (Pet q : pet.getOwner().getPets()) {
if (q.getName().equals(pet.getName())) {
if (q.getType().getId().equals(pet.getType().getId())) {
if (pet.getBirthDate().equals(q.getBirthDate())) {
clinicService.deletePet(q);
}
}
}
}
}
private static class SettingResponse {
public boolean isSuccessful;
public String errorMessage;

} else {
clinicService.savePet(pet);
}
i++;
public SettingResponse() {
this.isSuccessful = true;
this.errorMessage = null;
}

public SettingResponse(String errorMessage) {
this.isSuccessful = false;
this.errorMessage = errorMessage;
}
}


/** errorResponse: Creates an Http message with a bad request and a user defined message (headerValue).
* @param headerValue
* @return ResponseEntity<List<Pet>>
*/
private ResponseEntity<List<Pet>> errorResponse(String headerValue) {
HttpHeaders headers = new HttpHeaders();
headers.add("errors", headerValue);
return new ResponseEntity<>(headers, HttpStatus.BAD_REQUEST);
}


/** setDateOfBirth: Set's birthday to a pet, by parsing a string into a date.
* @param pet
* @param dateOfBirth
* @return SettingResponse
*/
private SettingResponse setDateOfBirth(Pet pet, String dateOfBirth) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that you extracted and created this and the below functions, which fulfill one single purpose, are small and you also kept the indentations small :)

try {
pet.setBirthDate((new SimpleDateFormat("yyyy-MM-dd")).parse(dateOfBirth));
return new SettingResponse();
} catch (ParseException e) {
return new SettingResponse("date " + dateOfBirth + " not valid");
}
}


/** setPetType: Assigns a pet type to a pet.
* @param pet
* @param petTypeName
* @return SettingResponse
*/
private SettingResponse setPetType(Pet pet, String petTypeName) {
PetType petType = findPetTypeByName(petTypeName);
if (petType != null) {
pet.setType(petType);
return new SettingResponse();
}
return new SettingResponse("type " + petTypeName + " not valid");
}


/** setOwner: Sets an owner to a pet, returns errors if not found.
* @param pet
* @param ownerName
* @return SettingResponse
*/
private SettingResponse setOwner(Pet pet, String ownerName) {
List<Owner> matchingOwners = findOwnersByName(ownerName);
if (matchingOwners.size() == 1) {
pet.setOwner(matchingOwners.iterator().next());
return new SettingResponse();
} else if (matchingOwners.size() > 1) {
return new SettingResponse("Owner not unique");
} else {
return new SettingResponse("Owner not found");
}
}


/** findPetTypeByName: Finds a pet in the clinicService, and returns null if not found.
* @param name
* @return PetType
*/
private PetType findPetTypeByName(String name) {
return clinicService.findPetTypes().stream().filter(
petType -> name.equals(petType.getName().toLowerCase())
).findFirst().orElse(null);
}

pets.add(pet);

/** findOwnersByName: Finds a list of owners by it's name.
* @param name
* @return List<Owner>
*/
private List<Owner> findOwnersByName(String name) {
return clinicService.findAllOwners()
.stream()
.filter(o -> o.getLastName().equals(name))
.collect(Collectors.toList());
}

} while (i < csv.length() && pet != null);

/** deletePet: Deletes a pet from the clinicService.
* @param pet
*/
private void deletePet(Pet pet) {
for (Pet existingPet : pet.getOwner().getPets()) {
if (equalPets(pet, existingPet)) {
clinicService.deletePet(existingPet);
}
}
}

return new ResponseEntity<List<Pet>>(pets, HttpStatus.OK);

/** equalPets: Compares if two pets are the same, by comparing name, type and birthdate.
* @param pet1
* @param pet2
* @return boolean
*/
private boolean equalPets(Pet pet1, Pet pet2) {
if (!pet1.getName().equals(pet2.getName())) {
return false;
}
if (!pet1.getType().getId().equals(pet2.getType().getId())) {
return false;
}
if (!pet1.getBirthDate().equals(pet2.getBirthDate())) {
return false;
}
return true;
}
}