-
Notifications
You must be signed in to change notification settings - Fork 0
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
Feat: add draft for data module #28
Merged
Merged
Changes from 1 commit
Commits
Show all changes
128 commits
Select commit
Hold shift + click to select a range
c5210c0
Feat: add draft for data module
HC-kang 1cf4c52
fix: type erros with jest-dom
DaleSeo 56e239f
ci: add integration workflow
DaleSeo 3f79d97
chore: do not clutter test output with errors
DaleSeo 02b602b
fix: global css for storybook
Sunjae95 31bbf09
Merge pull request #32 from DaleStudy/30-storybook-global-css
Sunjae95 feaaa8f
Merge pull request #29 from DaleStudy/ci-integration
DaleSeo 38021c1
feat : implement progress component
SamTheKorean d8f96be
feat : implement progress story
SamTheKorean 9f163b7
feat : implement progress test
SamTheKorean 6fbfe7a
feat : implement index page
SamTheKorean e6590c1
fix : refactor test cases and remove unused dependancy
SamTheKorean d4639a7
Merge pull request #31 from DaleStudy/sam/create-progress-page
SamTheKorean 2378ee2
fix : clean up css not required
SamTheKorean 0c95ee6
feat : implement leaderboard component
SamTheKorean 7069566
feat : implement story for leaderboard
SamTheKorean ed91ab0
feat : implement unit test for leaderboard
SamTheKorean f13112a
feat : implement index page
SamTheKorean 8b0ba70
fix : refactor test and remove unused imports
SamTheKorean ee0c9cc
ci: add deployment workflow
DaleSeo 86bb7ca
feat: clean up member data module
HC-kang f5dec9c
fix : apply feadback with test cases and htnl structure
SamTheKorean ddb16ec
fix: 데이터 모듈에서 동일한 멤버가 여러 팀에 속한 경우 중복 제거
HC-kang 65f05cc
test: add test code for get data service and clients
HC-kang 204e9de
refactor: move types to each directory
HC-kang 2ffaef9
fix: data module type import error
HC-kang 6a21ba6
remove: unused file
HC-kang fda3341
test: replace console error mock with spy
HC-kang 9a43193
Merge pull request #34 from DaleStudy/ci-deployment
DaleSeo 363ff4a
docs: contributing and pr template
DaleSeo 969f60d
fix : adjust folder and filenames for convention
SamTheKorean 0865793
Merge pull request #17 from DaleStudy/sam/create-leaderboard-page
SamTheKorean 4230f14
test: Certificate page
Sunjae95 b8322bf
story: Certificate Page
Sunjae95 cf784b1
rename: certificate from src to components
Sunjae95 bfeba2d
fix: review apply
Sunjae95 a6c9946
Merge pull request #18 from DaleStudy/15-certificate-only-frame
Sunjae95 1798d60
Refactor: modify format and types without changing test cases
HC-kang 8b50286
Merge pull request #40 from DaleStudy/docs-contributing
DaleSeo d2ee200
Refactor: encapsulate fetch client
HC-kang d405e1b
Rename: API_VERSION to MEDIA_TYPE
HC-kang e4af42b
Refactor: modify handle error function
HC-kang ef78a24
Test: enhance test cases
HC-kang 21ee78c
separate documents and scripts per page
DaleSeo dfe5b0c
configure vite to support mpa
DaleSeo 044e63b
deps: remove react-router-dom
DaleSeo d140a4d
feat: remove ErrorPage
DaleSeo c31a5b5
test: address broken tests
DaleSeo c33c3cd
remove progress folder
DaleSeo 9f8b2d0
create Progress folder
DaleSeo 212435d
Merge pull request #44 from DaleStudy/mpa
DaleSeo 86e9e2c
remove: unused variables
HC-kang 24866ca
feat: add attr to MemberInfo and rename GithubInfo to StudyInfo
HC-kang b7f5ab4
refactor: createMemberInfoMap function
HC-kang dd84127
fix: type import
HC-kang b47f411
remove: fetch client
HC-kang 9cc85bf
simplify: github client
HC-kang 0c2962d
simplify: member info service
HC-kang 7d577ff
remove: legacy codes
HC-kang 6c94c93
refactor: modify directory structure and divide types
HC-kang f36372b
test: add tests for modules
HC-kang 4922cc5
feat: add store service
HC-kang 3837225
test: fix side effects of type simplification
HC-kang b1eb0ed
test: add store service test
HC-kang b561ba1
feat: add store service local storage cache
HC-kang db3e480
feat: add hard refresh to get data
HC-kang a163cb3
chore: cleanup directory structure
HC-kang 4946eaf
typo
HC-kang 09a8ba8
rename: modify file name and remove cache
HC-kang 38d492f
remove: renamed file
HC-kang 90197a9
feat: add renamed file
HC-kang 747d927
rename: rename file
HC-kang ec2c708
rename: rename file
HC-kang 2d66dc7
rename: github directory
HC-kang 4f57007
rename: gitHub directory
HC-kang bb3dc47
remove: caching feature in store service
HC-kang f7c7eb1
style: arrow to function for exported functions
HC-kang 453464f
test: fix mock type safety
HC-kang 6825e8d
remove: unused features
HC-kang 1f3f11c
test: update test for feature change
HC-kang d814bca
refactor: apply review comments
HC-kang 0d0747d
test: remove invalid cohort test
HC-kang b3ab1d4
Feat: add draft for data module
HC-kang 98ff3cc
feat: clean up member data module
HC-kang 7e8b9d2
fix: 데이터 모듈에서 동일한 멤버가 여러 팀에 속한 경우 중복 제거
HC-kang a25315f
test: add test code for get data service and clients
HC-kang 1abdff9
refactor: move types to each directory
HC-kang 2b9d62d
fix: data module type import error
HC-kang acbb811
remove: unused file
HC-kang 3531a92
test: replace console error mock with spy
HC-kang 606ef44
Refactor: modify format and types without changing test cases
HC-kang e2249dd
Refactor: encapsulate fetch client
HC-kang a2252ed
Rename: API_VERSION to MEDIA_TYPE
HC-kang 0577a41
Refactor: modify handle error function
HC-kang 6edad94
Test: enhance test cases
HC-kang 8ac9720
remove: unused variables
HC-kang 72f710b
feat: add attr to MemberInfo and rename GithubInfo to StudyInfo
HC-kang 842aa44
refactor: createMemberInfoMap function
HC-kang a37e7a1
fix: type import
HC-kang d295ca2
remove: fetch client
HC-kang e78f71a
simplify: github client
HC-kang ce89bd5
simplify: member info service
HC-kang c28a759
remove: legacy codes
HC-kang 0370bd0
refactor: modify directory structure and divide types
HC-kang fc49ddf
test: add tests for modules
HC-kang 8f8c5a3
feat: add store service
HC-kang 265f97b
test: fix side effects of type simplification
HC-kang 8305e9b
test: add store service test
HC-kang 57e9ff3
feat: add store service local storage cache
HC-kang 2ae8ade
feat: add hard refresh to get data
HC-kang 77ac38d
chore: cleanup directory structure
HC-kang e39bbde
typo
HC-kang 2048e99
rename: modify file name and remove cache
HC-kang fadf497
remove: renamed file
HC-kang f4806d6
feat: add renamed file
HC-kang 049de21
rename: rename file
HC-kang 3319125
rename: rename file
HC-kang 2498290
rename: github directory
HC-kang dd44373
rename: gitHub directory
HC-kang b298f45
remove: caching feature in store service
HC-kang 1c4aae0
style: arrow to function for exported functions
HC-kang 6f70aa0
test: fix mock type safety
HC-kang 7bbd017
remove: unused features
HC-kang ff22c69
test: update test for feature change
HC-kang 618236a
refactor: apply review comments
HC-kang b7ef969
test: remove invalid cohort test
HC-kang dafdbfd
typo: 오탈자 수정
HC-kang 31e28ac
Merge branch 'feature/25-add-data-retrieve-module' of https://github.…
HC-kang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
export const GITHUB_API_BASE_URL = 'https://api.github.com'; | ||
export const TOTAL_PROBLEMS = 75; | ||
|
||
export const ALTERNATIVE_IDS: Record<string, string> = { | ||
// 1기 | ||
meoooh: 'han', | ||
koreas9408: 'seunghyun-lim', | ||
leokim0922: 'leo', | ||
|
||
// 2기 | ||
obzva: 'flynn', | ||
'kim-young': 'kimyoung', | ||
kjb512: 'kayden', | ||
lymchgmk: 'egon', | ||
jeonghwanmin: 'hwanmini', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { ALTERNATIVE_IDS, GITHUB_API_BASE_URL } from './const'; | ||
import { Cohort, CohortInfo } from './types'; | ||
import { fetchWithCache } from './utils'; | ||
|
||
const GITHUB_TOKEN = 'process.env.GITHUB_TOKEN'; // TODO: Github Token을 환경변수로 설정 | ||
|
||
export async function getMembersByCohort(cohort: number): Promise<CohortInfo> { | ||
return fetchWithCache(`members_cohort_${cohort}`, async () => { | ||
const teamName = ['', 'leetcode01', 'leetcode']; // TODO: getTeams 에서 가져와 주입 받도록 수정 | ||
const url = `/orgs/DaleStudy/teams/${teamName[cohort]}/members`; | ||
const headers = { | ||
Accept: 'application/vnd.github+json', | ||
Authorization: `Bearer ${GITHUB_TOKEN}`, | ||
}; | ||
|
||
try { | ||
const res = await fetch(GITHUB_API_BASE_URL + url, { headers }); | ||
|
||
if (!res.ok) | ||
throw new Error(`Failed to fetch members: ${res.statusText}`); | ||
|
||
const data = await res.json(); | ||
return { | ||
cohort: cohort as Cohort, | ||
totalMembers: data.length, | ||
members: data.map((member: { login: string }) => ({ | ||
id: | ||
ALTERNATIVE_IDS[member.login.toLowerCase()] || // 파일명이 Github 계정과 다른 경우 | ||
member.login.toLowerCase(), | ||
name: member.login, | ||
})), | ||
}; | ||
} catch (err) { | ||
if (err instanceof Error) { | ||
console.error(`Error fetching cohort members: ${err.message}`); | ||
} else { | ||
console.error('Error fetching cohort members:', err); | ||
} | ||
return { cohort: cohort as Cohort, totalMembers: 0, members: [] }; | ||
} | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { GITHUB_API_BASE_URL } from './const'; | ||
import { RepositoryTree } from './types'; | ||
import { fetchWithCache } from './utils'; | ||
|
||
export async function getRepositoryDirectoryData(): Promise<RepositoryTree[]> { | ||
return fetchWithCache('repository_directory', async () => { | ||
const url = `/repos/DaleStudy/leetcode-study/git/trees/main?recursive=1`; // recursive=1로 충분하다. | ||
const headers = { | ||
Accept: 'application/vnd.github+json', | ||
}; | ||
|
||
try { | ||
const res = await fetch(GITHUB_API_BASE_URL + url, { headers }); | ||
|
||
if (!res.ok) | ||
throw new Error( | ||
`Failed to fetch repository directory: ${res.statusText}` | ||
); | ||
|
||
const data = await res.json(); | ||
return data.tree; | ||
} catch (err) { | ||
if (err instanceof Error) { | ||
console.error(`Error fetching repository directory: ${err.message}`); | ||
} else { | ||
console.error('Error fetching repository directory:', err); | ||
} | ||
return []; | ||
} | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { RepositoryTree, SubmissionOfMember, SubmissionPath } from './types'; | ||
import { getMembersByCohort } from './getMembersByCohort'; | ||
import { getRepositoryDirectoryData } from './getRepoDirectoryData'; | ||
import { TOTAL_PROBLEMS } from './const'; | ||
|
||
/** | ||
* 전체 트리에서 필요한 데이터만 추출 | ||
* 블롭이면서 /를 포함하는 데이터 | ||
*/ | ||
function extractRelevantData(data: RepositoryTree[]): SubmissionPath[] { | ||
return data | ||
.filter((item) => item.type === 'blob') | ||
.filter((item) => item.path.includes('/')) | ||
.map((item) => item.path.toLowerCase() as SubmissionPath); | ||
} | ||
|
||
/** | ||
* 제출 경로를 분석하여 필요한 정보를 추출 | ||
*/ | ||
function parseSubmissionPath(path: SubmissionPath): { | ||
memberId: string; | ||
problemTitle: string; | ||
language: string; | ||
} { | ||
const regex = /^([^/]+)\/([^.]+)\.([a-zA-Z0-9]+)$/; | ||
const match = path.match(regex); | ||
|
||
if (match) { | ||
const problemTitle = match[1]; | ||
const memberId = match[2]; | ||
const language = match[3]; | ||
return { memberId, problemTitle, language }; | ||
} | ||
return { memberId: '', problemTitle: '', language: '' }; | ||
} | ||
|
||
/** | ||
* 각 멤버별 제출 현황 디스플레이 | ||
*/ | ||
function displayProgress( | ||
memberMap: Record<string, SubmissionOfMember> | ||
): string[] { | ||
const progressDisplay: string[] = []; | ||
const maxMemberIdLength = Math.max( | ||
...Object.values(memberMap).map((member) => member.memberId.length) | ||
); | ||
Object.values(memberMap).forEach((member) => { | ||
const progressPercentage = (member.totalSubmissions / TOTAL_PROBLEMS) * 100; | ||
const progressBarLength = Math.ceil(progressPercentage / 2); | ||
const progressBar = '█'.repeat(progressBarLength).padEnd(50, ' '); | ||
|
||
progressDisplay.push( | ||
`${member.memberId.padEnd(maxMemberIdLength)} | ${progressBar} | ${ | ||
member.totalSubmissions | ||
}/${TOTAL_PROBLEMS} (${progressPercentage.toFixed(2)}%)` | ||
); | ||
}); | ||
return progressDisplay; | ||
} | ||
|
||
/** | ||
* 주어진 기수별 제출 현황을 출력한다. | ||
*/ | ||
export async function printProcess(cohort: number) { | ||
try { | ||
// TODO: getTeams 추가 | ||
// 필요한 데이터 조회 | ||
const [membersOfCohort, repositoryDirectory] = await Promise.all([ | ||
getMembersByCohort(cohort), | ||
getRepositoryDirectoryData(), | ||
]); | ||
|
||
// 멤버별 제출 정보를 담을 객체 생성 | ||
const memberMap: Record<string, SubmissionOfMember> = {}; | ||
membersOfCohort.members.forEach((member) => { | ||
memberMap[member.id] = { | ||
memberId: member.id, | ||
totalSubmissions: 0, | ||
submissions: [], | ||
}; | ||
}); | ||
|
||
// 제출 정보를 memberMap에 저장 | ||
const relevantData = extractRelevantData(repositoryDirectory); | ||
relevantData.forEach((path) => { | ||
const { memberId, problemTitle, language } = parseSubmissionPath(path); | ||
if (memberMap[memberId]) { | ||
if ( // 다수의 언어로 제출한 경우 중복 제출로 간주하지 않음 | ||
!memberMap[memberId].submissions.some( | ||
(s) => s.problemTitle === problemTitle | ||
) | ||
) { | ||
memberMap[memberId].totalSubmissions += 1; | ||
} | ||
memberMap[memberId].submissions.push({ | ||
memberId, | ||
problemTitle, | ||
language, | ||
}); | ||
} | ||
}); | ||
|
||
const progressArray = displayProgress(memberMap); | ||
const progressString = progressArray.join('\n'); // 문자열로 변환 | ||
|
||
console.log(progressString); | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
console.error(`An error occurred: ${error.message}`); | ||
} else { | ||
console.error('An error occurred:', error); | ||
} | ||
} | ||
} | ||
|
||
// 전체 함수 호출 | ||
await printProcess(2); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
export type Cohort = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; | ||
|
||
export type CohortInfo = { | ||
cohort: Cohort; | ||
totalMembers: number; | ||
members: Member[]; | ||
}; | ||
|
||
export type Member = { | ||
id: string; | ||
name: string; | ||
}; | ||
|
||
export type RepositoryTree = { | ||
path: string; | ||
mode: string; | ||
type: string; | ||
sha: string; | ||
url: string; | ||
size: number; | ||
}; | ||
|
||
export type SubmissionPath = string; | ||
|
||
export type Submission = { | ||
memberId: string; | ||
problemTitle: string; | ||
language: string; | ||
}; | ||
|
||
export type SubmissionOfMember = { | ||
memberId: string; | ||
totalSubmissions: number; | ||
submissions: Submission[]; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// 캐시를 저장할 객체 | ||
const cache: Record<string, { data: unknown; timestamp: number }> = {}; | ||
const CACHE_DURATION = 10 * 60 * 1000; // 10분 (밀리초 단위) | ||
|
||
// 캐시를 이용한 데이터 요청 함수 | ||
export async function fetchWithCache<T>( | ||
key: string, | ||
fetchFunction: () => Promise<T> | ||
): Promise<T> { | ||
const currentTime = Date.now(); | ||
const cached = cache[key]; | ||
|
||
// 캐시가 존재하고 만료되지 않았다면 데이터 반환 | ||
if (cached) { | ||
if (currentTime - cached.timestamp < CACHE_DURATION) { | ||
console.log( | ||
`${new Date( | ||
currentTime | ||
).toISOString()} - Returning cached data for key: ${key}` | ||
); | ||
return cached.data as T; | ||
} else { | ||
// 만료된 캐시는 삭제 | ||
console.log( | ||
`${new Date( | ||
currentTime | ||
).toISOString()} - Cache expired for key: ${key}, removing from cache` | ||
); | ||
delete cache[key]; | ||
} | ||
} | ||
|
||
// API 호출 후 새로 캐시에 저장 | ||
const data = await fetchFunction(); | ||
cache[key] = { data, timestamp: currentTime }; | ||
console.log( | ||
`${new Date(currentTime).toISOString()} - Fetched new data for key: ${key}` | ||
); | ||
return data; | ||
} | ||
|
||
// 요청을 추적하기 위한 캐시 | ||
const rateLimitCache: Record<string, { count: number; timestamp: number }> = {}; | ||
const RATE_LIMIT_DURATION = 60 * 1000; // 1분 (밀리초 단위) | ||
const MAX_REQUESTS_PER_MINUTE = 60; // 1분당 최대 60회 요청 | ||
|
||
// 레이트 리미트 체크 함수 | ||
export function isRateLimited(ip: string): boolean { | ||
const currentTime = Date.now(); | ||
const cacheEntry = rateLimitCache[ip]; | ||
|
||
// 캐시에 IP가 없거나, 제한 시간이 지난 경우 초기화 | ||
if (!cacheEntry || currentTime - cacheEntry.timestamp > RATE_LIMIT_DURATION) { | ||
rateLimitCache[ip] = { count: 1, timestamp: currentTime }; | ||
return false; | ||
} | ||
|
||
// 요청 횟수를 초과한 경우 | ||
if (cacheEntry.count >= MAX_REQUESTS_PER_MINUTE) { | ||
return true; | ||
} | ||
|
||
// 요청 횟수를 증가시킴 | ||
cacheEntry.count += 1; | ||
return false; | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
타입을 이렇게 하나의 파일로 중앙화 시키셨네요. 코드를 읽기 좀 더 수월하도록 함수 근처에 타입을 두는 것도 고려해보셨으면 좋겠습니다.