본문 바로가기
스터디/[white-ship] 자바 스터디(study hale)

4주차 과제: live-study 대시 보드

by doyoungKim 2021. 1. 2.

 

다음을 만족 시키는 코드를 작성하시오.

  • 깃헙 이슈 1번부터 18번까지 댓글을 순회하며 댓글을 남긴 사용자를 체크 할 것.
  • 참여율을 계산하세요. 총 18회에 중에 몇 %를 참여했는지 소숫점 두자리가지 보여줄 것.

제출 코드는 다음을 통해서 자세히 볼 수 있다.

 

doyoung0205/github-api

Contribute to doyoung0205/github-api development by creating an account on GitHub.

github.com

준비물

먼저, 깃허브 이슈를 코드로 접근하려면 관련 dependency 와 깃허브 접근 수단이 있어야 한다.

 

GitHub API for Java –

What is this? This library defines an object oriented representation of the GitHub API. By "object oriented" we mean there are classes that correspond to the domain model of GitHub (such as GHUser and GHRepository), operations that act on them as defined a

github-api.kohsuke.org

 

필자는 maven을 사용하기 때문에 pom.xml 에 다음과 같이 적는다.

<dependency>
    <groupId>org.kohsuke</groupId>
    <artifactId>github-api</artifactId>
    <version>1.117</version>
</dependency>

깃허브 접근 수단

다음 링크에서 자세히 설명 하고 있다.

 

Creating a personal access token - GitHub Docs

Creating a personal access token You should create a personal access token to use in place of a password with the command line or with the API. Personal access tokens (PATs) are an alternative to using passwords for authentication to GitHub when using the

docs.github.com

 

도메인

준비가 끝났으면 도메인에 대해서 정리를 해보자.

github api 에서의 이슈를 우리는 과제로 이해 하고 있고
이슈의 댓글을 단 유저는 우리는 과제제출자로 이해를 할 것이다.

live-study 대시 보드를 만들기 위해 우리의 도메인을 먼저 정의 해보자.
필자는 모든 참석자와 과제별 참석자 정보를 도메인으로 정의 해보았다.
반복문을 최대한 조금 쓰려다 보니까 도메인이 Collection 만으로 구성된 것 같다..

@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Participation {

    // 모든 참석자
    private Set<String> allUserNames;

    // 과제 이름, 참석자들
    private Map<Integer, Set<String>> taskInfoMap;
}

변역 (translate)

github api 에서 주는 데이터들은 우리의 도메인 다르기 때문에
우리의 도메인으로 변역을 해주는 과정을 거쳐보자.

@Log
@Repository
public class TranslateGithubAPI {

    @Value("${oauthToken}")
    private String oauthToken;

    public Optional<Participation> translate() throws IOException {

        log.info("github api 접속 요청");
        final GitHub gitHub = new GitHubBuilder().withOAuthToken(oauthToken).build();
        log.info("github api 접속 완료");

        final String repositoryName = "whiteship/live-study";
        log.info("github repository 접속 요청");
        final GHRepository repository = gitHub.getRepository(repositoryName);
        log.info("github repository 접속 완료");


        log.info("github 이슈 조회 요청");
        final List<GHIssue> allIssues = repository.getIssues(GHIssueState.ALL);
        log.info("github 이슈 조회 완료");

        // 모든 참가자
        // 중복제거
        // 자동으로 정렬
        final Set<String> allUserIds = new TreeSet<>();

        // 과제 별 제출 정보
        final Map<Integer, Set<String>> taskInfoMap = new HashMap<>();
        final String TASK_MANGER_NAME = "Keesun Baik";
        final String COMMON_TITLE_WORD = "주";
        log.info("github translate 시작");
        for (GHIssue issue : allIssues) {

            // 백기선님 글만 담기
            final String issueWriterName = issue.getUser().getName();
            if (!issueWriterName.equals(TASK_MANGER_NAME)) continue;
            

            // "주" 앞의 숫자 가져오기
            String issueTitle = issue.getTitle();
            issueTitle = issueTitle.substring(0, issueTitle.indexOf(COMMON_TITLE_WORD));
            final int taskWeekNumber = Integer.parseInt(issueTitle);

            final Set<String> userIds = new HashSet<>();

            for (GHIssueComment comment : issue.getComments()) {
                final String commentUserId = comment.getUser().getLogin();
                allUserIds.add(commentUserId);
                userIds.add(commentUserId);
            }

            // 과제 별 제출자
            taskInfoMap.put(taskWeekNumber, userIds);
        }

        log.info("github translate 완료");
        return Optional.of(Participation.builder().
                allUserNames(allUserIds)
                .taskInfoMap(taskInfoMap)
                .build());
    }


}

 

