diff --git a/250515_DIW_2504회_A형_TEST.xlsx b/250515_DIW_2504회_A형_TEST.xlsx new file mode 100644 index 0000000..bcea5bd Binary files /dev/null and b/250515_DIW_2504회_A형_TEST.xlsx differ diff --git a/DIW_2504A.json b/DIW_2504A.json index f1c0709..729542b 100644 --- a/DIW_2504A.json +++ b/DIW_2504A.json @@ -637,7 +637,6 @@ "37": { "path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor", "path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr='2']/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor", - "searchValue": null, "value": "2862825", "points": 2, "category": "표", diff --git a/DIW_2504A_new.json b/DIW_2504A_new.json index aa365b0..17f8250 100644 --- a/DIW_2504A_new.json +++ b/DIW_2504A_new.json @@ -49,7 +49,7 @@ "searchValue": "클라우드컴퓨팅컨퍼런스", "value": "맑은 고딕", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (클라우드컴퓨팅컨퍼런스)/① 글씨체 (맑은 고딕)" }, "2": { @@ -83,7 +83,7 @@ "searchValue": "클라우드컴퓨팅컨퍼런스", "value": "true", "points": 2, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (클라우드컴퓨팅컨퍼런스)/⑤ 위치 (글자처럼 취급)" }, "6": { @@ -91,7 +91,7 @@ "searchValue": "클라우드컴퓨팅컨퍼런스", "value": "Center", "points": 2, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (클라우드컴퓨팅컨퍼런스)/⑥ 정렬 (가운데 정렬)" }, "7": { @@ -180,7 +180,7 @@ "searchValue": "참여안내", "value": "Center", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (● 참여안내 ●)/② 정렬 (가운데 정렬)" }, "17": { @@ -216,7 +216,7 @@ "searchValue": "2025. 04. 26.", "value": "1400", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (2025. 04. 26.)/① 크기 (14pt)", "desc": "1pt당 100" }, @@ -225,7 +225,7 @@ "searchValue": "2025. 04. 26.", "value": "Center", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (2025. 04. 26.)/② 정렬 (가운데 정렬)" }, "22": { @@ -241,7 +241,7 @@ "searchValue": "글로벌멀티클라우드협의회", "value": "2600", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (글로벌멀티클라우드협의회)/② 크기 (26pt)" }, "24": { @@ -249,7 +249,7 @@ "searchValue": "글로벌멀티클라우드협의회", "value": "Center", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (글로벌멀티클라우드협의회)/③ 정렬 (가운데 정렬)" }, "25": { @@ -265,7 +265,7 @@ "searchValue": "DIAT", "value": "900", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (DIAT)/② 크기 (9pt)" }, "27": { @@ -273,14 +273,14 @@ "searchValue": "DIAT", "value": "Right", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (DIAT)/③ 정렬 (오른쪽 정렬)" }, "28": { "path": "//PAGENUM/@FormatType", "value": "LatinCapital", "points": 2, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "① 쪽 번호 매기기 (A,B,C 순으로)", "desc": { "가,나,다":"HangulSyllable", @@ -299,7 +299,7 @@ "path": "//PAGENUM/@Pos", "value": "BottomRight", "points": 2, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "오른쪽 아래" }, "30": { @@ -335,7 +335,7 @@ "path": "//SECTION[2]//COLDEF/@Count", "value": "2", "points": 3, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "② 다단 2단" }, "4": { @@ -369,7 +369,7 @@ "searchValue": "클라우드 컴퓨팅", "value": "50", "points": 2, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (클라우드 컴퓨팅)/④ 글상자 모서리 (반원)" }, "8": { @@ -385,7 +385,7 @@ "searchValue": "클라우드 컴퓨팅", "value": "true", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (클라우드 컴퓨팅)/⑥ 글상자 위치 (글자처럼 취급)" }, "10": { @@ -393,7 +393,7 @@ "searchValue": "클라우드 컴퓨팅", "value": "Center", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (클라우드 컴퓨팅)/⑦ 글상자 정렬 (가운데 정렬)" }, "11": { @@ -410,7 +410,7 @@ "searchValue": "클라우드 컴퓨팅", "value": "2000", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (클라우드 컴퓨팅)/⑨ 글씨크기 (20pt)", "desc":"1pt당 100" }, @@ -419,7 +419,7 @@ "searchValue": "클라우드 컴퓨팅", "value": "Center", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (클라우드 컴퓨팅)/⑩ 정렬 (가운데 정렬)" }, "14": { @@ -471,7 +471,7 @@ "searchValue": "1. 주목하는 최신 트렌드", "value": "1200", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구① (1. 주목하는 최신 트렌드)/② 크기 (12pt)" }, "21": { @@ -495,7 +495,7 @@ "searchValue": "2. 기술의 경제적 가치", "value": "1200", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구② (2. 기술의 경제적 가치)/② 크기 (12pt)" }, "24": { @@ -528,7 +528,7 @@ "searchValue": "인터넷을 통해 액세스할 수 있는 가상화된 서버에서 실행되는 프로그램과 데이터베이스를 제공하는 환경", "value": "900", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (클라우드)/③ 크기 (9pt)" }, "28": { @@ -536,7 +536,7 @@ "searchValue": "인터넷을 통해 액세스할 수 있는 가상화된 서버에서 실행되는 프로그램과 데이터베이스를 제공하는 환경", "value": "Ideograph", "points": 2, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "문구 (클라우드)/④ 각주 번호모양", "desc": { "가,나,다":"HangulSyllable", @@ -601,7 +601,7 @@ "searchValue": "클라우드 보안(단위: 백만 달러)", "value": "1200", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "제목 문구 (클라우드 보안(단위: 백만 달러))/② 크기 (12pt)" }, "35": { @@ -617,9 +617,83 @@ "searchValue": "클라우드 보안(단위: 백만 달러)", "value": "Center", "points": 1, - "category": "SingleAnswer", + "category": "OneAnswer", "item": "제목 문구 (클라우드 보안(단위: 백만 달러))/④ 정렬 (가운데 정렬)" }, + "37": { + "path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor", + "path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor", + "value": "233,174,43", + "points": 2, + "category": "Color", + "item": "위쪽 제목 셀/① 색상(RGB:233,174,43)" + }, + "38": { + "path": "//CHARSHAPE[@Id=//TABLE/ROW[1]/descendant::TEXT/@CharShape]", + "value": "BOLD", + "points": 1, + "category": "FontAttribute", + "item": "위쪽 제목 셀/② 진하게", + "desc": "글자 속성이라 CELLZONE으로 적용 되지 않음" + }, + "39": { + "path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/BOTTOMBORDER/@Type", + "path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/BOTTOMBORDER/@Type", + "value": "DoubleSlim", + "points": 2, + "category": "OneAnswer", + "item": "제목 셀 아래선/① 이중실선" + }, + "40": { + "path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/BOTTOMBORDER/@Width", + "path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/BOTTOMBORDER/@Width", + "value": "0.5mm", + "points": 2, + "category": "OneAnswer", + "item": "제목 셀 아래선/② 0.5mm" + }, + "41": { + "path": "//TABLE//TEXT/@CharShape", + "path2": "//FONTFACE[@Lang='Hangul']/FONT[@Id=//CHARSHAPE[@Id=//TABLE/ROW/descendant::TEXT/@CharShape]/FONTID/@Hangul]/@Name", + "value": "중고딕", + "points": 1, + "category": "TableFontName", + "category_tmp": "FontName", + "item": "글자모양/① 글씨체 (중고딕)", + "desc": "테이블 폰트명 문항은 테이블의 모든 셀이 정답폰트와 일치해야 함, 하나만 일치해도 정답으로 채점할 경우 category값을 FontName으로 변경" + }, + "42": { + "path": "//CHARSHAPE[@Id=//TABLE//TEXT/@CharShape]/@Height", + "value": "1000", + "points": 1, + "category": "OneAnswer", + "item": "글자모양/② 크기 (10pt)" + }, + "43": { + "path": "//PARASHAPE[@Id=//TABLE/ROW//P/@ParaShape]/@Align", + "value": "Center", + "points": 1, + "category": "OneAnswer", + "item": "글자모양/③ 정렬 (가운데 정렬)" + }, + "44": { + "path": "boolean(//TABLE[1]/ROW[last()]/CELL[position()=last() or position()=last()-1]//FIELDBEGIN[starts-with(@Command, '={option}')])", + "option": "SUM", + "value": true, + "points": 4, + "category": "Boolean", + "item": "블록 계산식/합계", + "desc": "option값에 합계는 SUM / 평균은 AVG" + }, + "45": { + "path": "", + "chart_type": "묶은세로막대형", + "value": true, + "points": 2, + "category": "chart_type", + "item": "① 종류 (묶은 세로 막대형)", + "desc": "chart_type을 입력받아 차트타입에 맞는 xml요소가 있는지 내부적으로 검사, chart_type만 한글로 입력해주면 된다." + }, "61": {} } } \ No newline at end of file diff --git a/checklist.xbook b/checklist.xbook index 8645ad9..d0a182c 100644 --- a/checklist.xbook +++ b/checklist.xbook @@ -1 +1 @@ -[{"kind":1,"language":"markdown","value":"# XPath Notebook\nDate: 2025-01-22     Time: 16:12:58"},{"kind":1,"language":"markdown","value":"* mm > pt 변환비율 = 2.83465 \r\n* 283.465"},{"kind":1,"language":"markdown","value":"- 색상 demical 코드 [1-2] [1-10] [2-8] [2-37]"},{"kind":2,"language":"xpath","value":"//TEXTART[@Text='클라우드컴퓨팅컨퍼런스']/descendant::WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//RECTANGLE[.//CHAR[text()='전']]//WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//RECTANGLE//CHAR[text()='클라우드 컴퓨팅']/ancestor::RECTANGLE/descendant::WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor"},{"kind":1,"language":"markdown","value":"- [1-10] ① ●, ② ●, ③ ※"},{"kind":2,"language":"xpath","value":"\"path\": \"count(//CHAR[contains(text(),'●')]) + count(//CHAR[contains(text(),'※')])\",\r\n \"path2\": \"string-length(//CHAR[contains(text(),'●')]) - string-length(translate(//CHAR[contains(text(),'●')], '●', '')) + string-length(//CHAR[contains(text(),'※')]) - string-length(translate(//CHAR[contains(text(),'※')], '※', ''))\","},{"kind":1,"language":"markdown","value":"- [1-28] [2-28] @FormatType 종류\r\n - HangulSyllable : 가나다\r\n - Digit : 123\r\n - DecagonCircle : 갑을병정\r\n - LatinCapital : ABC\r\n - CircledDigit : ①,②,③\r\n - Ideograph : 一,二,三\r\n - CircledHangulJamo : ㉠,㉡,㉢\r\n - CircledLatinSmall : ⓐ,ⓑ,ⓒ\r\n - RomanSmall : i,ii,iii"},{"kind":2,"language":"xpath","value":"//SECTION[1]//PAGENUM/@FormatType"},{"kind":2,"language":"xpath","value":"//P[TEXT[CHAR[contains(text(), '눈으로 읽는 대신 귀로 들을 수 있게 책의 내용(문자)을 음성으로 녹음하여 기록한 것을 의미함')]]]//AUTONUMFORMAT/@Type"},{"kind":1,"language":"markdown","value":"- [2-30] ① 저감(低減), ② 화석(化石), ③ 투자(投資), ④ 달성(達成), ⑤ 세금(稅金)"},{"kind":2,"language":"xpath","value":"(count(//CHAR[contains(text(),'저감')][contains(text(),'低減')])+count(//CHAR[contains(text(),'화석')][contains(text(),'化石')])+count(//CHAR[contains(text(),'투자')][contains(text(),'投資')])+count(//CHAR[contains(text(),'달성')][contains(text(),'達成')])+count(//CHAR[contains(text(),'세금')][contains(text(),'稅金')]))*2"},{"kind":1,"language":"markdown","value":"- [2-37] [2-39] [2-40] @EndColAddr 속성값 \r\n - 표의 열 갯수-1\r\n - 4개=3 / 3개=2 / 2개=1"},{"kind":2,"language":"xpath","value":"@EndColAddr='2'"},{"kind":1,"language":"markdown","value":"- [2-45]\r\n - 꺾은선형 //c:lineChart/c:grouping/@val='standard'\r\n - 가로막대형 boolean(//c:barChart/c:barDir[@val='bar'])\r\n - 묶은가로막대형 boolean(//c:barChart[c:barDir[@val='bar'] and c:grouping[@val='clustered']])\r\n - 세로막대형 boolean(//c:barChart/c:barDir[@val='col'])\r\n - 묶은세로막대형 boolean(//c:barChart[c:barDir[@val='col'] and c:grouping[@val='clustered']])"},{"kind":2,"language":"xpath","value":"boolean(//c:barChart[c:barDir[@val='col'] and c:grouping[@val='clustered']])"},{"kind":2,"language":"xpath","value":"//c:valAx/c:majorTickMark/@val"}] \ No newline at end of file +[{"kind":1,"language":"markdown","value":"# XPath Notebook\nDate: 2025-01-22     Time: 16:12:58"},{"kind":1,"language":"markdown","value":"* mm > pt 변환비율 = 2.83465 \r\n* 283.465"},{"kind":1,"language":"markdown","value":"- 색상 demical 코드 [1-2] [1-10] [2-8] [2-37]"},{"kind":2,"language":"xpath","value":"//TEXTART[@Text='클라우드컴퓨팅컨퍼런스']/descendant::WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//RECTANGLE[.//CHAR[text()='전']]//WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//RECTANGLE//CHAR[text()='클라우드 컴퓨팅']/ancestor::RECTANGLE/descendant::WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor"},{"kind":1,"language":"markdown","value":"- [1-10] ① ●, ② ●, ③ ※"},{"kind":2,"language":"xpath","value":"\"path\": \"count(//CHAR[contains(text(),'●')]) + count(//CHAR[contains(text(),'※')])\",\r\n \"path2\": \"string-length(//CHAR[contains(text(),'●')]) - string-length(translate(//CHAR[contains(text(),'●')], '●', '')) + string-length(//CHAR[contains(text(),'※')]) - string-length(translate(//CHAR[contains(text(),'※')], '※', ''))\","},{"kind":1,"language":"markdown","value":"- [1-28] [2-28] @FormatType 종류\r\n - HangulSyllable : 가나다\r\n - Digit : 123\r\n - DecagonCircle : 갑을병정\r\n - LatinCapital : ABC\r\n - CircledDigit : ①,②,③\r\n - Ideograph : 一,二,三\r\n - CircledHangulJamo : ㉠,㉡,㉢\r\n - CircledLatinSmall : ⓐ,ⓑ,ⓒ\r\n - RomanSmall : i,ii,iii"},{"kind":2,"language":"xpath","value":"//SECTION[1]//PAGENUM/@FormatType"},{"kind":2,"language":"xpath","value":"//P[TEXT[CHAR[contains(text(), '눈으로 읽는 대신 귀로 들을 수 있게 책의 내용(문자)을 음성으로 녹음하여 기록한 것을 의미함')]]]//AUTONUMFORMAT/@Type"},{"kind":1,"language":"markdown","value":"- [2-30] ① 저감(低減), ② 화석(化石), ③ 투자(投資), ④ 달성(達成), ⑤ 세금(稅金)"},{"kind":2,"language":"xpath","value":"(count(//CHAR[contains(text(),'저감')][contains(text(),'低減')])+count(//CHAR[contains(text(),'화석')][contains(text(),'化石')])+count(//CHAR[contains(text(),'투자')][contains(text(),'投資')])+count(//CHAR[contains(text(),'달성')][contains(text(),'達成')])+count(//CHAR[contains(text(),'세금')][contains(text(),'稅金')]))*2"},{"kind":1,"language":"markdown","value":"- [2-37] [2-39] [2-40] @EndColAddr 속성값 \r\n - 표의 열 갯수-1\r\n - 4개=3 / 3개=2 / 2개=1"},{"kind":2,"language":"xpath","value":"@EndColAddr='2'"},{"kind":1,"language":"markdown","value":"- [2-45]\r\n - 꺾은선형 //c:lineChart\r\n - 묶은가로막대형 //c:barChart[c:barDir[@val='bar']]\r\n - 묶은세로막대형 //c:barChart[c:barDir[@val='col']]\r\n - 원형 //c:pieChart\r\n - 분산형 //c:scatterChart"},{"kind":1,"language":"markdown","value":"//c:{chart_type}Chart/"},{"kind":2,"language":"xpath","value":"boolean(//c:barChart[c:barDir[@val='col'] and c:grouping[@val='clustered']])"},{"kind":2,"language":"xpath","value":"//c:valAx/c:majorTickMark/@val"}] \ No newline at end of file diff --git a/diwScoring2.py b/diwScoring2.py index ec13310..8e28396 100644 --- a/diwScoring2.py +++ b/diwScoring2.py @@ -11,6 +11,8 @@ from difflib import SequenceMatcher import pandas as pd import base64 import math +from itertools import chain + # from xpathSearch import XMLPathHandler class XMLScorer: @@ -202,6 +204,7 @@ class XMLScorer: points = criterion.get('points', 0) category = criterion.get('category', None) item = criterion.get('item', None) + option = criterion.get('option', None) similar_text = None # search_value가 있는 경우 @@ -211,7 +214,11 @@ class XMLScorer: xpath = xpath.replace('{searchValue}', similar_text) if xpath2 is not None: xpath2 = xpath2.replace('{searchValue}', similar_text) - + + if option: + xpath = xpath.replace('{option}', option) if xpath else "" + xpath2 = xpath2.replace('{option}', option) if xpath2 else "" + # 문항 별 채점 결과 저장 scoring = { 'section': section_id, @@ -224,7 +231,7 @@ class XMLScorer: 'deductions': [] # 각 기준별 감점 내역 } - if "PageSetting" in (category or ""): + if (category or "") == "PageSetting": items = root.xpath(xpath) error_range = criterion.get('tolerance', 0) @@ -244,7 +251,7 @@ class XMLScorer: if scoring['points'] > 0: break - elif "BasicSetting" in (category or ""): + elif (category or "") == "BasicSetting": # 바탕글(기본설정) 요소 normal_style = root.xpath("//STYLE[@Name='바탕글']") @@ -277,17 +284,18 @@ class XMLScorer: self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal") # 오타 감점 부분은 미리 계산 하고, 이후 점수만 계산 - elif "오타감점" in (category or ""): + elif (category or "") == "오타감점": points = self.get_typo_score() self.total_score += points self.partial_score += points scoring['points'] = points # 정답이 하나인 경우 - elif "SingleAnswer" in (category or ""): + elif (category or "") == "OneAnswer": items = root.xpath(xpath) + items2 = root.xpath(xpath2) if xpath2 else [] - for item in items: + for item in chain(items, items2): user_answer = item self.evaluate_answer(scoring, user_answer, right_answer, points) @@ -295,24 +303,21 @@ class XMLScorer: if scoring['points'] > 0: break - elif "DoubleAnswer" in (category or ""): + elif (category or "") == "DoubleAnswer": items1 = root.xpath(xpath) - items2 = root.xpath(xpath2) + items2 = root.xpath(xpath2) if xpath else [] user_answer = [] for item1, item2 in zip(items1, items2): user_answer.append(item1) user_answer.append(item2) - # user_answer[0] = item1 - # user_answer[1] = item2 - + self.evaluate_answer(scoring, user_answer, right_answer, points) if scoring['points'] > 0: break - # 사용자 입력값이 mm단위인 경우 - elif "mmSize" in (category or ""): + elif (category or "") == "mmSize": items = root.xpath(xpath) error_range = criterion.get('tolerance', 0) @@ -325,7 +330,7 @@ class XMLScorer: if scoring['points'] > 0: break - elif "ParaShape" in (category or ""): + elif (category or "") == "ParaShape": items = root.xpath(xpath) for item in items: @@ -341,7 +346,7 @@ class XMLScorer: break # Boolean 타입 정답인 경우 - elif "Boolean" in (category or ""): + elif (category or "") == "Boolean": items = root.xpath(xpath) items2 = root.xpath(xpath2) if xpath2 else False @@ -350,23 +355,23 @@ class XMLScorer: self.evaluate_answer(scoring, user_answer, right_answer, points) # 채점기준표 파일에 작성된 rgb값을 그대로 읽어와 HML파일 요소의 int형 rgb값과 비교 - elif "Color" in (category or ""): - items = root.xpath(xpath) + elif (category or "") == "Color": + items = root.xpath(xpath) if xpath else [] + items2 = root.xpath(xpath2) if xpath2 else [] rgb_text = right_answer r, g, b = map(int, rgb_text.split(',')) rgb_int = (b << 16) + (g << 8) + r - for item in items: + # items, items2를 순차적으로 순회 + for item in chain(items, items2): user_answer = int(item) - self.evaluate_answer(scoring, user_answer, rgb_int, points, method="equal") - if scoring['points'] > 0: break # 문단 첫글자 장식 채점 - elif "TwoLineSize" in (category or ""): + elif (category or "") == "TwoLineSize": items = root.xpath(xpath) error_range = criterion.get('tolerance', 0) for item in items: @@ -380,26 +385,60 @@ class XMLScorer: break # 폰트명 - elif "FontName" in (category or ""): - charshape_id = root.xpath(xpath) + elif (category or "") == "FontName": + charshape_list = root.xpath(xpath) if not charshape_id: - charshape_id = None - user_answer = None + user_answer = "" else: - font_id = root.xpath(f"//CHARSHAPE[@Id='{charshape_id[0]}']/FONTID/@Hangul") - font_name = root.xpath(f"//FONTFACE[@Lang='Hangul']/FONT[@Id='{font_id[0]}']/@Name") - user_answer = font_name[0] + for charshape_id in charshape_list: + font_id = root.xpath(f"//CHARSHAPE[@Id='{charshape_id}']/FONTID/@Hangul") + font_name = root.xpath(f"//FONTFACE[@Lang='Hangul']/FONT[@Id='{font_id[0]}']/@Name") + user_answer = font_name[0] + + # 폰트 "견고딕"과 "중고딕"은 + # 한글프로그램 내부적으로 "한양견고딕", "한양중고딕"으로 저장되므로 + # 수험자 답변에서 "한양"을 제거 + if right_answer in ["견고딕", "중고딕"]: + user_answer = user_answer.replace("한양", "") + + self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal") + + if scoring['points'] > 0: + break - # 폰트 "견고딕"과 "중고딕"은 - # 한글프로그램 내부적으로 "한양견고딕", "한양중고딕"으로 저장되므로 - # 수험자 답변에서 "한양"을 제거 - if right_answer in ["견고딕", "중고딕"]: - user_answer = user_answer.replace("한양", "") + # 테이블 폰트명 + # 테이블 내부 모든 셀의 폰트가 정답과 일치해야 함 + elif (category or "") == "TableFontName": + charshape_list = root.xpath(xpath) + + # 문자속성이 없는 경우 + if not charshape_list: + user_answer = "" + self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal") + else: + all_match = True # 모든 항목이 정답과 일치해야 함 + + for charshape_id in charshape_list: + font_id = root.xpath(f"//CHARSHAPE[@Id='{charshape_id}']/FONTID/@Hangul") + font_name = root.xpath(f"//FONTFACE[@Lang='Hangul']/FONT[@Id='{font_id[0]}']/@Name") - self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal") + user_answer = font_name[0] + + # 내부 저장된 접두어 제거 + if right_answer in ["견고딕", "중고딕"]: + user_answer = user_answer.replace("한양", "") + + if user_answer != right_answer: + all_match = False + break # 하나라도 다르면 바로 오답 처리 + + if all_match: + self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal") + else: + self.evaluate_answer(scoring, user_answer, right_answer, 0, method="equal") # 오답 처리 # 폰트 속성 - elif "FontAttribute" in (category or ""): + elif (category or "") == "FontAttribute": charshape = root.xpath(xpath) if not charshape: charshape = None @@ -491,6 +530,7 @@ class XMLScorer: self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal") + # 한자 elif "Hanja" in (category or ""): word_list = criterion.get('word', []) @@ -516,9 +556,27 @@ class XMLScorer: user_answer = min(score, max_score) self.evaluate_answer(scoring, user_answer, right_answer, points, method="partial_score") - + + elif (category or "") == "chart_type": + chart_type_list = { + '꺾은선형': "//c:lineChart", + '가로막대형': "//c:barChart[c:barDir[@val='bar']]", + '세로막대형': "//c:barChart[c:barDir[@val='col']]", + '원형': "//c:pieChart", + '분산형': "//c:scatterChart" + } + chart_type = criterion.get('chart_type').replace(" ","") + if "묶은" in chart_type: + chart_type = chart_type.replace("묶은", "") + chart_xpath = chart_type_list[chart_type] + + user_answer = bool(chart_tree.xpath(chart_xpath, namespaces=namespaces)) + self.evaluate_answer(scoring, user_answer, right_answer, points) + + # 문항 채점 결과를 리스트에 입력 onePersonResult['score_results'].append(scoring) print(f'scoring: {scoring}') + onePersonResult['partial_scores'].append({ 'section': section_id, 'score': self.partial_score diff --git a/zzz.xbook b/zzz.xbook index ce25fa0..791db43 100644 --- a/zzz.xbook +++ b/zzz.xbook @@ -1 +1 @@ -[{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]/*[contains(local-name(), 'BORDER')]/@Type"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]"},{"kind":2,"language":"xpath","value":"//RECTANGLE//CHAR[text()='클라우드 컴퓨팅']/ancestor::RECTANGLE/descendant::LINESHAPE"},{"kind":2,"language":"xpath","value":"//RECTANGLE[.//CHAR[text()='클라우드 컴퓨팅']]//LINESHAPE"},{"kind":2,"language":"xpath","value":"//RECTANGLE[.//CHAR[text()='클라우드 컴퓨팅']]/SHAPEOBJECT/POSITION/@TreatAsChar"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//RECTANGLE//CHAR[text()='{searchValue}']/ancestor::P[last()]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//RECTANGLE//CHAR[text()='클라우드 컴퓨팅']/parent::TEXT/@CharShape]/@Height"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//RECTANGLE//P[.//CHAR[text()='{searchValue}']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]/preceding-sibling::SHAPEOBJECT/SIZE/@Width"},{"kind":2,"language":"xpath","value":"//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[text()='{searchValue}']]/@CharShape"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//P[.//TEXTART[@Text='클라우드컴퓨팅컨퍼런스']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//TEXT[CHAR[contains(text(),'클라우드 보안(단위: 백만 달러)')]]/@CharShape]/@Height"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE[CHAR[contains(text(),'인터넷을 통해 액세스할 수 있는 가상화된 서버에서 실행되는 프로그램과 데이터베이스를 제공하는 환경')]]"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE[.//CHAR[contains(text(),'인터넷을 통해 액세스할 수 있는 가상화된 서버에서 실행되는 프로그램과 데이터베이스를 제공하는 환경')]]"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//TEXT[CHAR[text()='클라우드 보안(단위: 백만 달러)']]/@CharShape]"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//P[.//CHAR[text()='클라우드 보안(단위: 백만 달러)']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//CHAR[contains(text(),'클라우드 보안(단위: 백만 달러)')]/ancestor::P/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//P[.//CHAR[text()='클라우드 보안(단위: 백만 달러)']]"}] \ No newline at end of file +[{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]/*[contains(local-name(), 'BORDER')]/@Type"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]"},{"kind":2,"language":"xpath","value":"//RECTANGLE//CHAR[text()='클라우드 컴퓨팅']/ancestor::RECTANGLE/descendant::LINESHAPE"},{"kind":2,"language":"xpath","value":"//RECTANGLE[.//CHAR[text()='클라우드 컴퓨팅']]//LINESHAPE"},{"kind":2,"language":"xpath","value":"//RECTANGLE[.//CHAR[text()='클라우드 컴퓨팅']]/SHAPEOBJECT/POSITION/@TreatAsChar"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//RECTANGLE//CHAR[text()='{searchValue}']/ancestor::P[last()]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//RECTANGLE//CHAR[text()='클라우드 컴퓨팅']/parent::TEXT/@CharShape]/@Height"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//RECTANGLE//P[.//CHAR[text()='{searchValue}']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]/preceding-sibling::SHAPEOBJECT/SIZE/@Width"},{"kind":2,"language":"xpath","value":"//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[text()='{searchValue}']]/@CharShape"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//P[.//TEXTART[@Text='클라우드컴퓨팅컨퍼런스']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//TEXT[CHAR[contains(text(),'클라우드 보안(단위: 백만 달러)')]]/@CharShape]/@Height"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE[CHAR[contains(text(),'인터넷을 통해 액세스할 수 있는 가상화된 서버에서 실행되는 프로그램과 데이터베이스를 제공하는 환경')]]"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE[.//CHAR[contains(text(),'인터넷을 통해 액세스할 수 있는 가상화된 서버에서 실행되는 프로그램과 데이터베이스를 제공하는 환경')]]"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//TEXT[CHAR[text()='클라우드 보안(단위: 백만 달러)']]/@CharShape]"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//P[.//CHAR[text()='클라우드 보안(단위: 백만 달러)']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//CHAR[contains(text(),'클라우드 보안(단위: 백만 달러)')]/ancestor::P/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//CELLZONE[@EndColAddr=../../@ColCount]]"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//TABLE//TEXT/@CharShape]/@Height"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//TABLE/ROW//P/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//TABLE[1]/ROW[last()]/CELL[last()-1]//FIELDBEGIN[starts-with(@Command, '=SUM')]"},{"kind":2,"language":"xpath","value":"//TABLE[1]/ROW[last()]/CELL[position() = last() or position() = last() - 1]//FIELDBEGIN[starts-with(@Command, '=SUM')]\r\n"}] \ No newline at end of file diff --git a/회차별채점자료/2504/excel_채점기준표/DIW_2504A.xlsx b/회차별채점자료/2504/excel_채점기준표/DIW_2504A.xlsx index 181ffb7..d297037 100644 Binary files a/회차별채점자료/2504/excel_채점기준표/DIW_2504A.xlsx and b/회차별채점자료/2504/excel_채점기준표/DIW_2504A.xlsx differ diff --git a/회차별채점자료/2504/요청사항/0508.txt b/회차별채점자료/2504/요청사항/0508.txt index 5284179..99265b1 100644 --- a/회차별채점자료/2504/요청사항/0508.txt +++ b/회차별채점자료/2504/요청사항/0508.txt @@ -18,7 +18,7 @@ 글맵시 있는데 오답처리 -000076 박다은 >> 정상채점 + 000285 문예슬 >> 글맵시 텍스트에 개행이 들어가 있음 "서울국제도서박람회␍␊" 001345 조수빈 >> 서울국제도시박람회 000203 이지운 >> 서울국제도섭가람회 @@ -29,11 +29,11 @@ 문구 (◆ 행사안내 ◆) - 궁서/가운데정렬 맞는데 오답처리(띄어쓰기 때문인듯) >> "◆ 행사안내 ◆" 기준으로 검색할 때는 "◆ 행사_안내 ◆" (공백 하나)는 유사도가 높아 찾을 수 있지만 ->> 특수문자를 제외하고 "행사안내" 로만 검색 할 때는 "행사_안내"는 유사도가 낮아 해당 구문을 찾을 수 없음 +>> 특수문자를 제외하고 "행사안내" 로만 검색 할 때는 "행사 안내"는 유사도가 낮아 해당 구문을 찾을 수 없음 001473 노이솜 문제1 줄간격 180% 맞는데 오답처리(마지막 엔터값 때문인듯) >> 1페이지의 문단 속성들의 줄간격이 180% 가 아닌 문단이 있으면 오답으로 처리중 ->> 마지막 엔터값 줄간격 160%를 예외 처리할 기준이 모호함 +>> 마지막 엔터값 줄간격 160%를 예외 처리할 기준이 아직 모호함