UCSBGradeHistoryServiceImpl.java

package edu.ucsb.cs156.courses.services;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.opencsv.CSVReader;
import edu.ucsb.cs156.courses.entities.GradeHistory;
import edu.ucsb.cs156.courses.models.ApiResult;
import edu.ucsb.cs156.courses.models.Quarter;
import edu.ucsb.cs156.courses.models.TreeElement;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service("UCSBGradeHistoryService")
@Slf4j
public class UCSBGradeHistoryServiceImpl implements UCSBGradeHistoryService {

  private final RestTemplate restTemplate;

  public UCSBGradeHistoryServiceImpl(RestTemplateBuilder restTemplateBuilder) {
    restTemplate = restTemplateBuilder.build();
  }

  @Autowired ObjectMapper mapper;

  public static final String REPO_OWNER_AND_NAME = "ucsb-cs156/UCSB_Grades";
  public static final String API_ENDPOINT =
      "https://api.github.com/repos/" + REPO_OWNER_AND_NAME + "/git/trees/main?recursive=1";

  @Override
  public List<String> getUrls() throws Exception {

    log.info("getting data from {}", API_ENDPOINT);

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(List.of(MediaType.APPLICATION_JSON));
    headers.setContentType(MediaType.APPLICATION_JSON);

    HttpEntity<String> entity = new HttpEntity<>(headers);
    Map<String, String> uriVariables = Map.of("recursive", "1");

    ResponseEntity<String> re =
        restTemplate.exchange(API_ENDPOINT, HttpMethod.GET, entity, String.class, uriVariables);

    ApiResult apiResult = mapper.readValue(re.getBody(), ApiResult.class);

    List<TreeElement> treeElements = apiResult.getTree();
    List<String> urls =
        treeElements.stream()
            .map(treeElement -> treeElement.getPath())
            .filter(path -> (path.startsWith("quarters/") && path.endsWith(".csv")))
            .collect(Collectors.toList());
    List<String> rawUrls =
        urls.stream()
            .map(url -> "https://raw.githubusercontent.com/" + REPO_OWNER_AND_NAME + "/main/" + url)
            .collect(Collectors.toList());
    return rawUrls;
  }

  @Override
  public List<GradeHistory> getGradeData(String url) throws Exception {
    log.info("getting data from {}", url);
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(List.of(MediaType.APPLICATION_JSON));
    headers.setContentType(MediaType.APPLICATION_JSON);

    HttpEntity<String> entity = new HttpEntity<>(headers);
    ResponseEntity<String> re = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
    String csvData = re.getBody();
    return parse(new StringReader(csvData));
  }

  @Override
  public List<GradeHistory> parse(Reader reader) throws Exception {
    List<GradeHistory> gradeHistoryList = new ArrayList<GradeHistory>();
    log.info("Parsing CSV file with grade history... ");
    CSVReader csvReader = new CSVReader(reader);
    csvReader.skip(1);
    List<String[]> myEntries = csvReader.readAll();
    for (String[] row : myEntries) {
      String yyyyq = Integer.toString(Quarter.qyyToyyyyQ(row[0]));
      GradeHistory gradeHistory =
          GradeHistory.builder()
              .yyyyq(yyyyq)
              .course(row[2])
              .instructor(row[3])
              .grade(row[4])
              .count(Integer.parseInt(row[5]))
              .build();
      log.info("Parsed: " + gradeHistory.toString());
      gradeHistoryList.add(gradeHistory);
    }
    csvReader.close();
    return gradeHistoryList;
  }
}