diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..42d06c5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,20 @@ +{ + "cSpell.words": [ + "AUTONUMFORMAT", + "BINITEM", + "borderfill", + "Borfer", + "BOTTOMBORDER", + "CELLZONE", + "charshape", + "chartxml", + "COLDEF", + "hwpx", + "PAGEMARGIN", + "PARASHAPE", + "SHAPEOBJECT", + "TEXTART", + "TEXTARTSHAPE", + "WINDOWBRUSH" + ] +} \ No newline at end of file diff --git a/250908_DIW_2508D_TEST.xlsx b/250908_DIW_2508D_TEST.xlsx new file mode 100644 index 0000000..2d5d1a3 Binary files /dev/null and b/250908_DIW_2508D_TEST.xlsx differ diff --git a/250908_DIW_2508D_채점결과.xlsx b/250908_DIW_2508D_채점결과.xlsx new file mode 100644 index 0000000..46c5ba5 Binary files /dev/null and b/250908_DIW_2508D_채점결과.xlsx differ diff --git a/250908_DIW_2508D_채점결과_header.xlsx b/250908_DIW_2508D_채점결과_header.xlsx new file mode 100644 index 0000000..5f1eeb7 Binary files /dev/null and b/250908_DIW_2508D_채점결과_header.xlsx differ diff --git a/DIW_2508D.json b/DIW_2508D.json index 684479e..b5956ab 100644 --- a/DIW_2508D.json +++ b/DIW_2508D.json @@ -259,7 +259,7 @@ "searchValue": "DIAT", "value": "궁서", "points": 1, - "category": "FontName.Header", + "category": "Header.FontName", "item": "문구 (DIAT)/① 글꼴 (궁서)" }, "26": { @@ -267,7 +267,7 @@ "searchValue": "DIAT", "value": "900", "points": 1, - "category": "OneAnswer.Header", + "category": "Header.OneAnswer", "item": "문구 (DIAT)/② 크기 (9pt)" }, "27": { @@ -275,7 +275,7 @@ "searchValue": "DIAT", "value": "Right", "points": 1, - "category": "OneAnswer.Header", + "category": "Header.OneAnswer", "item": "문구 (DIAT)/③ 정렬 (오른쪽 정렬)" }, "28": { diff --git a/HWP.code-workspace b/HWP.code-workspace index 19ba4c0..2670b54 100644 --- a/HWP.code-workspace +++ b/HWP.code-workspace @@ -5,6 +5,10 @@ } ], "settings": { - "terminal.integrated.cwd": "${workspaceFolder}" + "terminal.integrated.cwd": "${workspaceFolder}", + "cSpell.words": [ + "hwpx", + "PAGEMARGIN" + ] } } \ No newline at end of file diff --git a/diwScoring2.py b/diwScoring2.py index 009e571..8850ef3 100644 --- a/diwScoring2.py +++ b/diwScoring2.py @@ -133,10 +133,36 @@ class XMLScorer: # 하나의 XML 파일 채점 def _score_xml_file(self, xml_file, chart_xml): - + # def parse_pages_by_bookmark(root): + # """ + # P/TEXT/BOOKMARK 구조를 가진 XML에서 페이지 구간별
요소를 파싱하여 반환 + # """ + # pages = {} + # all_p_tags = root.xpath('//P') + + # current_page = None + # page_start_index = None + + # for i, p in enumerate(all_p_tags): + # # BOOKMARK가 존재하는지 확인 (어디에 있든 탐색) + # bookmarks = p.xpath('.//BOOKMARK') + # for bm in bookmarks: + # name = bm.get('Name') + # if name and name.endswith('_start'): + # current_page = name.replace('_start', '') + # page_start_index = i + # elif name and name.endswith('_end') and current_page is not None: + # page_end_index = i + # page_content = all_p_tags[page_start_index:page_end_index + 1] + # pages[current_page] = page_content + # current_page = None + # page_start_index = None + + # return pages def parse_pages_by_bookmark(root): """ - P/TEXT/BOOKMARK 구조를 가진 XML에서 페이지 구간별
요소를 파싱하여 반환 + BOOKMARK(Name="Page_X_start" ~ "Page_X_end") 사이의
요소들을 + 페이지 단위로 딕셔너리에 저장 """ pages = {} all_p_tags = root.xpath('//P') @@ -145,15 +171,16 @@ class XMLScorer: page_start_index = None for i, p in enumerate(all_p_tags): - # BOOKMARK가 존재하는지 확인 - bookmark = p.xpath('./TEXT/BOOKMARK') - if bookmark: - name = bookmark[0].get('Name') + # P 안의 모든 BOOKMARK 탐색 + bookmarks = p.xpath('.//BOOKMARK') + for bm in bookmarks: + name = bm.get('Name') if name and name.endswith('_start'): current_page = name.replace('_start', '') page_start_index = i elif name and name.endswith('_end') and current_page is not None: page_end_index = i + # 시작~끝까지 P 태그 묶음 저장 page_content = all_p_tags[page_start_index:page_end_index + 1] pages[current_page] = page_content current_page = None @@ -248,6 +275,25 @@ class XMLScorer: } try: + if "Header" in (category or ""): + def has_elements(ptags, xpath): + for p in ptags: + element_list = p.xpath(xpath) if xpath else [] + if element_list: + return True + return False + + page1_ptags = pages.get('Page_1', []) + page2_ptags = pages.get('Page_2', []) + header_xpath = "//HEADER//P" + has_page1_element = has_elements(page1_ptags, header_xpath) + has_page2_element = has_elements(page2_ptags, header_xpath) + + if not has_page1_element or not has_page2_element: + user_answer = None + self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal") + continue + if (category or "") == "PageSetting": items = root.xpath(xpath) error_range = criterion.get('tolerance', 0) @@ -418,26 +464,7 @@ class XMLScorer: # 정답이 하나인 경우 # elif (category or "") in ["OneAnswer", "ChartOneAnswer"]: - elif "OneAnswer" in (category or ""): - if "Header" in category: - def has_elements(ptags, xpath): - for p in ptags: - charshape_list = p.xpath(xpath) if xpath else [] - if charshape_list: - return True - return False - - page1_ptags = pages.get('Page_1', []) - page2_ptags = pages.get('Page_2', []) - header_xpath = "//HEADER//P" - has_page1_element = has_elements(page1_ptags, header_xpath) - has_page2_element = has_elements(page2_ptags, header_xpath) - - if not has_page1_element or not has_page2_element: - user_answer = "" - self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal") - continue - + elif "OneAnswer" in (category or ""): items = root.xpath(xpath) if xpath else [] items2 = root.xpath(xpath2) if xpath2 else [] @@ -580,26 +607,6 @@ class XMLScorer: # 폰트명 elif "FontName" in (category or ""): - # 'DIAT' 머릿말 문항 1,2페이지 둘 중 하나라도 없으면 0점 처리 - if "Header" in category: - def has_charshape(ptags, xpath): - for p in ptags: - charshape_list = p.xpath(xpath) if xpath else [] - if charshape_list: - return True - return False - - page1_ptags = pages.get('Page_1', []) - page2_ptags = pages.get('Page_2', []) - header_xpath = "//HEADER//P" - has_page1_element = has_charshape(page1_ptags, header_xpath) - has_page2_element = has_charshape(page2_ptags, header_xpath) - - if not has_page1_element or not has_page2_element: - user_answer = "" - self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal") - continue - charshape_list = root.xpath(xpath) # 문자속성이 없는 경우 @@ -1313,19 +1320,19 @@ class XMLScorer: def main(): # 시험회차 및 유형 - # exam_round = '2508' - exam_round = '2522' + exam_round = '2508' + # exam_round = '2522' # 채점하고자 하는 유형은 주석 해제 exam_types = [ - 'A', - 'B', - 'C', - # 'D', + # 'A', + # 'B', + # 'C', + 'D', ] - test_mode = False - # test_mode = True #/TEST 폴더 채점시 + # test_mode = False + test_mode = True #/TEST 폴더 채점시 output_excel_paths = [] for exam_type in exam_types: diff --git a/250829_DIW_2508A_채점결과.xlsx b/회차별채점자료/2508/채점결과/250829_DIW_2508A_채점결과.xlsx similarity index 100% rename from 250829_DIW_2508A_채점결과.xlsx rename to 회차별채점자료/2508/채점결과/250829_DIW_2508A_채점결과.xlsx diff --git a/250829_DIW_2508B_채점결과.xlsx b/회차별채점자료/2508/채점결과/250829_DIW_2508B_채점결과.xlsx similarity index 100% rename from 250829_DIW_2508B_채점결과.xlsx rename to 회차별채점자료/2508/채점결과/250829_DIW_2508B_채점결과.xlsx diff --git a/250829_DIW_2508C_채점결과.xlsx b/회차별채점자료/2508/채점결과/250829_DIW_2508C_채점결과.xlsx similarity index 100% rename from 250829_DIW_2508C_채점결과.xlsx rename to 회차별채점자료/2508/채점결과/250829_DIW_2508C_채점결과.xlsx diff --git a/250829_DIW_2508D_채점결과.xlsx b/회차별채점자료/2508/채점결과/250829_DIW_2508D_채점결과.xlsx similarity index 100% rename from 250829_DIW_2508D_채점결과.xlsx rename to 회차별채점자료/2508/채점결과/250829_DIW_2508D_채점결과.xlsx diff --git a/250903_DIW_2508C_채점결과.xlsx b/회차별채점자료/2508/채점결과/250903_DIW_2508C_채점결과.xlsx similarity index 100% rename from 250903_DIW_2508C_채점결과.xlsx rename to 회차별채점자료/2508/채점결과/250903_DIW_2508C_채점결과.xlsx diff --git a/회차별채점자료/2508/채점결과/git_branch_manage.md b/회차별채점자료/2508/채점결과/git_branch_manage.md new file mode 100644 index 0000000..d433264 --- /dev/null +++ b/회차별채점자료/2508/채점결과/git_branch_manage.md @@ -0,0 +1,87 @@ +# GIT branch + +## 📌 장기간 개발용 브랜치 전략 + +### 1. 메인(main) 브랜치 + +* 항상 **안정된 코드** +* 실제 서비스/라이브에서 사용하는 코드 +* 여기서는 직접 실험하지 않음 + +### 2. 기능(feature) 브랜치 + +* 장기간 개발할 기능을 위한 브랜치 +* 예시: `feature/new-parser`, `feature/ui-redesign` + +--- + +## 📌 워크플로우 + +### 1. 기능 브랜치 생성 + +```bash +git checkout main +git pull origin main # 최신화 +git checkout -b feature/new-parser +``` + +### 2. 기능 개발 (여러 번 커밋) + +```bash +# 코드 수정 +git add . +git commit -m "초기 버전: XML 파서 구조 추가" +``` + +* 계속 개발하면서 커밋 누적 +* 필요하면 `git push origin feature/new-parser` 해서 원격에도 올려두기 (백업용) + +### 3. 장기간 개발 중 main 따라가기 (동기화) + +* main 브랜치가 바뀌면, 기능 브랜치로 가져와야 충돌 줄임 + +```bash +git checkout main +git pull origin main +git checkout feature/new-parser +git merge main # main의 최신 내용 병합 +``` + +⚡ 이 과정을 정기적으로 하면 `feature` 브랜치가 main과 멀어지지 않음 + +### 4. 기능 완료 → main에 병합 + +```bash +git checkout main +git pull origin main +git merge feature/new-parser +git push origin main +``` + +### 5. 필요 시 브랜치 삭제 + +```bash +git branch -d feature/new-parser +git push origin --delete feature/new-parser +``` + +--- + +## 📌 네이밍 규칙 (혼자 관리할 때도 편리) + +* `feature/기능명` : 장기간 개발용 (예: `feature/new-ui`) +* `fix/버그명` : 버그 수정용 (예: `fix/xml-encoding`) +* `test/실험명` : 단기 테스트용 (예: `test/xpath`) + +--- + +✅ 정리 + +* **main = 안정 코드** +* **feature 브랜치 = 장기간 개발용** (필요할 때 main과 동기화) +* 완료되면 main에 병합 → 안정화 +* 이렇게 하면 실험/개발/운영 코드가 깔끔하게 분리됨 + +--- + +👉 기능 브랜치 개발 중에, **main 브랜치 변경분을 가져올 때** `merge` 방식이 편할까요, 아니면 `rebase` 로 깔끔하게 이력을 유지하는 게 좋을까요? diff --git a/250905_DIW_2522A_채점결과.xlsx b/회차별채점자료/2522/채점결과/250905_DIW_2522A_채점결과.xlsx similarity index 100% rename from 250905_DIW_2522A_채점결과.xlsx rename to 회차별채점자료/2522/채점결과/250905_DIW_2522A_채점결과.xlsx diff --git a/250905_DIW_2522B_채점결과.xlsx b/회차별채점자료/2522/채점결과/250905_DIW_2522B_채점결과.xlsx similarity index 100% rename from 250905_DIW_2522B_채점결과.xlsx rename to 회차별채점자료/2522/채점결과/250905_DIW_2522B_채점결과.xlsx diff --git a/250905_DIW_2522C_채점결과.xlsx b/회차별채점자료/2522/채점결과/250905_DIW_2522C_채점결과.xlsx similarity index 100% rename from 250905_DIW_2522C_채점결과.xlsx rename to 회차별채점자료/2522/채점결과/250905_DIW_2522C_채점결과.xlsx diff --git a/250905_DIW_2522A_채점결과 - 복사본.xlsx b/회차별채점자료/2522/채점결과/250908_DIW_2522A_채점결과.xlsx similarity index 99% rename from 250905_DIW_2522A_채점결과 - 복사본.xlsx rename to 회차별채점자료/2522/채점결과/250908_DIW_2522A_채점결과.xlsx index b1dcc4f..979ada8 100644 Binary files a/250905_DIW_2522A_채점결과 - 복사본.xlsx and b/회차별채점자료/2522/채점결과/250908_DIW_2522A_채점결과.xlsx differ diff --git a/250905_DIW_2522B_채점결과 - 복사본.xlsx b/회차별채점자료/2522/채점결과/250908_DIW_2522B_채점결과.xlsx similarity index 99% rename from 250905_DIW_2522B_채점결과 - 복사본.xlsx rename to 회차별채점자료/2522/채점결과/250908_DIW_2522B_채점결과.xlsx index e1e5856..f2bf742 100644 Binary files a/250905_DIW_2522B_채점결과 - 복사본.xlsx and b/회차별채점자료/2522/채점결과/250908_DIW_2522B_채점결과.xlsx differ diff --git a/250905_DIW_2522C_채점결과 - 복사본.xlsx b/회차별채점자료/2522/채점결과/250908_DIW_2522C_채점결과.xlsx similarity index 99% rename from 250905_DIW_2522C_채점결과 - 복사본.xlsx rename to 회차별채점자료/2522/채점결과/250908_DIW_2522C_채점결과.xlsx index 9295cad..d92f8bb 100644 Binary files a/250905_DIW_2522C_채점결과 - 복사본.xlsx and b/회차별채점자료/2522/채점결과/250908_DIW_2522C_채점결과.xlsx differ