participation service

외부 github api 에서 가져온 데이터를 도메인인 Participation 으로 바꿨다면,

대시보드를 만들 때 쓸 수 있도록  participation service 를 만들어보자.

@Log
@Service
@RequiredArgsConstructor
public class ParticipationService {

    private final TranslateGithubAPI translateGithub;

    public Optional<Participation> findParticipationAll() {
        try {
            final Participation participation = translateGithub.translate().orElseThrow(() -> new IllegalAccessError("번역을 잘못했어영"));
            log.info("github api 변역 완료");
            return Optional.of(participation);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Optional.empty();
    }

}

대시보드 VIEW

그렇다면 이제 대시보드를 그리는 코드를 작성해보자.

대시보드는 Application 이 실행되면 바로 실행 할 수 있도록 설정 했다.
그리고 대시보드는 markdown 형태의 글들로 작성하였고
작성된 내용은 프로젝트 README.md 파일로 생성 되도록 만들었다.

@Log
@Service
@RequiredArgsConstructor
public class DashBoardService {

    private final ParticipationService participationService;

    @EventListener(ApplicationReadyEvent.class)
    public void createViewFromParticipation() {
        final Participation participation = participationService.findParticipationAll().orElseThrow(() -> new IllegalArgumentException("findParticipationAll error"));
        final String contents = getContents(participation);

        try {
            log.info("README 작성 시작");

            final String path = System.getProperty("user.dir");
            File file = new File(path + "/README.md");
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(contents.getBytes());
            log.info("README 작성 완료");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String getContents(Participation participation) {

        final Map<Integer, Set<String>> taskInfoMap = participation.getTaskInfoMap();

        final List<Map.Entry<Integer, Set<String>>> sortedTaskInfoMap = taskInfoMap.entrySet()
                .stream()
                .sorted(Map.Entry.comparingByKey())
                .collect(Collectors.toList());


        final Set<String> allUserNames = participation.getAllUserNames();

        // 총 과제 수
        final double totalTaskCount = taskInfoMap.keySet().size();

        // 제목
        String subject = "### 스터디 참여 현황 \n";

        log.info("테이블 제목 작성 시작");
        StringBuilder tableHeading = new StringBuilder("| 참여자 ");
        StringBuilder tableLine = new StringBuilder("|---|:");
        for (Map.Entry<Integer, Set<String>> entry : sortedTaskInfoMap) {
            String ISSUE_TITLE_SUFFIX = "주차";
            final String issueTitle = String.format("| %s ", entry.getKey() + ISSUE_TITLE_SUFFIX);
            tableHeading.append(issueTitle);
            tableLine.append("---:|");
        }

        tableHeading.append(" | 참여율 | \n");
        tableLine.append("---:| \n");
        log.info("테이블 제목 작성 완료");


        log.info("테이블 컨텐츠 작성 시작");
        StringBuilder tableRows = new StringBuilder();
        for (String userName : allUserNames) {

            tableRows.append("| ").append(userName);
            double submittedCount = 0;

            for (Map.Entry<Integer, Set<String>> entry : sortedTaskInfoMap) {
                if (entry.getValue().contains(userName)) {
                    tableRows.append("| ✅ ");
                    submittedCount += 1;
                    continue;
                }
                tableRows.append("|  ");
            }


            double taskRate = (submittedCount * 100) / totalTaskCount;
            tableRows.append(" | ` ").append(String.format("%.2f", taskRate)).append("% ` | \n");
        }

        log.info("테이블 컨텐츠 작성 종료");
        return subject +
                tableHeading +
                tableLine +
                tableRows;

    }
}

이제 어플리케이션을 작동 시켜보면 README.md 파일이 생성된 것 을 볼 수 있다.

생성된 README 파일
REAED.md 파일 내부

728x90

'스터디 > [white-ship] 자바 스터디(study hale)' 카테고리의 다른 글

3주차 과제: 연산자  (0) 2021.01.02
4주차 과제: 제어문  (0) 2021.01.02
4주차 과제 LinkedList  (0) 2021.01.02
4주차 과제: Stack  (0) 2021.01.02
4주차 과제: Queue  (0) 2021.01.02

댓글