From 9f06857beac45aa8fe8966b0e4141a78a04a12b6 Mon Sep 17 00:00:00 2001 From: Taehoon Date: Wed, 11 Mar 2026 14:03:26 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20?= =?UTF-8?q?=ED=99=9C=EC=84=B1=EB=8F=84=20=EB=B6=84=EC=84=9D=20=EC=8B=9C?= =?UTF-8?q?=EC=8A=A4=ED=85=9C=20=EB=B0=8F=20=ED=81=AC=EB=A1=A4=EB=A7=81=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D/=EC=A4=91=EB=8B=A8=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20DB=20=EC=97=B0=EA=B2=B0=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94,=20=ED=99=9C=EC=84=B1=EB=8F=84=20=EC=9C=84?= =?UTF-8?q?=EC=A0=AF=20=EB=B0=8F=20=EB=82=B4=EB=B9=84=EA=B2=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98,=20=EA=B4=80=EB=A6=AC=EC=9E=90=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=20=EB=AA=A8=EB=8B=AC,=20=EC=A4=91=EB=8B=A8=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5,=20UI=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20?= =?UTF-8?q?=EC=B5=9C=EC=A0=81=ED=99=94,=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=20=EB=B0=8F=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 6 +- README.md | 16 +- __pycache__/crawler_service.cpython-312.pyc | Bin 18372 -> 17206 bytes __pycache__/server.cpython-312.pyc | Bin 8943 -> 12543 bytes composition_debug.png | Bin 17859 -> 0 bytes crawler_service.py | 154 +++---- crawling_result 2026.03.06.csv | 42 -- crawling_result 2026.03.09.csv | 42 -- crwaling_result.csv | 42 -- db_2026.03.09.csv | 42 -- db_2026.03.10.csv | 42 -- debug_modal.html | 226 ---------- diag_folders.py | 66 --- js/common.js | 24 +- js/dashboard.js | 437 ++++++++------------ log_debug.png | Bin 129162 -> 0 bytes migrate_db_history.py | 2 +- migrate_normalized.py | 2 +- server.py | 257 ++++++------ server_reboot.log | Bin 93476 -> 0 bytes server_revert.log | Bin 47692 -> 0 bytes server_startup.log | Bin 968 -> 0 bytes sheet.csv | 42 -- style/dashboard.css | 375 +++++------------ templates/dashboard.html | 60 ++- test_main_filtered.py | 33 -- 26 files changed, 587 insertions(+), 1323 deletions(-) delete mode 100644 composition_debug.png delete mode 100644 crawling_result 2026.03.06.csv delete mode 100644 crawling_result 2026.03.09.csv delete mode 100644 crwaling_result.csv delete mode 100644 db_2026.03.09.csv delete mode 100644 db_2026.03.10.csv delete mode 100644 debug_modal.html delete mode 100644 diag_folders.py delete mode 100644 log_debug.png delete mode 100644 server_reboot.log delete mode 100644 server_revert.log delete mode 100644 server_startup.log delete mode 100644 sheet.csv delete mode 100644 test_main_filtered.py diff --git a/.env b/.env index 3164fdb..d1fdeed 100644 --- a/.env +++ b/.env @@ -1,2 +1,6 @@ PM_USER_ID=b21364 -PM_PASSWORD=b21364!.`nDB_HOST=localhost`nDB_USER=root`nDB_PASSWORD=45278434`nDB_NAME=crawling +PM_PASSWORD=b21364!. +DB_HOST=localhost +DB_USER=root +DB_PASSWORD=45278434 +DB_NAME=PM_proto diff --git a/README.md b/README.md index 39180ec..afa860c 100644 --- a/README.md +++ b/README.md @@ -37,12 +37,18 @@ AI는 파일을 분류할 때 단순한 키워드 매칭이 아닌, 아래의 ** --- -# 프로젝트 관리 규칙 +# 🛠️ 개발 및 관리 규칙 (Strict Development Rules) -1. **언어 설정**: 영어로 생각하되, 모든 답변은 한국어로 작성한다. (일본어, 중국어는 절대 사용하지 않는다.) -2. **수정 권한 제한**: 사용자가 명시적으로 지시한 사항 외에는 **절대 절대 절대** 코드를 임의로 수정하지 않는다. -3. **로그 기록 철저**: 모달 오픈 여부, 수집 성공/실패 여부 등 진행 상황을 실시간 로그에 상세히 표시한다. -4. **선보고 후승인**: 모든 기능 수정 및 코드 변경 전에는 예상 방안을 먼저 보고하고, 사용자가 **'진행시켜'**라고 명령한 경우에만 작업을 수행한다. +1. **언어 설정**: 영어로 생각하되, 모든 답변은 **한국어**로 작성한다. +2. **임의 수정 절대 금지 (Zero-Arbitrary Change)**: + - 사용자가 명시적으로 지시한 부분 외에는 **단 한 줄의 코드도, 그 어떤 파일도 임의로 수정, 정리, 리팩토링하지 않는다.** + - 지시받지 않은 다른 파트의 코드는 절대 건드리지 않으며, 영향 범위가 요청 범위를 벗어나지 않도록 '외과 수술식(Surgical) 수정'을 원칙으로 한다. +3. **개선 작업 절차 (Test-First Approach)**: + - 사용자가 개선(Refactoring, Optimization 등)을 지시한 경우, **수정 전 현재 시스템이 정상적으로 잘 작동하는지 먼저 전수 확인**한다. + - 기존 동작 방식과 성능을 기준(Baseline)으로 삼고, 수정 후에도 **기존의 모든 기능이 무결하게 유지되는지 반드시 테스트하여 입증**한다. + - 검증 결과를 바탕으로 "무엇을, 왜, 어떻게" 바꿀지 상세 보고 후, 사용자로부터 **'진행시켜'** 승인을 얻은 뒤에만 집행한다. +4. **선보고 후승인**: 모든 기능 수정 및 코드 변경 전에는 예상 방안을 먼저 보고하고 승인 절차를 거친다. +5. **로그 기록 철저**: 진행 상황(로그인, 수집, 오류 등)을 실시간 로그에 상세히 표시하여 투명성을 확보한다. --- diff --git a/__pycache__/crawler_service.cpython-312.pyc b/__pycache__/crawler_service.cpython-312.pyc index 73a9a5c507c508ce8291c734fa7193c02d3b66f3..65ae16e74d5db8b77b9530eedc799428ef204f84 100644 GIT binary patch delta 6616 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 delta 7504 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 diff --git a/__pycache__/server.cpython-312.pyc b/__pycache__/server.cpython-312.pyc index 34b8a2d999cc9ef0e725d038c310c8547fed212c..2f465daf4dbd5211f1a5ca10c3fe324d489d8147 100644 GIT binary patch literal 12543 zcmcIKYj9KNmG`B$WLtj8;0Lme4FYV(24aWj_{A&u5$q(OI8}u1#g@^-xmN~kq?8bE zLLMz<+r&-_8`GhUX_CfCvs=^YPJz&c?at0f$Xz=($uynK&TeZnV?uV>ko~deT-_@P zVVCU8_G90}HYa=jM2#hYi(gS3GodTW%% zCt}vP2@(_mx$6>G1!7HX+FMjCWzXmG7vW)&dFa^mw=>=%W%>f(n#G#m&WvLUxq{x! z3hzYBW&x#{%}!7PobwhPqacYzO65wWEJ>v--=I{URBB05sii5UwkBGpK%9}y?4!nagP>nLJ7!4?e>>@rTHwApTDtwV&pm@S5|1lqq{;gpEk5@lT2Qciz>VwVpw zZ^_yA<%^UlODR*HRHkf^GAmNbtV}9XzDOBcyv(X0BHpSMi%_qQQ*U9{a4VrN6+^0c zDcd5YD(@*(l~igKlzKcdS_;HI5HD5DS)rzyq~5Gqq^7m;GV79hQ?Us3`Z)E2Nz|2# zP}jz(>yoIe7NLG9PQ5`Ir;SOie_)X^o8o0QC)H3rK-#wc5r?JKMv7Y28Q=nLF9)!$ z!6|SpewOptC{eS`DFo`5CDR-#F z7vOvWV4lVI`y4LbdDz484uRtjxm}!?At4+>z~ATK4gukysy}d`qE5_c-`BaLt#xNx zb8BN;)6UlIVtQwDM@LhAXT76+PxH2&dj~f*Zs>Xx7zt9?Ro~aw4GK-Fh;H&LkT}WFsQB)0t1k*k|LmId`>SnNLTd=BGoGlnyLgRj7V$@1W_GJiN?FI zC0R<*dH12D zS>_v3s#x_mq|~rlg_6|;;;rh{#=$tyD_JM;qy)UJe@pID2Vpbxqb_S1=y+teb@n?G zI1xh=v+s`qF!$0q>)go1pWX-Tkac!q^v3I>)|C+a>HWW-{rR!E6QkBYO-=ybCTf}* z96Q=NIz^oacUX^K2uQd`JDT^14Da^`L<5kt*LQS0-nOSn)UI1!Q(L!g?K+97wZ5fU z)V8-c`gni94@)g@pw7F0olPw=5+?@N9$yUYl!TabKL{^Tp#eKsAW2>X4%0#L>YO;M|&Kg;AYRTy4 z@l{dtdL{MHSZ~x^J(IEIlkEJFwo`4>*_Dy($}deCqk5jusMJy`T=A6&m4W3*ya4}O zJ_sHr$osBbMW==+iU?A@SU%-r$so~91<4x+Q6u#8DizT|)Do{S z@6d-y8`WP4MQ2aG4%2MC@#;9t`0S5=Wu3h+G&eCm`|>!fiSu(WpP4)TtJ$-st#dD* zyYbqKtu|WJIE4Y9%k3A_a3m$s?eO^hePTL_dAHBO9pPO4AS{8FIneKOiHwu)7DNV# z1l*%mtCf$6kVYM-0VLL+P=m8FSfi_O!Vy zVlEqF$D6|Dx`}ny&2`g;x-e50Hq_mb1jg|->&c&yYngXd>&ZK0m8juky6>*RMR1F&Rk$C^QN_5i2}GRFG;SPSP%_n{ZK277#>;4$>^O1V{z)6`cb2 zqJmBNSX!y&6!l6G1LZ@&hh^}m>=6>MshAGHAwEPOCQ6BFsOK=G_ks8$2_Jog-bWlJ zbwp~Z0Vb7pfQiwT5`nlPTu$(o5W}hhCZ(}KMroy+bW!_&Z-{w@>5${YBE9ZLnxA*fNKWCup)@FaP!93tV3F}{8K=KTV42P2&64MuhyuFKQR?WP1bpW(Q)J$ z$m5)m{8RbgE1V~26ClcGGIB@KPNjvH^-g6RoUs&+eEZb5qn4G!%*?LVf3dI^*M|2z z8h&hlbdPJ49^D_Q3k%3Y<ZBUw5`2I2}%5d|P{ zKo~3}PLe3-ASsjw>4iiJYk_Y0wl-giO83stk+W>WjG(mL}@g%69kT$4gwb&v)Pl0;lA|$~@ zDb1@CDjx%oPU?#;sOe2l&?-<+2NZgwfY>0%MmaXgahe>bW8AGtjN~ir48f{{nr}Kj zsX{VIpr(xF0G&!ZAPZj*s2NJ@C4s7EGXhx(Ca70hCks?Mq<=;aGb6<*0@d9A0F=A& z(uKLPS7lK;cV^UjX5Lc?w+28D=5hBLYfq(oEz%$30HQhI_Q~vAe!zm3SdiV_ z3ME#v)w>|6G=@rdA>XpiTUQwSINl_i^ji^tIl&DXPlc^G2$PC1!dEtO%FFuE>2RA zl8)o|LXNB%{XUN0he^1o;oW_r+S%8~`B)y0c)X+?4ZIWc4FNcv;E@oP65oZnlDedM z4zwklH2EhH*Nvnk&*xym0fB&`C20}G4m26aN$D!eaQ+C8Aw%H=EInC?=BtW7dSKmT z_QkAU<`1(Yo>QK&wd3?u?wV`MPZ$*SYSC5RrCoDPm1E@}SxRQImyWcaY7LiuYbx6@ zv()-V)$3K!rK^V)QWyUuT&te@<;@bESBwN3xHENp)w+|?7Y zyTb=PVegU1!GZA6;50!PP>?FyN&P25O%=p>b5k+>%gQDLYI^`d+XEJ~ zJrG>lSic!EKCHuxD52fTUi2_ctpv2H@fD1Lgbz#L17hwsNPA#PL6^_!Wfkx#8TtU$wAA)PZkb2><4yuF9UFHkbi-VZ*DI7x@R{3d9Gu;HMJ57NRqXBc~ z9WZBfL7i-J=wc>E$Pm)A#x5`cL&lIPsONivde$VFjIJPv!k~fG9BhKw>k8y6RrbPZ zF!f{8K>f>xWGF3|#y163N#nNA{G|2b9YD&*#!VbHUEwzfr?_5g3Lzvz>A`e1()pFm9Rw531!vMth}W_f%t63j!+?Ofc?hl?}1{P{uPEV2DX^iXm3mk8AeE>!;_2-kUuMH@<&2G&ghx zogTakGI$Py0~oLn48lpYqRd-S#ya6p{tay=H;a~$qW)4XsKu?JdkhEEtN#b1XLs|q z&Y1Nm*PS?Z-_P=~*0(m@YvbJ0ARbpdI8D@&ScdCqaGUwSS%%hy6TCs1e&0c#|F94J zZ9>1->*NPS4a)`K5=h`ZIH+D^K=C1n-2V=exgQPe_?0}|#6LfG=9R%EE4$dE>q54! zN(5?>0RB;+|07=H@Pi29M&plS5X9ga#G$C>zl}i;25ty!v}Ce?9}1i^{Q};w4+sut zx4$)JMrEg1PyEwZ5;ccNLx*A}65cStwSt%9`!E9xG4Fn`TtyoEg(4$m?86L-=b$3a z$-8<)RX6YN@53YPfWQmLG+}v}CAJA;i|@zM=}Cr|sBdY6e-XtJV-mOmO86Mk@XaYJpx=E;NCH|!d7jjz65{@_&m-s$$P zNPAbb{ae%Ru1LEp+RmM7`@Fnzy1X`0UOUPB+IYcuvEy24w5~PWZjY8fFu-#nqAG>WQi=byqiC+BDtR8ENc{Ha-?DetbA>o?;|pPoFUM9O2~;PG!{I zW$o3hy+~fG2SY8gqATn^7|Hi2)?abS*rv0~CWm(^W(^kVS z($R27=ZDXPA9qIA?~g3sKV8}rDeW0;nyFqtX?}mn*}c~~!;e1xTl*zl_(^tj+f3;S z$jdGm-51GTF|LkgS4~t$ve!YZAY(q3Npf=41=IDqy|Ts0_J)rHBUPc_Wj`~YgXUx| zk!QWV5hiG!fZ#R_Q+Jbq;5Mb;z>-vQZexy={q>hy?qf;f7CcVw$bp-r?KN%OrfcYI~LD>S`J>l@g{(f@^d; zGU=-ujXO;AH4O>yHLVKxTr;T<&ZGezG*mgkk6R@j(%^QFd-^Ve5zIvSz6gMr!3gFO zz^Py2HfUj=l41r*#$q|}Sio<3A0Ba2QsTkDO%LNHL0N%G#A;4cvNJ_13C0`GzCC;P z-0YjrTIbG8&i&+!s0aTcx*Hukn?wV^abF{dTbB!5`<>hROQ2Kng6Pz|@#>fr{g^0R zU;5kGmru;Te6A9N@DG4c)OewkvzxOqycN>?MhwtSkX(^Al79{0WCw>H2RinQfj zqwC;4GOV@ESPH_nmWZJ>%(Q+b;Fun-PpgN6fT{|eY%V~=zxZ6DXDl|C3DZ9@_KJbX zl5fYo*a=6KIlvu}opu!<)#1qpJYmp#Io9oT1P1!xDF}~7E)RASk?J|sVUzHj?Bw5o z>~ruJ@*t4<|GA-Hs_=nmVNH1Jo+(2|nCbXRz!o3R%|k~OKJ%!!r%R|+QD7%~;N5MUFI1n}zgqZ@_onx$W1_Dl358nOU?aD#7gVcfFLbGFY!jd5(0O;7aP$w3; zVGR<>4ij<`v%1hOOHh(YPNRih2P7!L!2`Fel7r`YFsI4;w9lZlJ`vxi1TL49F9-*F z#Xwg2#%~YaukHozPyc`FW~o8w7fb&ToKt5gR&|;Jr_~9`X$8wjc1VFUDCP!gMU0Ho#khVVD0Lv=}up%S42RWNo5*Z@X=M40SYVHX5dBDE%IuF8T;ssIT0R%vz z+AW}IA~L;xFqbr7Xh_8={>R9RJRsmYR~#|vXon=NHvS#R#J_-xR$8l{8M2NwePS@3 ztbMWesqF4t@AXIYdKf*X6@P9H@BYM`a#VvYTv8<#isW*U1Tmg zqic6WOLtyd9w}|TCWK2m!X1x=t&c~Id%?j1%;&+)(oTLAOME5ZEiuTj#!~W9MuVZT zi1{#I3kh&-$dAJ+q`}Z)h);+XR#)f>zemKy#inS7o2C`!0D+10zW`SOG}wOu|7ci!cfv5hNHvbMfXRRfb6~F65VFZf`gLbD)`o zzkrslEQT$k#;Bnz%#_LU*O*4~6Oe7IiLF;!0E7G`K=Ab#JcI!@6>iA~SSMUbyLgn_ zqUHeHa69|lJh}>bR868qMpb&_PX{YqeqP}5SR$I0T(~)vUJwc~Re~K6vy@Cchqzra zTOFmRn5pE*5ANVp#iIa%cP6pdgwp(p>5_$$ct04^P?EIQz|$W$5AS#+?TzOuk%rse z*b5Am7jKQlG$+eC63vdiG205H(h?DA*mkjp810Bdhd=)m26$5}-Qh}S0od&D_DRzI zk_wbGiL^zf^&`zk%ADQNZi>B}+robcyimIdZ@}sWMcfV@2vPJ|X8v%=i0zauTu>3N z@JEwqdzj{j~$#c)*RCTBa(SR_pI*3%2N-Iub9fN znY0|!O%e4o1TCSGH0@K zhs$Pi^J6h4<_!dy^(CPusV_4rGIQAWB>~YFdL60$B9|r~nl}=Z>IKWQmJ?ae70gp4 zxr-dtfCPc~7EMraYC#a1DpM6i$y~&>l4BMmYn8|>F;s#qKT75xu07S(IFIlPV;O$Fja4AVQj@^N5(=66XL7D!fUR0#U~o z1Vl(wG>?e+T_jTFJ)3u;B1-1L`HO5LM^}GAV2or-VyMIrSfZr)CbYU!%Fe_Nbf)zE zCPk5jSQxqtP`oQAilB;{P(^!!HAv+6=we@xUn7Z~!C3AMf{Ae}jFJ{?K{@6EKY*mC GYX1vEb9!e0 literal 8943 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 G%YOp!hIVrR diff --git a/crawler_service.py b/crawler_service.py index f56d3a2..e2af8e7 100644 --- a/crawler_service.py +++ b/crawler_service.py @@ -11,15 +11,18 @@ from datetime import datetime from playwright.async_api import async_playwright from dotenv import load_dotenv -load_dotenv() +load_dotenv(override=True) + +# 글로벌 중단 제어용 이벤트 +crawl_stop_event = threading.Event() def get_db_connection(): - """MySQL 데이터베이스 연결을 반환합니다.""" + """MySQL 데이터베이스 연결을 반환 (환경변수 기반)""" return pymysql.connect( - host='localhost', - user='root', - password='45278434', - database='crawling', + host=os.getenv('DB_HOST', 'localhost'), + user=os.getenv('DB_USER', 'root'), + password=os.getenv('DB_PASSWORD', '45278434'), + database=os.getenv('DB_NAME', 'PM_proto'), charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor ) @@ -27,12 +30,10 @@ def get_db_connection(): def clean_date_string(date_str): if not date_str: return "" match = re.search(r'(\d{2})[./-](\d{2})[./-](\d{2})', date_str) - if match: - return f"20{match.group(1)}.{match.group(2)}.{match.group(3)}" + if match: return f"20{match.group(1)}.{match.group(2)}.{match.group(3)}" return date_str[:10].replace("-", ".") def parse_log_id(log_id): - """ID 구조: 로그고유번호_시간_활동한 사람_활동내용_활동대상""" if not log_id or "_" not in log_id: return log_id try: parts = log_id.split('_') @@ -45,6 +46,7 @@ def parse_log_id(log_id): return log_id def crawler_thread_worker(msg_queue, user_id, password): + crawl_stop_event.clear() if sys.platform == 'win32': asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) @@ -55,11 +57,18 @@ def crawler_thread_worker(msg_queue, user_id, password): async with async_playwright() as p: browser = None try: - msg_queue.put(json.dumps({'type': 'log', 'message': '브라우저 엔진 가동 (전 기능 완벽 복구 모드)...'})) - browser = await p.chromium.launch(headless=False, args=["--no-sandbox"]) - context = await browser.new_context(viewport={'width': 1600, 'height': 900}) + msg_queue.put(json.dumps({'type': 'log', 'message': '브라우저 엔진 가동 (전 기능 복구 모드)...'})) + browser = await p.chromium.launch(headless=False, args=[ + "--no-sandbox", + "--disable-dev-shm-usage", + "--disable-blink-features=AutomationControlled" + ]) + context = await browser.new_context( + viewport={'width': 1600, 'height': 900}, + user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" + ) - captured_data = {"tree": None, "_is_root_archive": False, "_tree_url": "", "project_list": []} + captured_data = {"tree": None, "_is_root_archive": False, "project_list": []} async def global_interceptor(response): url = response.url @@ -68,17 +77,13 @@ def crawler_thread_worker(msg_queue, user_id, password): data = await response.json() captured_data["project_list"] = data.get("data", []) elif "getTreeObject" in url: - # [핵심 복원] 정확한 루트 경로 판별 로직 is_root = False if "params[resourcePath]=" in url: path_val = url.split("params[resourcePath]=")[1].split("&")[0] if path_val in ["%2F", "/"]: is_root = True - if is_root: - data = await response.json() - captured_data["tree"] = data - captured_data["_is_root_archive"] = "archive" in url - captured_data["_tree_url"] = url + captured_data["tree"] = await response.json() + captured_data["_is_root_archive"] = True except: pass context.on("response", global_interceptor) @@ -86,54 +91,52 @@ def crawler_thread_worker(msg_queue, user_id, password): await page.goto("https://overseas.projectmastercloud.com/dashboard", wait_until="domcontentloaded") # 로그인 - if await page.locator("#login-by-id").is_visible(timeout=5000): + if await page.locator("#login-by-id").is_visible(timeout=10000): await page.click("#login-by-id") await page.fill("#user_id", user_id) await page.fill("#user_pw", password) await page.click("#login-btn") - # 리스트 로딩 대기 await page.wait_for_selector("h4.list__contents_aria_group_body_list_item_label", timeout=60000) await asyncio.sleep(3) - # [Phase 1] DB 기초 정보 동기화 (마스터 테이블) + # [Phase 1] DB 마스터 정보 동기화 if captured_data["project_list"]: conn = get_db_connection() try: with conn.cursor() as cursor: for p_info in captured_data["project_list"]: - try: - sql = """ - INSERT INTO projects_master (project_id, project_nm, short_nm, master, continent, country) - VALUES (%s, %s, %s, %s, %s, %s) - ON DUPLICATE KEY UPDATE - project_nm = VALUES(project_nm), short_nm = VALUES(short_nm), - master = VALUES(master), continent = VALUES(continent), country = VALUES(country) - """ - cursor.execute(sql, (p_info.get("project_id"), p_info.get("project_nm"), - p_info.get("short_nm", "").strip(), p_info.get("master"), - p_info.get("large_class"), p_info.get("mid_class"))) - except: continue - conn.commit() - msg_queue.put(json.dumps({'type': 'log', 'message': f'DB 마스터 정보 동기화 완료.'})) + sql = """ + INSERT INTO projects_master (project_id, project_nm, short_nm, master, continent, country) + VALUES (%s, %s, %s, %s, %s, %s) + ON DUPLICATE KEY UPDATE + project_nm = VALUES(project_nm), short_nm = VALUES(short_nm), + master = VALUES(master), continent = VALUES(continent), country = VALUES(country) + """ + cursor.execute(sql, (p_info.get("project_id"), p_info.get("project_nm"), + p_info.get("short_nm", "").strip(), p_info.get("master"), + p_info.get("large_class"), p_info.get("mid_class"))) + conn.commit() + msg_queue.put(json.dumps({'type': 'log', 'message': 'DB 마스터 정보 동기화 완료.'})) finally: conn.close() - # [Phase 2] h4 태그 기반 수집 루프 + # [Phase 2] 수집 루프 names = await page.locator("h4.list__contents_aria_group_body_list_item_label").all_inner_texts() project_names = list(dict.fromkeys([n.strip() for n in names if n.strip()])) count = len(project_names) for i, project_name in enumerate(project_names): - # 현재 프로젝트의 고유 ID 매칭 (저장용) + if crawl_stop_event.is_set(): + msg_queue.put(json.dumps({'type': 'log', 'message': '>>> 중단 신호 감지: 종료합니다.'})) + break + + msg_queue.put(json.dumps({'type': 'log', 'message': f'[{i+1}/{count}] {project_name} 수집 시작'})) p_match = next((p for p in captured_data["project_list"] if p.get('project_nm') == project_name or p.get('short_nm', '').strip() == project_name), None) current_p_id = p_match.get('project_id') if p_match else None + captured_data["tree"] = None; captured_data["_is_root_archive"] = False - captured_data["tree"] = None - captured_data["_is_root_archive"] = False - msg_queue.put(json.dumps({'type': 'log', 'message': f'[{i+1}/{count}] {project_name} 수집 시작'})) - try: - # 1. 프로젝트 진입 ([완전 복원] 좌표 클릭) + # 1. 프로젝트 진입 (좌표 클릭) target_el = page.locator(f"h4.list__contents_aria_group_body_list_item_label:has-text('{project_name}')").first await target_el.scroll_into_view_if_needed() box = await target_el.bounding_box() @@ -143,44 +146,34 @@ def crawler_thread_worker(msg_queue, user_id, password): await page.wait_for_selector("text=활동로그", timeout=30000) await asyncio.sleep(2) - recent_log = "데이터 없음" - file_count = 0 + recent_log = "데이터 없음"; file_count = 0 - # 2. 활동로그 ([완전 복원] 3회 재시도 + 좌표 클릭 + 날짜 필터) + # 2. 활동로그 (날짜 필터 적용 버전) modal_opened = False for _ in range(3): - log_btn = page.get_by_text("활동로그").first - btn_box = await log_btn.bounding_box() - if btn_box: await page.mouse.click(btn_box['x'] + 5, btn_box['y'] + 5) - else: await page.evaluate("(el) => el.click()", await log_btn.element_handle()) - + await page.get_by_text("활동로그").first.click() try: await page.wait_for_selector("article.archive-modal", timeout=5000) - modal_opened = True - break + modal_opened = True; break except: await asyncio.sleep(1) if modal_opened: - # 날짜 필터 입력 + # 날짜 필터 2020-01-01 적용 inputs = await page.locator("article.archive-modal input").all() for inp in inputs: if (await inp.get_attribute("type")) == "date": - await inp.fill("2020-01-01") - break + await inp.fill("2020-01-01"); break apply_btn = page.locator("article.archive-modal").get_by_text("적용").first if await apply_btn.is_visible(): await apply_btn.click() - await asyncio.sleep(5) # 렌더링 보장 + await asyncio.sleep(5) log_elements = await page.locator("article.archive-modal div[id*='_']").all() if log_elements: - raw_id = await log_elements[0].get_attribute("id") - recent_log = parse_log_id(raw_id) - msg_queue.put(json.dumps({'type': 'log', 'message': f' - [분석] 최신 로그 ID 추출 성공: {recent_log}'})) - msg_queue.put(json.dumps({'type': 'log', 'message': f' - [최종 결과] {recent_log}'})) + recent_log = parse_log_id(await log_elements[0].get_attribute("id")) await page.keyboard.press("Escape") - # 3. 구성 수집 ([완전 복원] BaseURL fetch + 정밀 합산) + # 3. 구성 수집 (API Fetch 방식 - 팝업 없음) await page.evaluate("""() => { const baseUrl = window.location.origin + window.location.pathname.split('/').slice(0, 2).join('/'); fetch(`${baseUrl}/archive/getTreeObject?params[storageType]=CLOUD¶ms[resourcePath]=/`); @@ -190,43 +183,26 @@ def crawler_thread_worker(msg_queue, user_id, password): await asyncio.sleep(0.5) if captured_data["tree"]: - data_root = captured_data["tree"] - tree = data_root.get('currentTreeObject', data_root) if isinstance(data_root, dict) else {} - total = 0 - # 루트 파일 합산 - rf = tree.get("file", {}) - total += len(rf) if isinstance(rf, (dict, list)) else 0 - # 폴더별 filesCount 합산 + tree = captured_data["tree"].get('currentTreeObject', captured_data["tree"]) + total = len(tree.get("file", {})) folders = tree.get("folder", {}) if isinstance(folders, dict): - for f in folders.values(): - c = f.get("filesCount", "0") - total += int(c) if str(c).isdigit() else 0 + for f in folders.values(): total += int(f.get("filesCount", 0)) file_count = total - msg_queue.put(json.dumps({'type': 'log', 'message': f' - [구성] 데이터 채택 성공: ...{captured_data.get("_tree_url", "")[-40:]}'})) - msg_queue.put(json.dumps({'type': 'log', 'message': f' - [구성] 최종 정밀 합산 성공 ({file_count}개)'})) - # 4. DB 실시간 저장 (히스토리 테이블) + # 4. DB 실시간 저장 if current_p_id: - conn = get_db_connection() - try: + with get_db_connection() as conn: with conn.cursor() as cursor: - # 오늘 날짜 히스토리 데이터 삽입 또는 업데이트 - sql = """ - INSERT INTO projects_history (project_id, crawl_date, recent_log, file_count) - VALUES (%s, CURRENT_DATE(), %s, %s) - ON DUPLICATE KEY UPDATE - recent_log = VALUES(recent_log), file_count = VALUES(file_count) - """ + sql = "INSERT INTO projects_history (project_id, crawl_date, recent_log, file_count) VALUES (%s, CURRENT_DATE(), %s, %s) ON DUPLICATE KEY UPDATE recent_log=VALUES(recent_log), file_count=VALUES(file_count)" cursor.execute(sql, (current_p_id, recent_log, file_count)) conn.commit() - msg_queue.put(json.dumps({'type': 'log', 'message': f' - [DB] 히스토리 업데이트 완료 (ID: {current_p_id})'})) - finally: conn.close() + msg_queue.put(json.dumps({'type': 'log', 'message': f' - [성공] 로그: {recent_log[:20]}... / 파일: {file_count}개'})) await page.goto("https://overseas.projectmastercloud.com/dashboard", wait_until="domcontentloaded") except Exception as e: - msg_queue.put(json.dumps({'type': 'log', 'message': f' - [{project_name}] 건너뜀: {str(e)}'})) + msg_queue.put(json.dumps({'type': 'log', 'message': f' - {project_name} 실패: {str(e)}'})) await page.goto("https://overseas.projectmastercloud.com/dashboard") msg_queue.put(json.dumps({'type': 'done', 'data': []})) @@ -241,10 +217,8 @@ def crawler_thread_worker(msg_queue, user_id, password): loop.close() async def run_crawler_service(): - user_id = os.getenv("PM_USER_ID") - password = os.getenv("PM_PASSWORD") msg_queue = queue.Queue() - thread = threading.Thread(target=crawler_thread_worker, args=(msg_queue, user_id, password)) + thread = threading.Thread(target=crawler_thread_worker, args=(msg_queue, os.getenv("PM_USER_ID"), os.getenv("PM_PASSWORD"))) thread.start() while True: try: diff --git a/crawling_result 2026.03.06.csv b/crawling_result 2026.03.06.csv deleted file mode 100644 index bd6d6c4..0000000 --- a/crawling_result 2026.03.06.csv +++ /dev/null @@ -1,42 +0,0 @@ -projectName,recentLog,fileCount -ITTC 관개 교육센터,"26.01.29, 박진규, 폴더 삭제",16 -비엔티안 메콩강 관리 2차,"25.12.07, 나쉬, 파일 업로드",260 -만달레이 철도 개량 감리,"25.11.19, 이태훈, 보안참여자 권한 추가",298 -푸옥호아 양수 발전,"26.02.23, 이철호, 폴더 이름 변경",139 -아시르 지잔 고속도로,"26.03.04, 이태훈, 보안참여자 권한 추가",73 -지방 도로 복원,"26.03.04, 이태훈, 보안참여자 권한 추가",0 -타슈켄트 철도,"26.02.05, 조항언, 파일 업로드",51 -Habbaniyah Shuaiba AirBase,"26.03.04, 이태훈, 보안참여자 권한 추가",0 -시엠립 하수처리 개선,"26.02.09, 이태훈, 보안참여자 권한 추가",221 -반테 민체이 관개 홍수저감,"25.12.07, 나쉬, 파일 업로드",44 -메콩유역 수자원 관리 기후적응,"25.11.19, 이태훈, 보안참여자 권한 추가",0 -잘랄아바드 상수도 계획,"26.03.06, -, 폴더 자동 삭제 (파일 개수 미달)",58 -CAREC 도로 감리,"26.03.04, 이태훈, 보안참여자 권한 추가",0 -펀잡 홍수 방재,"25.12.08, 콰윰 아딜, 폴더 삭제",0 -KP 아보타바드 상수도,"26.02.26, 정기일, 파일 업로드",240 -홍수 복원 InFRA2,"25.12.18, -, 폴더 자동 삭제 (파일 개수 미달)",6 -PGN 해상교량 BID2,"26.03.04, 이태훈, 보안참여자 권한 추가",631 -홍수 관리 Package5B,"25.12.02, 조명훈, 폴더 이름 변경",14 -족자~바웬 도로사업,"26.03.06, 시스템관리-Savannah, 참관자 권한 추가",0 -테치만 상수도 확장,"26.02.09, 이태훈, 보안참여자 권한 추가",0 -기니 벼 재배단지,"26.01.07, -, 폴더 자동 삭제 (파일 개수 미달)",43 -부수쿠마 분뇨 자원화 2단계,"26.02.09, 이태훈, 보안참여자 권한 추가",9 -우간다 벼 재배단지,"25.12.08, 박수빈, 파일 업로드",52 -Adeaa-Becho 지하수 관개,"25.12.29, -, 폴더 자동 삭제 (파일 개수 미달)",140 -도도타군 관개,"25.12.30, -, 폴더 자동 삭제 (파일 개수 미달)",142 -지하수 관개 환경설계,"26.02.09, 이태훈, 보안참여자 권한 추가",0 -Dodoma 하수 설계감리,"26.02.09, 이태훈, 보안참여자 권한 추가",32 -Iringa 상하수도 개선,"26.02.09, 이태훈, 보안참여자 권한 추가",0 -도도마 유수율 상수도개선,"26.02.12, 서하연, 부관리자 권한 추가",35 -잔지바르 쌀 생산,"25.12.08, 박수빈, 파일 업로드",23 -SALDEORO 수력발전 28MW,"25.11.19, 이태훈, 보안참여자 권한 추가",0 -LaPaz Danli 상수도,"26.02.09, 이태훈, 보안참여자 권한 추가",60 -에스꼬마 차라짜니 도로,"26.03.06, -, 폴더 자동 삭제 (파일 개수 미달)",0 -마모레 교량도로,"26.03.04, 이태훈, 보안참여자 권한 추가",120 -Bombeo-Colomi 도로설계,"26.03.04, 이태훈, 보안참여자 권한 추가",48 -AI 폐기물,"25.11.19, 이태훈, 보안참여자 권한 추가",0 -도로 통행료 현대화,"26.02.25, 류창수, 폴더 삭제",0 -Barranca 상하수도 확장,"26.02.09, 이태훈, 보안참여자 권한 추가",44 -태평양 철도,"26.02.24, -, 폴더 자동 삭제 (파일 개수 미달)",101 -필리핀 사무소,"26.03.06, 한형남, 파일 다운로드",316 -PGN 해상교량 BID2,"26.03.04, 이태훈, 보안참여자 권한 추가",631 diff --git a/crawling_result 2026.03.09.csv b/crawling_result 2026.03.09.csv deleted file mode 100644 index b1bc926..0000000 --- a/crawling_result 2026.03.09.csv +++ /dev/null @@ -1,42 +0,0 @@ -projectName,recentLog,fileCount -비엔티안 메콩강 관리 2차,212487_25-12-07 12:22:26_나쉬_파일 업로드_/서류/06. 전문가파견/01. 파견/251031~260208_전문가 파견(이범주).zip,260 -ITTC 관개 교육센터,225728_26-01-29 09:10:21_박진규_폴더 삭제_/서류/ggg,16 -만달레이 철도 개량 감리,207041_25-11-19 16:54:36_이태훈_보안참여자 권한 추가_홍아름_김혜인,298 -푸옥호아 양수 발전,233465_26-02-23 10:24:46_이철호_폴더 이름 변경_/6 준공/3 준공도서 26년 2월 작성예정 발주처 협의중_/6 준공/3 준공도서 26년 3월 작성예정 발주처 협의중,139 -아시르 지잔 고속도로,234455_26-03-04 14:31:52_이태훈_보안참여자 권한 추가_복진훈,73 -타슈켄트 철도,228919_26-02-05 10:08:11_조항언_파일 업로드_/02_성과품/B_중간보고/BB_중간보고서/[러문] INTERIM REPORT_0115_F.pdf,51 -지방 도로 복원,234456_26-03-04 14:32:07_이태훈_보안참여자 권한 추가_복진훈,0 -Habbaniyah Shuaiba AirBase,234457_26-03-04 14:32:28_이태훈_보안참여자 권한 추가_복진훈,0 -시엠립 하수처리 개선,231205_26-02-09 11:03:05_이태훈_보안참여자 권한 추가_김창환_배형원,221 -반테 민체이 관개 홍수저감,212512_25-12-07 12:35:02_나쉬_파일 업로드_/서류/04. 기성/18차 기성금/06. 통장사본.pdf,44 -메콩유역 수자원 관리 기후적응,207047_25-11-19 17:01:19_이태훈_보안참여자 권한 추가_홍아름_김혜인,0 -잘랄아바드 상수도 계획,234860_26-03-06 10:24:27_-_폴더 자동 삭제 (파일 개수 미달)_/4. MP 성과품/3. 최종보고서/초안 제출,58 -펀잡 홍수 방재,212686_25-12-08 13:05:24_콰윰 아딜_폴더 삭제_/RFP,0 -CAREC 도로 감리,234458_26-03-04 14:32:50_이태훈_보안참여자 권한 추가_복진훈,0 -KP 아보타바드 상수도,234120_26-02-26 20:58:46_정기일_파일 업로드_/99 참고자료/99 체코 두코바니 원전/00 2025년 입찰/Engineering work instruction_241206.pdf,240 -PGN 해상교량 BID2,234454_26-03-04 14:31:31_이태훈_보안참여자 권한 추가_복진훈,631 -홍수 복원 InFRA2,214399_25-12-18 09:05:49_-_폴더 자동 삭제 (파일 개수 미달)_/서류/3.경비신청 및 정산/1. 신청,6 -홍수 관리 Package5B,211353_25-12-02 10:04:18_조명훈_폴더 이름 변경_/서류/01. 계약서/01. 계약서_/서류/01. 계약서/01. 사업계약서,14 -족자~바웬 도로사업,234908_26-03-06 13:36:39_시스템관리-Savannah_참관자 권한 추가_이호성,0 -테치만 상수도 확장,231210_26-02-09 11:06:41_이태훈_보안참여자 권한 추가_배형원_정낙훈,0 -기니 벼 재배단지,216888_26-01-07 11:07:23_-_폴더 자동 삭제 (파일 개수 미달)_/계약서류/경비 신청 및 정산/01. 신청,43 -우간다 벼 재배단지,"212622_25-12-08 11:17:57_박수빈_파일 업로드_/계약서류/경비 신청 및 정산/01. 현장경비 신청전표/11-20251029-J0401-006 (9,10월 현장운영비).pdf",52 -부수쿠마 분뇨 자원화 2단계,231215_26-02-09 11:08:34_이태훈_보안참여자 권한 추가_김창환_배형원,9 -지하수 관개 환경설계,231212_26-02-09 11:07:35_이태훈_보안참여자 권한 추가_김창환_배형원,0 -Adeaa-Becho 지하수 관개,215553_25-12-29 09:36:23_-_폴더 자동 삭제 (파일 개수 미달)_/Topographic Survey/측량조사성과품/Appendix B - List of GPS Control Points,140 -도도타군 관개,215706_25-12-30 09:17:43_-_폴더 자동 삭제 (파일 개수 미달)_/서류/03.경비신청 및 정산/02.사내정산 2차,142 -Iringa 상하수도 개선,231216_26-02-09 11:10:18_이태훈_보안참여자 권한 추가_김창환_배형원,0 -Dodoma 하수 설계감리,231217_26-02-09 11:11:06_이태훈_보안참여자 권한 추가_김창환_배형원,32 -도도마 유수율 상수도개선,232629_26-02-12 17:26:13_서하연_부관리자 권한 추가_정기일,35 -잔지바르 쌀 생산,212641_25-12-08 11:43:37_박수빈_파일 업로드_/서류/경비신청 및 정산/01. 25년/11-20250409-J0401-005 (운영경비 2분기 송금).pdf,23 -SALDEORO 수력발전 28MW,207029_25-11-19 16:44:04_이태훈_보안참여자 권한 추가_홍아름_김혜인,0 -LaPaz Danli 상수도,231219_26-02-09 11:12:37_이태훈_보안참여자 권한 추가_배형원_정낙훈,60 -에스꼬마 차라짜니 도로,234837_26-03-06 08:00:21_-_폴더 자동 삭제 (파일 개수 미달)_/EOI/001/EOI서류,0 -Bombeo-Colomi 도로설계,234463_26-03-04 14:34:24_이태훈_보안참여자 권한 추가_복진훈,48 -마모레 교량도로,234462_26-03-04 14:33:56_이태훈_보안참여자 권한 추가_복진훈,120 -AI 폐기물,207034_25-11-19 16:50:49_이태훈_보안참여자 권한 추가_홍아름_김혜인,0 -도로 통행료 현대화,233938_26-02-25 14:39:08_류창수_폴더 삭제_/필리핀다바오프로젝트,0 -Barranca 상하수도 확장,231220_26-02-09 11:13:28_이태훈_보안참여자 권한 추가_김창환_배형원,44 -태평양 철도,233798_26-02-24 17:52:33_-_폴더 자동 삭제 (파일 개수 미달)_/01_수행문서/D_보고자료/D6_최종보고,101 -필리핀 사무소,234973_26-03-09 09:35:45_한형남_파일 다운로드_/3. DPTMP(Davao Public Transportation Modernization Project)/AFCS/2.배포자료/PHI DPTMP AFCS Package PIM 04March2026.pdf_/3. DPTMP(Davao Public Transportation Modernization Project)/AFCS/2.배포자료/PAFCS PIM v04March2026.pdf,322 -PGN 해상교량 BID2,234454_26-03-04 14:31:31_이태훈_보안참여자 권한 추가_복진훈,631 diff --git a/crwaling_result.csv b/crwaling_result.csv deleted file mode 100644 index 5560a02..0000000 --- a/crwaling_result.csv +++ /dev/null @@ -1,42 +0,0 @@ -projectName,recentLog,fileCount -ITTC 관개 교육센터,"26.01.29, 박진규, 폴더 삭제",16 -비엔티안 메콩강 관리 2차,"25.12.07, 나쉬, 파일 업로드",260 -만달레이 철도 개량 감리,"25.11.19, 이태훈, 보안참여자 권한 추가",298 -푸옥호아 양수 발전,"26.02.23, 이철호, 폴더 이름 변경",139 -아시르 지잔 고속도로,"26.03.04, 이태훈, 보안참여자 권한 추가",75 -지방 도로 복원,"26.03.04, 이태훈, 보안참여자 권한 추가",0 -타슈켄트 철도,"26.02.05, 조항언, 파일 업로드",51 -Habbaniyah Shuaiba AirBase,"26.03.04, 이태훈, 보안참여자 권한 추가",0 -시엠립 하수처리 개선,"26.02.09, 이태훈, 보안참여자 권한 추가",222 -반테 민체이 관개 홍수저감,"25.12.07, 나쉬, 파일 업로드",0 -메콩유역 수자원 관리 기후적응,"25.11.19, 이태훈, 보안참여자 권한 추가",0 -잘랄아바드 상수도 계획,"26.03.06, -, 폴더 자동 삭제 (파일 개수 미달)",58 -CAREC 도로 감리,"26.03.04, 이태훈, 보안참여자 권한 추가",0 -펀잡 홍수 방재,"25.12.08, 콰윰 아딜, 폴더 삭제",0 -KP 아보타바드 상수도,"26.02.26, 정기일, 파일 업로드",240 -홍수 복원 InFRA2,"25.12.18, -, 폴더 자동 삭제 (파일 개수 미달)",6 -PGN 해상교량 BID2,"26.03.04, 이태훈, 보안참여자 권한 추가",0 -홍수 관리 Package5B,"25.12.02, 조명훈, 폴더 이름 변경",14 -족자~바웬 도로사업,"26.03.06, 시스템관리-Savannah, 참관자 권한 추가",0 -테치만 상수도 확장,"26.02.09, 이태훈, 보안참여자 권한 추가",0 -기니 벼 재배단지,"26.01.07, -, 폴더 자동 삭제 (파일 개수 미달)",44 -부수쿠마 분뇨 자원화 2단계,"26.02.09, 이태훈, 보안참여자 권한 추가",9 -우간다 벼 재배단지,"25.12.08, 박수빈, 파일 업로드",52 -Adeaa-Becho 지하수 관개,"25.12.29, -, 폴더 자동 삭제 (파일 개수 미달)",140 -도도타군 관개,"25.12.30, -, 폴더 자동 삭제 (파일 개수 미달)",0 -지하수 관개 환경설계,"26.02.09, 이태훈, 보안참여자 권한 추가",0 -Dodoma 하수 설계감리,"26.02.09, 이태훈, 보안참여자 권한 추가",32 -Iringa 상하수도 개선,"26.02.09, 이태훈, 보안참여자 권한 추가",0 -도도마 유수율 상수도개선,"26.02.12, 서하연, 부관리자 권한 추가",35 -잔지바르 쌀 생산,"25.12.08, 박수빈, 파일 업로드",23 -SALDEORO 수력발전 28MW,"25.11.19, 이태훈, 보안참여자 권한 추가",0 -LaPaz Danli 상수도,"26.02.09, 이태훈, 보안참여자 권한 추가",65 -에스꼬마 차라짜니 도로,"26.03.06, -, 폴더 자동 삭제 (파일 개수 미달)",0 -마모레 교량도로,"26.03.04, 이태훈, 보안참여자 권한 추가",120 -Bombeo-Colomi 도로설계,"26.03.04, 이태훈, 보안참여자 권한 추가",49 -AI 폐기물,"25.11.19, 이태훈, 보안참여자 권한 추가",0 -도로 통행료 현대화,"26.02.25, 류창수, 폴더 삭제",0 -Barranca 상하수도 확장,"26.02.09, 이태훈, 보안참여자 권한 추가",50 -태평양 철도,"26.02.24, -, 폴더 자동 삭제 (파일 개수 미달)",101 -필리핀 사무소,"26.03.06, 한형남, 파일 다운로드",0 -PGN 해상교량 BID2,"26.03.04, 이태훈, 보안참여자 권한 추가",637 diff --git a/db_2026.03.09.csv b/db_2026.03.09.csv deleted file mode 100644 index 475e24e..0000000 --- a/db_2026.03.09.csv +++ /dev/null @@ -1,42 +0,0 @@ -[PM Overseas 프로젝트 현황],,2026.03.04,,,,,,<<활동로그가 없는 프로젝트 (8),, -,,,,,,,,,, -No.,프로젝트 명,담당부서,담당자,종료(예정)일,최근 활동로그,과업개요 작성 유무,파일 수,비고,, -1,라오스 ITTC 관개 교육센터 PMC,수자원1부,방노성,2025.12.20,"2026.01.29, 폴더 삭제",O,16,2026.01.29 로그는 테스트 활동 추정,종료(예정)일 지남,진행 -2,라오스 비엔티안 메콩강 관리 2차 DD,수자원1부,방노성,2026.05.31,"2025.12.07, 파일업로드",X,260,탭 1개에 모든파일 업로드,, -3,미얀마 만달레이 철도 개량 감리 CS,철도사업부,김태헌,2027.11.17,"2025.11.17, 폴더이름변경",O,298,,, -4,베트남 푸옥호아 양수 발전 FS,수력부,이철호,2025.11.30,"2026.02.23, 폴더이름변경",O,139,준공도서 3월 작성예정,종료(예정)일 지남,준공 -5,사우디아라비아 아시르 지잔 고속도로 FS,도로부,공태원,2025.11.21,"2026.02.09, 파일다운로드",O,73,,종료(예정)일 지남,준공 -6,우즈베키스탄 지방 도로 복원 MP,도로부,장진영,2029.04.28,X,X,0,,, -7,우즈베키스탄 타슈켄트 철도 FS,철도사업부,김태헌,2026.03.20,"2026.02.05, 파일업로드",O,51,,, -8,이라크 Habbaniyah Shuaiba AirBase PD,도로부,강동구,2026.12.31,X,X,0,,, -9,메콩유역 수자원 관리 기후적응 MP,수자원1부,정귀한,2025.12.31,X,X,0,,종료(예정)일 지남,준공 -10,캄보디아 반테 민체이 관개 홍수저감 MP,수자원1부,이대주,2026.08.28,"2025.12.07, 파일업로드",X,44,,, -11,캄보디아 시엠립 하수처리 개선 DD,물환경사업1부,변역근,2028.12.18,"2026.02.06, AI 요약",O,221,,, -12,키르기스스탄 잘랄아바드 상수도 계획 MP,물환경사업1부,변기상,2025.12.31,"2026.02.12, 파일업로드",X,60,,종료(예정)일 지남,준공 -13,파키스탄 펀잡 홍수 방재 PMC,수자원1부,방노성,2027.12.31,"2025.12.08, 폴더삭제",O,0,,, -14,파키스탄 KP 아보타바드 상수도 PMC,물환경사업2부,변기상,2026.12.31,"2026.02.26, 파일업로드",O,240,,, -15,파키스탄 CAREC 도로 감리 DD,도로부,황효섭,2026.10.26,X,X,0,,, -16,필리핀 홍수 복원 InFRA2 DD,수자원1부,이대주,2026.08.07,"2025.12.01, 폴더삭제",O,6,최근로그 >> 폴더자동삭제(파일 개수 미달),, -17,필리핀 홍수 관리 Package5B MP,수자원1부,이희철,2026.05.31,"2025.12.02, 폴더이름변경",O,14,,, -18,필리핀 PGN 해상교량 BID2 IDC,구조부,이상희,2026.05.31,"2026.02.11, 파일다운로드",O,631,,, -19,가나 테치만 상수도 확장 DS,물환경사업2부,-,2029.04.25,X,X,0,책임자 및 담당자 설정X,, -20,기니 벼 재배단지 PMC,수자원1부,이대주,2028.12.20,"2025.12.08, 파일업로드",O,43,최근로그 >> 폴더자동삭제(파일 개수 미달),, -21,우간다 벼 재배단지 PMC,수자원1부,방노성,2028.12.20,"2025.12.08, 파일업로드",O,52,,, -22,우간다 부수쿠마 분뇨 자원화 2단계 PMC,물환경사업2부,변기상,2026.12.31,"2026.02.05, 파일업로드",X,9,,, -23,에티오피아 지하수 관개 환경설계 DD,물환경사업2부,변기상,2026.06.23,X,X,0,,, -24,에티오피아 도도타군 관개 PMC,수자원1부,방노성,2026.12.31,"2025.12.01, 폴더이름변경",O,144,탭 1개에 모든파일 업로드 // 최근로그 >> 폴더자동삭제(파일 개수 미달),, -25,에티오피아 Adeaa-Becho 지하수 관개 MP,수자원1부,방노성,2026.07.31,"2025.11.21, 파일업로드",O,146,최근로그 >> 폴더자동삭제(파일 개수 미달),, -26,탄자니아 Iringa 상하수도 개선 CS,물환경사업1부,백운영,2029.06.08,"2026.02.03, 폴더생성",X,0,,, -27,탄자니아 Dodoma 하수 설계감리 DD,물환경사업2부,변기상,2027.07.08,"2026.02.04, 폴더삭제",X,32,,, -28,탄자니아 잔지바르 쌀 생산 PMC,수자원1부,방노성,2027.12.20,"2025.12.08, 파일 업로드",O,23,,, -29,탄자니아 도도마 유수율 상수도개선 PMC,물환경사업1부,박순석,2026.12.31,"2026.02.12, 부관리자권한추가",X,35,,, -30,아르헨티나 SALDEORO 수력발전 28MW DD,플랜트1부,양정모,2026.01.31,X,X,0,,종료(예정)일 지남,준공 -31,온두라스 LaPaz Danli 상수도 CS,물환경사업2부,-,2027.02.23,"2026.01.29, 파일 삭제",O,60,"책임자 및 담당자 설정 X, 실 관리부서는 해외사업부, 더미파일 다수",, -32,볼리비아 에스꼬마 차라짜니 도로 CS,도로부,전홍찬,2029.12.15,"2026.02.06, 파일업로드",X,1,,, -33,볼리비아 마모레 교량도로 FS,도로부,황효섭,2025.10.17,"2026.02.06, 파일업로드",X,120,,종료(예정)일 지남,준공 -34,볼리비아 Bombeo-Colomi 도로설계 DD,도로부,황효섭,2026.07.24,"2025.12.05, 파일삭제",O,48,"더미파일(폴더유지용) 12개, 실 관리부서는 해외사업부",, -35,콜롬비아 AI 폐기물 FS,플랜트1부,서재희,2026.02.27,X,X,0,,종료(예정)일 지남, -36,파라과이 도로 통행료 현대화 MP,교통계획부,오제훈,2025.10.24,"2025.02.25, 폴더삭제",X,0,,종료(예정)일 지남,준공 -37,페루 Barranca 상하수도 확장 DD,물환경사업2부,변기상,2026.03.08,"2025.11.14, 파일업로드",O,44,"더미파일(폴더유지용) 27개, 실 관리부서는 해외사업부",, -38,엘살바도르 태평양 철도 FS,철도사업부,김태헌,2025.12.31,"2026.02.24, 폴더자동삭제",X,101,,종료(예정)일 지남,준공 -39,필리핀 사무소,해외사업부,한형남,,"2026-03-09, 파일다운로드",과업개요 페이지 없음,323,,, diff --git a/db_2026.03.10.csv b/db_2026.03.10.csv deleted file mode 100644 index 4708abf..0000000 --- a/db_2026.03.10.csv +++ /dev/null @@ -1,42 +0,0 @@ -[PM Overseas 프로젝트 현황],,2026.03.04,,,,,,<<활동로그가 없는 프로젝트 (8),, -,,,,,,,,,, -No.,프로젝트 명,담당부서,담당자,종료(예정)일,최근 활동로그,과업개요 작성 유무,파일 수,비고,, -1,라오스 ITTC 관개 교육센터 PMC,수자원1부,방노성,2025.12.20,"2026.01.29, 폴더 삭제",O,16,2026.01.29 로그는 테스트 활동 추정,종료(예정)일 지남,진행 -2,라오스 비엔티안 메콩강 관리 2차 DD,수자원1부,방노성,2026.05.31,"2025.12.07, 파일업로드",X,260,탭 1개에 모든파일 업로드,, -3,미얀마 만달레이 철도 개량 감리 CS,철도사업부,김태헌,2027.11.17,"2025.11.17, 폴더이름변경",O,298,,, -4,베트남 푸옥호아 양수 발전 FS,수력부,이철호,2025.11.30,"2026.02.23, 폴더이름변경",O,139,준공도서 3월 작성예정,종료(예정)일 지남,준공 -5,사우디아라비아 아시르 지잔 고속도로 FS,도로부,공태원,2025.11.21,"2026.02.09, 파일다운로드",O,73,,종료(예정)일 지남,준공 -6,우즈베키스탄 지방 도로 복원 MP,도로부,장진영,2029.04.28,X,X,0,,, -7,우즈베키스탄 타슈켄트 철도 FS,철도사업부,김태헌,2026.03.20,"2026.02.05, 파일업로드",O,51,,, -8,이라크 Habbaniyah Shuaiba AirBase PD,도로부,강동구,2026.12.31,X,X,0,,, -9,메콩유역 수자원 관리 기후적응 MP,수자원1부,정귀한,2025.12.31,X,X,0,,종료(예정)일 지남,준공 -10,캄보디아 반테 민체이 관개 홍수저감 MP,수자원1부,이대주,2026.08.28,"2025.12.07, 파일업로드",X,44,,, -11,캄보디아 시엠립 하수처리 개선 DD,물환경사업1부,변역근,2028.12.18,"2026.02.06, AI 요약",O,221,,, -12,키르기스스탄 잘랄아바드 상수도 계획 MP,물환경사업1부,변기상,2025.12.31,"2026.02.12, 파일업로드",X,60,,종료(예정)일 지남,준공 -13,파키스탄 펀잡 홍수 방재 PMC,수자원1부,방노성,2027.12.31,"2025.12.08, 폴더삭제",O,0,,, -14,파키스탄 KP 아보타바드 상수도 PMC,물환경사업2부,변기상,2026.12.31,"2026.02.26, 파일업로드",O,240,,, -15,파키스탄 CAREC 도로 감리 DD,도로부,황효섭,2026.10.26,X,X,0,,, -16,필리핀 홍수 복원 InFRA2 DD,수자원1부,이대주,2026.08.07,"2025.12.01, 폴더삭제",O,6,최근로그 >> 폴더자동삭제(파일 개수 미달),, -17,필리핀 홍수 관리 Package5B MP,수자원1부,이희철,2026.05.31,"2025.12.02, 폴더이름변경",O,14,,, -18,필리핀 PGN 해상교량 BID2 IDC,구조부,이상희,2026.05.31,"2026.02.11, 파일다운로드",O,631,,, -19,가나 테치만 상수도 확장 DS,물환경사업2부,-,2029.04.25,X,X,0,책임자 및 담당자 설정X,, -20,기니 벼 재배단지 PMC,수자원1부,이대주,2028.12.20,"2025.12.08, 파일업로드",O,43,최근로그 >> 폴더자동삭제(파일 개수 미달),, -21,우간다 벼 재배단지 PMC,수자원1부,방노성,2028.12.20,"2025.12.08, 파일업로드",O,52,,, -22,우간다 부수쿠마 분뇨 자원화 2단계 PMC,물환경사업2부,변기상,2026.12.31,"2026.02.05, 파일업로드",X,9,,, -23,에티오피아 지하수 관개 환경설계 DD,물환경사업2부,변기상,2026.06.23,X,X,0,,, -24,에티오피아 도도타군 관개 PMC,수자원1부,방노성,2026.12.31,"2025.12.01, 폴더이름변경",O,144,탭 1개에 모든파일 업로드 // 최근로그 >> 폴더자동삭제(파일 개수 미달),, -25,에티오피아 Adeaa-Becho 지하수 관개 MP,수자원1부,방노성,2026.07.31,"2025.11.21, 파일업로드",O,146,최근로그 >> 폴더자동삭제(파일 개수 미달),, -26,탄자니아 Iringa 상하수도 개선 CS,물환경사업1부,백운영,2029.06.08,"2026.02.03, 폴더생성",X,0,,, -27,탄자니아 Dodoma 하수 설계감리 DD,물환경사업2부,변기상,2027.07.08,"2026.02.04, 폴더삭제",X,32,,, -28,탄자니아 잔지바르 쌀 생산 PMC,수자원1부,방노성,2027.12.20,"2025.12.08, 파일 업로드",O,23,,, -29,탄자니아 도도마 유수율 상수도개선 PMC,물환경사업1부,박순석,2026.12.31,"2026.02.12, 부관리자권한추가",X,35,,, -30,아르헨티나 SALDEORO 수력발전 28MW DD,플랜트1부,양정모,2026.01.31,X,X,0,,종료(예정)일 지남,준공 -31,온두라스 LaPaz Danli 상수도 CS,물환경사업2부,-,2027.02.23,"2026.01.29, 파일 삭제",O,60,"책임자 및 담당자 설정 X, 실 관리부서는 해외사업부, 더미파일 다수",, -32,볼리비아 에스꼬마 차라짜니 도로 CS,도로부,전홍찬,2029.12.15,"2026.02.06, 파일업로드",X,1,,, -33,볼리비아 마모레 교량도로 FS,도로부,황효섭,2025.10.17,"2026.02.06, 파일업로드",X,120,,종료(예정)일 지남,준공 -34,볼리비아 Bombeo-Colomi 도로설계 DD,도로부,황효섭,2026.07.24,"2025.12.05, 파일삭제",O,48,"더미파일(폴더유지용) 12개, 실 관리부서는 해외사업부",, -35,콜롬비아 AI 폐기물 FS,플랜트1부,서재희,2026.02.27,X,X,0,,종료(예정)일 지남, -36,파라과이 도로 통행료 현대화 MP,교통계획부,오제훈,2025.10.24,"2025.02.25, 폴더삭제",X,0,,종료(예정)일 지남,준공 -37,페루 Barranca 상하수도 확장 DD,물환경사업2부,변기상,2026.03.08,"2025.11.14, 파일업로드",O,44,"더미파일(폴더유지용) 27개, 실 관리부서는 해외사업부",, -38,엘살바도르 태평양 철도 FS,철도사업부,김태헌,2025.12.31,"2026.02.24, 폴더자동삭제",X,101,,종료(예정)일 지남,준공 -39,필리핀 사무소,해외사업부,한형남,,"2026.03.10, PDF 변환",과업개요 페이지 없음,829,,, diff --git a/debug_modal.html b/debug_modal.html deleted file mode 100644 index 31b4811..0000000 --- a/debug_modal.html +++ /dev/null @@ -1,226 +0,0 @@ - -
-
-
- 로그필터 - -
-
-
- 활동시간 -
- 시작 - -
-
- 종료 - -
-
-
- 사용자 -
-
모든 사용자
- - -
-
-
-
- 활동유형 -
- - -
-
- 파일 / 폴더관련 - - - - - - - - - 유저관련 - - - - 기타 - -
-
-
- -
-
- - -
- \ No newline at end of file diff --git a/diag_folders.py b/diag_folders.py deleted file mode 100644 index d63fa8e..0000000 --- a/diag_folders.py +++ /dev/null @@ -1,66 +0,0 @@ -import asyncio, os, json, re, sys -from playwright.async_api import async_playwright -from dotenv import load_dotenv - -load_dotenv() - -async def run_diagnostics(): - user_id = os.getenv("PM_USER_ID") - password = os.getenv("PM_PASSWORD") - - async with async_playwright() as p: - browser = await p.chromium.launch(headless=False) - context = await browser.new_context(viewport={"width": 1600, "height": 900}) - page = await context.new_page() - - print(">>> 로그인 중...") - await page.goto("https://overseas.projectmastercloud.com/dashboard") - if await page.locator("#login-by-id").is_visible(timeout=5000): - await page.click("#login-by-id") - await page.fill("#user_id", user_id) - await page.fill("#user_pw", password) - await page.click("#login-btn") - - await page.wait_for_selector("h4.list__contents_aria_group_body_list_item_label", timeout=60000) - - project_name = "필리핀 사무소" - print(f">>> [{project_name}] 폴더 전수 조사 시작...") - - target_el = page.get_by_text(project_name).first - await target_el.scroll_into_view_if_needed() - await target_el.click(force=True) - - await asyncio.sleep(10) # 충분한 로딩 대기 - - print("\n" + "="*60) - print(f"{'No':<4} | {'Folder Name':<40} | {'Files'}") - print("-" * 60) - - # fetch 결과를 직접 리턴받음 - tree_data = await page.evaluate("""async () => { - const resp = await fetch('/api/getTreeObject?params[storageType]=CLOUD¶ms[resourcePath]=/'); - return await resp.json(); - }""") - - if tree_data: - tree = tree_data.get('currentTreeObject', {}) - folders = tree.get('folder', {}) - folder_items = list(folders.values()) if isinstance(folders, dict) else (folders if isinstance(folders, list) else []) - - total_sum = 0 - for i, f in enumerate(folder_items): - name = f.get('name', 'Unknown') - count = int(f.get('filesCount', 0)) - total_sum += count - print(f"{i+1:<4} | {name:<40} | {count}개") - - print("-" * 60) - print(f">>> 총 {len(folder_items)}개 폴더 발견 | 전체 파일 합계: {total_sum}개") - print("="*60) - else: - print(">>> [오류] 데이터를 가져오지 못했습니다.") - - await browser.close() - -if __name__ == "__main__": - asyncio.run(run_diagnostics()) diff --git a/js/common.js b/js/common.js index 94ef6aa..accf091 100644 --- a/js/common.js +++ b/js/common.js @@ -1,9 +1,25 @@ -// 공통 네비게이션 및 유틸리티 로직 +/** + * Project Master Overseas Common JS + * 공통 네비게이션, 유틸리티, 전역 이벤트 관리 + */ + function navigateTo(path) { location.href = path; } -// 상단바 클릭 시 홈으로 이동 등 공통 이벤트 설정 -document.addEventListener('DOMContentLoaded', () => { - // 필요한 경우 공통 초기화 로직 추가 +// --- 전역 이벤트: 모든 모달창 ESC 키로 닫기 --- +document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + // 대시보드 모달 + if (typeof closeAuthModal === 'function') closeAuthModal(); + if (typeof closeActivityModal === 'function') closeActivityModal(); + + // 메일 시스템 모달 + if (typeof closeModal === 'function') closeModal(); + if (typeof closeAddressBook === 'function') closeAddressBook(); + } +}); + +document.addEventListener('DOMContentLoaded', () => { + // 공통 초기화 로직 }); diff --git a/js/dashboard.js b/js/dashboard.js index a870fca..41daaa9 100644 --- a/js/dashboard.js +++ b/js/dashboard.js @@ -1,295 +1,220 @@ +/** + * Project Master Overseas Dashboard JS + * 기능: 데이터 로드, 활성도 분석, 인증 모달 제어, 크롤링 동기화 및 중단 + */ + +// --- 글로벌 상태 관리 --- let rawData = []; +let projectActivityDetails = []; +let isCrawling = false; -const continentMap = { - "라오스": "아시아", "미얀마": "아시아", "베트남": "아시아", "사우디아라비아": "아시아", - "우즈베키스탄": "아시아", "이라크": "아시아", "캄보디아": "아시아", - "키르기스스탄": "아시아", "파키스탄": "아시아", "필리핀": "아시아", - "아르헨티나": "아메리카", "온두라스": "아메리카", "볼리비아": "아메리카", "콜롬비아": "아메리카", - "파라과이": "아메리카", "페루": "아메리카", "엘살바도르": "아메리카", - "가나": "아프리카", "기니": "아프리카", "우간다": "아프리카", "에티오피아": "아프리카", "탄자니아": "아프리카" -}; - -const continentOrder = { - "아시아": 1, - "아프리카": 2, - "아메리카": 3, - "지사": 4 -}; +const CONTINENT_ORDER = { "아시아": 1, "아프리카": 2, "아메리카": 3, "지사": 4 }; +// --- 초기화 --- async function init() { + console.log("Dashboard Initializing..."); const container = document.getElementById('projectAccordion'); - const baseDateStrong = document.getElementById('baseDate'); - if (!container) return; + if (!container) return; - // 1. 가용한 날짜 목록 가져오기 및 셀렉트 박스 생성 + await loadAvailableDates(); + await loadDataByDate(); +} + +// --- 데이터 통신 및 로드 --- +async function loadAvailableDates() { try { - const datesRes = await fetch('/available-dates'); - const dates = await datesRes.json(); - - if (dates && dates.length > 0) { - let selectHtml = ``; - // 기준날짜 텍스트 영역을 셀렉트 박스로 교체 - const baseDateInfo = document.querySelector('.base-date-info'); - if (baseDateInfo) { - baseDateInfo.innerHTML = `기준날짜: ${selectHtml}`; - } + const response = await fetch('/available-dates'); + const dates = await response.json(); + if (dates?.length > 0) { + const selectHtml = ` + `; + const baseDateStrong = document.getElementById('baseDate'); + if (baseDateStrong) baseDateStrong.innerHTML = selectHtml; } - } catch (e) { - console.error("날짜 목록 로드 실패:", e); - } - - // 2. 기본 데이터 로드 (최신 날짜) - loadDataByDate(); + } catch (e) { console.error("날짜 로드 실패:", e); } } async function loadDataByDate(selectedDate = "") { - const container = document.getElementById('projectAccordion'); - try { - const url = selectedDate ? `/project-data?date=${selectedDate}` : `/project-data?t=${new Date().getTime()}`; + await loadActivityAnalysis(selectedDate); + const url = selectedDate ? `/project-data?date=${selectedDate}` : `/project-data?t=${Date.now()}`; const response = await fetch(url); const data = await response.json(); - if (data.error) throw new Error(data.error); - rawData = data.projects || []; renderDashboard(rawData); - } catch (e) { console.error("데이터 로드 실패:", e); alert("데이터를 가져오는 데 실패했습니다."); } } +async function loadActivityAnalysis(date = "") { + const dashboard = document.getElementById('activityDashboard'); + if (!dashboard) return; + try { + const url = date ? `/project-activity?date=${date}` : `/project-activity`; + const response = await fetch(url); + const data = await response.json(); + if (data.error) return; + const { summary, details } = data; + projectActivityDetails = details; + dashboard.innerHTML = ` +
+
정상 (7일 이내)
${summary.active}
+
+
+
주의 (14일 이내)
${summary.warning}
+
+
+
방치 (14일 초과)
${summary.stale}
+
+
+
데이터 없음 (파일 0개 등)
${summary.unknown}
+
`; + } catch (e) { console.error("분석 로드 실패:", e); } +} + +// --- 렌더링 엔진 --- function renderDashboard(data) { const container = document.getElementById('projectAccordion'); - container.innerHTML = ''; // 초기화 - const groupedData = {}; - - data.forEach((item, index) => { - let continent = item[5] || "기기타"; - let country = item[6] || "미분류"; - - if (!groupedData[continent]) groupedData[continent] = {}; - if (!groupedData[continent][country]) groupedData[continent][country] = []; - - groupedData[continent][country].push({ item, index }); - }); - - const sortedContinents = Object.keys(groupedData).sort((a, b) => (continentOrder[a] || 99) - (continentOrder[b] || 99)); - - sortedContinents.forEach(continent => { - const continentGroup = document.createElement('div'); - continentGroup.className = 'continent-group'; - - let continentHtml = ` -
- ${continent} - -
-
- `; - - const sortedCountries = Object.keys(groupedData[continent]).sort((a, b) => a.localeCompare(b)); - - sortedCountries.forEach(country => { - continentHtml += ` -
-
- ${country} - -
-
-
-
-
프로젝트명
-
담당부서
-
담당자
-
파일수
-
최근로그
-
- `; - - const sortedProjects = groupedData[continent][country].sort((a, b) => a.item[0].localeCompare(b.item[0])); - - sortedProjects.forEach(({ item, index }) => { - const projectName = item[0]; - const dept = item[1]; - const admin = item[2]; - const recentLogRaw = item[3]; - const fileCount = item[4]; - - const recentLog = recentLogRaw === "X" ? "기록 없음" : recentLogRaw; - const logTime = recentLog !== "기록 없음" ? recentLog.split(',')[0] : "기록 없음"; - - let statusClass = ""; - if (fileCount === 0) statusClass = "status-error"; - else if (recentLog === "기록 없음") statusClass = "status-warning"; - - continentHtml += ` -
-
-
${projectName}
-
${dept}
-
${admin}
-
${fileCount}
-
${recentLog}
-
-
-
-
-

참여 인원 상세

- - - - - - - - -
이름소속사용자권한
${admin}${dept}관리자
김철수${dept}부관리자
박지민${dept}일반참여자
최유리${dept}참관자
-
-
-

최근 문의사항 및 파일 변경 로그

- - - - - - - -
유형내용일시
로그데이터 동기화 완료${logTime}
문의프로젝트 접근 권한 요청2026-02-23
파일설계도면 v2.pdf 업로드2026-02-22
-
-
-
-
- `; - }); - - continentHtml += ` -
-
-
- `; + container.innerHTML = ''; + const grouped = groupData(data); + Object.keys(grouped).sort((a,b) => (CONTINENT_ORDER[a]||99) - (CONTINENT_ORDER[b]||99)).forEach(continent => { + const continentDiv = document.createElement('div'); + continentDiv.className = 'continent-group active'; + let html = `
${continent}
`; + Object.keys(grouped[continent]).sort().forEach(country => { + html += `
${country}
+
프로젝트명
담당부서
담당자
파일수
최근로그
+ ${grouped[continent][country].sort((a,b)=>a[0].localeCompare(b[0])).map(p => createProjectHtml(p)).join('')}
`; }); + html += `
`; + continentDiv.innerHTML = html; + container.appendChild(continentDiv); + }); +} - continentHtml += ` +function groupData(data) { + const res = {}; + data.forEach(item => { + const c1 = item[5] || "기타", c2 = item[6] || "미분류"; + if (!res[c1]) res[c1] = {}; + if (!res[c1][c2]) res[c1][c2] = []; + res[c1][c2].push(item); + }); + return res; +} + +function createProjectHtml(p) { + const [name, dept, admin, logRaw, files] = p; + const recentLog = (!logRaw || logRaw === "X" || logRaw === "데이터 없음") ? "기록 없음" : logRaw; + const logTime = recentLog !== "기록 없음" ? recentLog.split(',')[0] : "기록 없음"; + const statusClass = (files === 0 || files === null) ? "status-error" : (recentLog === "기록 없음") ? "status-warning" : ""; + return ` +
+
+
${name}
${dept}
${admin}
${files||0}
${recentLog}
- `; - - continentGroup.innerHTML = continentHtml; - container.appendChild(continentGroup); - }); - - const allContinents = container.querySelectorAll('.continent-group'); - allContinents.forEach(continent => { - continent.classList.add('active'); - }); - - const allCountries = container.querySelectorAll('.country-group'); - allCountries.forEach(country => { - country.classList.add('active'); - }); +
+
+

참여 인원 상세

이름소속권한
${admin}${dept}관리자
+

최근 활동

유형내용일시
로그동기화 완료${logTime}
+
+
+
`; } -function toggleGroup(header) { - const group = header.parentElement; - group.classList.toggle('active'); -} - -function toggleAccordion(header) { - const item = header.parentElement; - const container = item.parentElement; - - const allItems = container.querySelectorAll('.accordion-item'); - allItems.forEach(el => { - if (el !== item) el.classList.remove('active'); - }); - +// --- 이벤트 핸들러 --- +function toggleGroup(h) { h.parentElement.classList.toggle('active'); } +function toggleAccordion(h) { + const item = h.parentElement; + item.parentElement.querySelectorAll('.accordion-item').forEach(el => { if(el!==item) el.classList.remove('active'); }); item.classList.toggle('active'); } -async function syncData() { - const btn = document.getElementById('syncBtn'); - const logConsole = document.getElementById('logConsole'); - const logBody = document.getElementById('logBody'); +function showActivityDetails(status) { + const modal = document.getElementById('activityDetailModal'), tbody = document.getElementById('modalTableBody'), title = document.getElementById('modalTitle'); + const names = { active:'정상', warning:'주의', stale:'방치', unknown:'데이터 없음' }; + const filtered = (projectActivityDetails || []).filter(d => d.status === status); + title.innerText = `${names[status]} 목록 (${filtered.length}개)`; + tbody.innerHTML = filtered.map(p => { + const o = rawData.find(r => r[0] === p.name); + return `${p.name}${o?o[1]:"-"}${o?o[2]:"-"}`; + }).join(''); + modal.style.display = 'flex'; +} - btn.classList.add('loading'); - btn.innerHTML = ` 동기화 중 (진행 상황 확인 중...)`; - btn.disabled = true; +function closeActivityModal() { document.getElementById('activityDetailModal').style.display = 'none'; } - logConsole.style.display = 'block'; - logBody.innerHTML = ''; - - function addLog(msg) { - const logItem = document.createElement('div'); - logItem.innerText = `[${new Date().toLocaleTimeString()}] ${msg}`; - logBody.appendChild(logItem); - logConsole.scrollTop = logConsole.scrollHeight; - } - - try { - console.log("Attempting to connect to /sync..."); - const response = await fetch(`/sync`); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const reader = response.body.getReader(); - const decoder = new TextDecoder(); - - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - const chunk = decoder.decode(value); - const lines = chunk.split('\n'); - - for (const line of lines) { - if (line.startsWith('data: ')) { - const payload = JSON.parse(line.substring(6)); - - if (payload.type === 'log') { - addLog(payload.message); - } else if (payload.type === 'done') { - const newData = payload.data; - newData.forEach(scrapedItem => { - const target = rawData.find(item => - item[0].replace(/\s/g, '').includes(scrapedItem.projectName.replace(/\s/g, '')) || - scrapedItem.projectName.replace(/\s/g, '').includes(item[0].replace(/\s/g, '')) - ); - - if (target) { - if (scrapedItem.recentLog !== "기존데이터유지") { - target[3] = scrapedItem.recentLog; - } - target[4] = scrapedItem.fileCount; - } - }); - - document.getElementById('projectAccordion').innerHTML = ''; - init(); - addLog(">>> 모든 동기화 작업이 완료되었습니다!"); - alert(`총 ${newData.length}개 프로젝트 동기화 완료!`); - logConsole.style.display = 'none'; - } - } - } - } - } catch (e) { - addLog(`오류 발생: ${e.message}`); - alert("서버 연결 실패. 백엔드 서버가 실행 중인지 확인하세요."); - console.error(e); - } finally { - btn.classList.remove('loading'); - btn.innerHTML = ` 데이터 동기화 (크롤링)`; - btn.disabled = false; +function scrollToProject(name) { + closeActivityModal(); + const target = Array.from(document.querySelectorAll('.repo-title')).find(t => t.innerText.trim() === name.trim())?.closest('.accordion-header'); + if (target) { + let p = target.parentElement; + while (p && p !== document.body) { if (p.classList.contains('continent-group') || p.classList.contains('country-group')) p.classList.add('active'); p = p.parentElement; } + target.parentElement.classList.add('active'); + const pos = target.getBoundingClientRect().top + window.pageYOffset - 220; + window.scrollTo({ top: pos, behavior: 'smooth' }); + target.style.backgroundColor = 'var(--primary-lv-1)'; + setTimeout(() => target.style.backgroundColor = '', 2000); } } +// --- 크롤링 및 인증 제어 --- +async function syncData() { + if (isCrawling) { + if (confirm("크롤링을 중단하시겠습니까?")) { + const res = await fetch('/stop-sync'); + if ((await res.json()).success) document.getElementById('syncBtn').innerText = "중단 요청 중..."; + } + return; + } + const modal = document.getElementById('authModal'); + if (modal) { + document.getElementById('authId').value = ''; document.getElementById('authPw').value = ''; + document.getElementById('authErrorMessage').style.display = 'none'; + modal.style.display = 'flex'; document.getElementById('authId').focus(); + } +} + +function closeAuthModal() { document.getElementById('authModal').style.display = 'none'; } + +async function submitAuth() { + const id = document.getElementById('authId').value, pw = document.getElementById('authPw').value, err = document.getElementById('authErrorMessage'); + try { + const res = await fetch('/auth/crawl', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({user_id:id, password:pw}) }); + const data = await res.json(); + if (data.success) { closeAuthModal(); startCrawlProcess(); } + else { err.innerText = "크롤링을 할 수 없습니다."; err.style.display = 'block'; } + } catch { err.innerText = "서버 연결 실패"; err.style.display = 'block'; } +} + +async function startCrawlProcess() { + isCrawling = true; + const btn = document.getElementById('syncBtn'), logC = document.getElementById('logConsole'), logB = document.getElementById('logBody'); + btn.classList.add('loading'); btn.style.backgroundColor = 'var(--error-color)'; btn.innerHTML = ` 크롤링 중단`; + logC.style.display = 'block'; logB.innerHTML = '
>>> 엔진 초기화 중...
'; + try { + const res = await fetch(`/sync`); + const reader = res.body.getReader(), decoder = new TextDecoder(); + while (true) { + const { done, value } = await reader.read(); if (done) break; + decoder.decode(value).split('\n').forEach(line => { + if (line.startsWith('data: ')) { + const p = JSON.parse(line.substring(6)); + if (p.type === 'log') { + const div = document.createElement('div'); div.innerText = `[${new Date().toLocaleTimeString()}] ${p.message}`; + logB.appendChild(div); logC.scrollTop = logC.scrollHeight; + } else if (p.type === 'done') { init(); alert(`동기화 종료`); logC.style.display = 'none'; } + } + }); + } + } catch { alert("스트림 끊김"); } + finally { isCrawling = false; btn.classList.remove('loading'); btn.style.backgroundColor = ''; btn.innerHTML = ` 데이터 동기화 (크롤링)`; } +} + document.addEventListener('DOMContentLoaded', init); diff --git a/log_debug.png b/log_debug.png deleted file mode 100644 index dcb29f1fa173a184f2e932f33d272488bf5c0378..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129162 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ⅇ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 0: + match = re.search(r'(\d{4})\.(\d{2})\.(\d{2})', log) + if match: + diff = (target_date_dt - datetime.strptime(match.group(0), "%Y.%m.%d")).days + status = "active" if diff <= 7 else "warning" if diff <= 14 else "stale" + days = diff + analysis["summary"][status] += 1 + analysis["details"].append({"name": r['short_nm'] or r['project_nm'], "status": status, "days_ago": days}) + return analysis + except Exception as e: + return {"error": str(e)} + +@app.post("/auth/crawl") +async def auth_crawl(req: AuthRequest): + """크롤링 인증""" + if req.user_id == os.getenv("PM_USER_ID") and req.password == os.getenv("PM_PASSWORD"): + return {"success": True} + return {"success": False, "message": "크롤링을 할 수 없습니다."} @app.get("/sync") async def sync_data(): - """ - 크롤링 서비스(crawler_service.py) 호출 - """ - print(">>> /sync request received") return StreamingResponse(run_crawler_service(), media_type="text_event-stream") + +@app.get("/stop-sync") +async def stop_sync(): + crawl_stop_event.set() + return {"success": True} + +@app.get("/attachments") +async def get_attachments(): + path = "sample" + if not os.path.exists(path): os.makedirs(path) + return [{"name": f, "size": f"{os.path.getsize(os.path.join(path, f))/1024:.1f} KB"} + for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))] + +@app.get("/analyze-file") +async def analyze_file(filename: str): + return await run_in_threadpool(analyze_file_content, filename) + +@app.get("/sample.png") +async def get_sample_img(): + return FileResponse("sample.png") diff --git a/server_reboot.log b/server_reboot.log deleted file mode 100644 index 8a8f47f37d79037894171bdfbadc448c66c2c371..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93476 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 diff --git a/server_revert.log b/server_revert.log deleted file mode 100644 index 955460613ea1c1de81d78692f80967c899188afa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47692 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 diff --git a/sheet.csv b/sheet.csv deleted file mode 100644 index 775627a..0000000 --- a/sheet.csv +++ /dev/null @@ -1,42 +0,0 @@ -[PM Overseas 프로젝트 현황],,2026.03.04,,,,,,<<활동로그가 없는 프로젝트 (8),, -,,,,,,,,,, -No.,프로젝트 명,담당부서,담당자,종료(예정)일,최근 활동로그,과업개요 작성 유무,파일 수,비고,, -1,라오스 ITTC 관개 교육센터 PMC,수자원1부,방노성,2025.12.20,"2026.01.29, 폴더 삭제",O,16,2026.01.29 로그는 테스트 활동 추정,종료(예정)일 지남,진행 -2,라오스 비엔티안 메콩강 관리 2차 DD,수자원1부,방노성,2026.05.31,"2025.12.07, 파일업로드",X,260,탭 1개에 모든파일 업로드,, -3,미얀마 만달레이 철도 개량 감리 CS,철도사업부,김태헌,2027.11.17,"2025.11.17, 폴더이름변경",O,298,,, -4,베트남 푸옥호아 양수 발전 FS,수력부,이철호,2025.11.30,"2026.02.23, 폴더이름변경",O,139,준공도서 3월 작성예정,종료(예정)일 지남,준공 -5,사우디아라비아 아시르 지잔 고속도로 FS,도로부,공태원,2025.11.21,"2026.02.09, 파일다운로드",O,73,,종료(예정)일 지남,준공 -6,우즈베키스탄 지방 도로 복원 MP,도로부,장진영,2029.04.28,X,X,0,,, -7,우즈베키스탄 타슈켄트 철도 FS,철도사업부,김태헌,2026.03.20,"2026.02.05, 파일업로드",O,51,,, -8,이라크 Habbaniyah Shuaiba AirBase PD,도로부,강동구,2026.12.31,X,X,0,,, -9,메콩유역 수자원 관리 기후적응 MP,수자원1부,정귀한,2025.12.31,X,X,0,,종료(예정)일 지남,준공 -10,캄보디아 반테 민체이 관개 홍수저감 MP,수자원1부,이대주,2026.08.28,"2025.12.07, 파일업로드",X,44,,, -11,캄보디아 시엠립 하수처리 개선 DD,물환경사업1부,변역근,2028.12.18,"2026.02.06, AI 요약",O,221,,, -12,키르기스스탄 잘랄아바드 상수도 계획 MP,물환경사업1부,변기상,2025.12.31,"2026.02.12, 파일업로드",X,60,,종료(예정)일 지남,준공 -13,파키스탄 펀잡 홍수 방재 PMC,수자원1부,방노성,2027.12.31,"2025.12.08, 폴더삭제",O,0,,, -14,파키스탄 KP 아보타바드 상수도 PMC,물환경사업2부,변기상,2026.12.31,"2026.02.26, 파일업로드",O,240,,, -15,파키스탄 CAREC 도로 감리 DD,도로부,황효섭,2026.10.26,X,X,0,,, -16,필리핀 홍수 복원 InFRA2 DD,수자원1부,이대주,2026.08.07,"2025.12.01, 폴더삭제",O,6,최근로그 >> 폴더자동삭제(파일 개수 미달),, -17,필리핀 홍수 관리 Package5B MP,수자원1부,이희철,2026.05.31,"2025.12.02, 폴더이름변경",O,14,,, -18,필리핀 PGN 해상교량 BID2 IDC,구조부,이상희,2026.05.31,"2026.02.11, 파일다운로드",O,631,,, -19,가나 테치만 상수도 확장 DS,물환경사업2부,-,2029.04.25,X,X,0,책임자 및 담당자 설정X,, -20,기니 벼 재배단지 PMC,수자원1부,이대주,2028.12.20,"2025.12.08, 파일업로드",O,43,최근로그 >> 폴더자동삭제(파일 개수 미달),, -21,우간다 벼 재배단지 PMC,수자원1부,방노성,2028.12.20,"2025.12.08, 파일업로드",O,52,,, -22,우간다 부수쿠마 분뇨 자원화 2단계 PMC,물환경사업2부,변기상,2026.12.31,"2026.02.05, 파일업로드",X,9,,, -23,에티오피아 지하수 관개 환경설계 DD,물환경사업2부,변기상,2026.06.23,X,X,0,,, -24,에티오피아 도도타군 관개 PMC,수자원1부,방노성,2026.12.31,"2025.12.01, 폴더이름변경",O,144,탭 1개에 모든파일 업로드 // 최근로그 >> 폴더자동삭제(파일 개수 미달),, -25,에티오피아 Adeaa-Becho 지하수 관개 MP,수자원1부,방노성,2026.07.31,"2025.11.21, 파일업로드",O,146,최근로그 >> 폴더자동삭제(파일 개수 미달),, -26,탄자니아 Iringa 상하수도 개선 CS,물환경사업1부,백운영,2029.06.08,"2026.02.03, 폴더생성",X,0,,, -27,탄자니아 Dodoma 하수 설계감리 DD,물환경사업2부,변기상,2027.07.08,"2026.02.04, 폴더삭제",X,32,,, -28,탄자니아 잔지바르 쌀 생산 PMC,수자원1부,방노성,2027.12.20,"2025.12.08, 파일 업로드",O,23,,, -29,탄자니아 도도마 유수율 상수도개선 PMC,물환경사업1부,박순석,2026.12.31,"2026.02.12, 부관리자권한추가",X,35,,, -30,아르헨티나 SALDEORO 수력발전 28MW DD,플랜트1부,양정모,2026.01.31,X,X,0,,종료(예정)일 지남,준공 -31,온두라스 LaPaz Danli 상수도 CS,물환경사업2부,-,2027.02.23,"2026.01.29, 파일 삭제",O,60,"책임자 및 담당자 설정 X, 실 관리부서는 해외사업부, 더미파일 다수",, -32,볼리비아 에스꼬마 차라짜니 도로 CS,도로부,전홍찬,2029.12.15,"2026.02.06, 파일업로드",X,1,,, -33,볼리비아 마모레 교량도로 FS,도로부,황효섭,2025.10.17,"2026.02.06, 파일업로드",X,120,,종료(예정)일 지남,준공 -34,볼리비아 Bombeo-Colomi 도로설계 DD,도로부,황효섭,2026.07.24,"2025.12.05, 파일삭제",O,48,"더미파일(폴더유지용) 12개, 실 관리부서는 해외사업부",, -35,콜롬비아 AI 폐기물 FS,플랜트1부,서재희,2026.02.27,X,X,0,,종료(예정)일 지남, -36,파라과이 도로 통행료 현대화 MP,교통계획부,오제훈,2025.10.24,"2025.02.25, 폴더삭제",X,0,,종료(예정)일 지남,준공 -37,페루 Barranca 상하수도 확장 DD,물환경사업2부,변기상,2026.03.08,"2025.11.14, 파일업로드",O,44,"더미파일(폴더유지용) 27개, 실 관리부서는 해외사업부",, -38,엘살바도르 태평양 철도 FS,철도사업부,김태헌,2025.12.31,"2026.02.24, 폴더자동삭제",X,101,,종료(예정)일 지남,준공 -39,필리핀 사무소,해외사업부,한형남,,"2026.03.04, 파일다운로드",과업개요 페이지 없음,817,,, \ No newline at end of file diff --git a/style/dashboard.css b/style/dashboard.css index 611bbef..38ce943 100644 --- a/style/dashboard.css +++ b/style/dashboard.css @@ -1,91 +1,72 @@ +:root { + --topbar-h: 36px; + --header-h: 56px; + --activity-h: 110px; + --fixed-total-h: calc(var(--topbar-h) + var(--header-h) + var(--activity-h)); + + --primary-color: #1E5149; + --primary-lv-0: #f0f7f4; + --primary-lv-1: #e1eee9; + --border-color: #e5e7eb; + --bg-muted: #F9FAFB; + --text-main: #111827; + --text-sub: #6B7280; + --error-color: #F21D0D; +} + /* Portal (Index) */ .portal-container { display: flex; flex-direction: column; align-items: center; justify-content: center; - height: calc(100vh - 36px); + height: calc(100vh - var(--topbar-h)); background: var(--bg-muted); - padding: var(--space-lg); - margin-top: 36px; + padding: 32px; + margin-top: var(--topbar-h); } -.portal-header { - text-align: center; - margin-bottom: 50px; -} +.portal-header { text-align: center; margin-bottom: 50px; } +.portal-header h1 { font-size: 28px; color: var(--primary-color); margin-bottom: 10px; font-weight: 800; } +.portal-header p { color: var(--text-sub); font-size: 15px; } +.button-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 30px; width: 100%; max-width: 800px; } +.portal-card { background: #fff; border: 1px solid var(--border-color); border-radius: 12px; padding: 40px; text-align: center; transition: all 0.3s ease; width: 100%; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); display: flex; flex-direction: column; align-items: center; gap: 20px; cursor: pointer; text-decoration: none; } +.portal-card:hover { transform: translateY(-8px); border-color: var(--primary-color); box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); } +.portal-card i { font-size: 48px; color: var(--primary-color); } +.portal-card h3 { font-size: 20px; color: var(--text-main); margin: 0; } +.portal-card p { font-size: 14px; color: var(--text-sub); margin: 0; } -.portal-header h1 { - font-size: 28px; - color: var(--primary-color); - margin-bottom: 10px; -} - -.portal-header p { - color: var(--text-sub); - font-size: 15px; -} - -.button-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 30px; - width: 100%; - max-width: 800px; -} - -.portal-card { - background: #fff; - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 40px; - text-align: center; - transition: 0.3s; - width: 100%; - box-shadow: var(--box-shadow); - display: flex; - flex-direction: column; - align-items: center; - gap: 20px; -} - -.portal-card:hover { - transform: translateY(-5px); - border-color: var(--primary-color); - box-shadow: var(--box-shadow-lg); -} - -/* Dashboard List & Console */ +/* Dashboard Layout */ header { - position: fixed; - top: 36px; - left: 0; - right: 0; - z-index: 1000; - background: #fff; - display: flex; - justify-content: space-between; - align-items: center; - padding: var(--space-md) var(--space-lg); - border-bottom: 1px solid var(--border-color); - box-shadow: 0 2px 4px rgba(0,0,0,0.05); + position: fixed; top: var(--topbar-h); left: 0; right: 0; z-index: 1001; + background: #fff; height: var(--header-h); display: flex; justify-content: space-between; align-items: center; padding: 0 32px; border-bottom: 1px solid #f5f5f5; } -.main-content { - margin-top: 100px; - padding: var(--space-lg); - max-width: 1400px; - margin-left: auto; - margin-right: auto; +.activity-dashboard-wrapper { + position: fixed; top: calc(var(--topbar-h) + var(--header-h)); left: 0; right: 0; z-index: 1000; + background: #fff; height: var(--activity-h); border-bottom: 1px solid var(--border-color); box-shadow: 0 4px 6px rgba(0,0,0,0.03); } +.activity-dashboard { max-width: 1200px; margin: 0 auto; height: 100%; display: flex; gap: 15px; padding: 10px 32px 20px 32px; } +.activity-card { flex: 1; padding: 12px 15px; border-radius: 8px; cursor: pointer; transition: all 0.2s ease; display: flex; flex-direction: column; justify-content: center; gap: 2px; border-left: 5px solid transparent; } +.activity-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.08); } +.activity-card.active { background: #e8f5e9; border-left-color: #4DB251; } +.activity-card.warning { background: #fff8e1; border-left-color: #FFBF00; } +.activity-card.stale { background: #ffebee; border-left-color: #F21D0D; } +.activity-card.unknown { background: #f5f5f5; border-left-color: #9e9e9e; } +.activity-card .label { font-size: 11px; font-weight: 600; opacity: 0.7; } +.activity-card .count { font-size: 20px; font-weight: 800; } + +.main-content { margin-top: var(--fixed-total-h); padding: 32px; max-width: 1400px; margin-left: auto; margin-right: auto; } + +/* 로그 콘솔 - 초기 디자인 복구 (Sticky Terminal 스타일) */ .log-console { position: sticky; - top: 100px; + top: var(--fixed-total-h); z-index: 999; background: #000; color: #0f0; - font-family: monospace; + font-family: 'Consolas', 'Monaco', monospace; padding: 15px; margin-bottom: 20px; border-radius: 4px; @@ -104,212 +85,72 @@ header { font-weight: bold; } -.accordion-container { - border-top: 1px solid var(--border-color); +/* 모달 정중앙 배치 */ +.activity-modal-overlay { + position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; + background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(4px); z-index: 3000; + display: flex; align-items: center; justify-content: center; padding: 20px; } -.accordion-list-header, -.accordion-header { - display: grid; - grid-template-columns: 2.5fr 1fr 1fr 0.8fr 2fr; - gap: var(--space-md); - padding: var(--space-md) var(--space-lg); - align-items: center; - cursor: pointer; +.activity-modal-content { + background: #fff; width: 600px; max-height: 85vh; border-radius: 12px; + display: flex; flex-direction: column; overflow: hidden; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.5); } -.accordion-list-header { - position: sticky; - top: 100px; - background: var(--bg-muted); - z-index: 10; - font-size: 11px; - font-weight: 700; - color: var(--text-sub); - border-bottom: 1px solid var(--text-main); - cursor: default; +.auth-modal-content { + background: #fff; width: 400px; border-radius: 16px; padding: 40px; + text-align: center; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.5); + display: flex; flex-direction: column; gap: 25px; } -.accordion-item { - border-bottom: 1px solid var(--border-color); -} +.modal-header { padding: 20px; border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center; } +.modal-header h3 { margin: 0; font-size: 16px; color: var(--primary-color); } +.close-btn { background: none; border: none; font-size: 24px; cursor: pointer; color: var(--text-sub); } +.modal-body { padding: 20px; overflow-y: auto; } +.modal-row { cursor: pointer; border-bottom: 1px solid #f5f5f5; } +.modal-row:hover { background: var(--primary-lv-0); } -.accordion-item:hover { - background: var(--primary-lv-0); -} +/* 인증 모달 내부 요소 */ +.auth-header i { font-size: 40px; color: var(--primary-color); margin-bottom: 15px; } +.auth-header h3 { font-size: 22px; color: var(--text-main); margin: 0; font-weight: 800; } +.auth-header p { font-size: 14px; color: var(--text-sub); margin-top: 8px; } +.auth-body { display: flex; flex-direction: column; gap: 15px; text-align: left; } +.input-group { display: flex; flex-direction: column; gap: 6px; } +.input-group label { font-size: 12px; font-weight: 700; color: var(--text-sub); } +.input-group input { padding: 12px 16px; border: 1px solid var(--border-color); border-radius: 8px; font-size: 14px; transition: 0.2s; } +.input-group input:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 3px var(--primary-lv-1); } +.error-text { color: var(--error-color); font-size: 13px; font-weight: 600; margin-top: 10px; text-align: center; } +.auth-footer { display: flex; gap: 10px; margin-top: 10px; } +.auth-footer button { flex: 1; padding: 12px; border-radius: 8px; font-weight: 700; cursor: pointer; transition: 0.2s; border: none; } +.cancel-btn { background: #f3f4f6; color: var(--text-sub); } +.login-btn { background: var(--primary-color); color: #fff; } -.repo-title { - font-weight: 700; - color: var(--primary-color); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} +/* Accordion Layout */ +.accordion-list-header { position: sticky; top: var(--fixed-total-h); background: #fff; z-index: 900; font-size: 11px; font-weight: 700; color: var(--text-sub); padding: 14px 24px; border-bottom: 2px solid var(--primary-color); box-shadow: 0 4px 10px rgba(0,0,0,0.05); display: grid; grid-template-columns: 2.5fr 1fr 1fr 0.8fr 2fr; gap: 16px; align-items: center; } +.accordion-header { display: grid; grid-template-columns: 2.5fr 1fr 1fr 0.8fr 2fr; gap: 16px; padding: 16px 24px; align-items: center; cursor: pointer; border-bottom: 1px solid var(--border-color); transition: background 0.1s; } +.accordion-item:hover .accordion-header { background: var(--primary-lv-0); } +.repo-title { font-weight: 700; color: var(--primary-color); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.repo-dept, .repo-admin { font-size: 12px; color: var(--text-main); } +.repo-files { text-align: center; font-weight: 600; } +.repo-log { font-size: 11px; color: var(--text-sub); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.accordion-body { display: none; padding: 24px; background: var(--bg-muted); border-bottom: 1px solid var(--border-color); } +.accordion-item.active .accordion-body { display: block; } +.status-warning { background: #fffcf0; } +.status-error { background: #fff5f4; } +.warning-text { color: var(--error-color) !important; font-weight: 700; } -.repo-files { - text-align: center; - font-weight: 600; -} +.continent-group, .country-group { margin-bottom: 15px; } +.continent-header, .country-header { background: #fff; padding: 14px 20px; border: 1px solid var(--border-color); border-radius: 8px; display: flex; justify-content: space-between; align-items: center; cursor: pointer; font-weight: 700; } +.continent-header { background: var(--primary-color); color: white; border: none; font-size: 15px; } +.country-header { font-size: 14px; color: var(--text-main); margin-top: 8px; } +.continent-body, .country-body { display: none; padding: 10px 0 10px 15px; } +.active>.continent-body, .active>.country-body { display: block; } -.repo-log { - font-size: 11px; - color: var(--text-sub); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.accordion-body { - display: none; - padding: var(--space-lg); - background: var(--bg-muted); - border-top: 1px solid var(--border-color); -} - -.accordion-item.active .accordion-body { - display: block; -} - -.status-warning { - background: #fff9e6; -} - -.status-error { - background: #fee9e7; -} - -.warning-text { - color: #f21d0d !important; - font-weight: 700; -} - -/* Multi-level Groups */ -.continent-group, -.country-group { - margin-bottom: 10px; -} - -.continent-header, -.country-header { - background: #fff; - padding: 12px 20px; - border: 1px solid var(--border-color); - border-radius: 8px; - display: flex; - justify-content: space-between; - align-items: center; - cursor: pointer; - font-weight: 700; - transition: all 0.2s; -} - -.continent-header { - background: var(--primary-color); - color: white; - border: none; - font-size: 15px; -} - -.country-header { - font-size: 14px; - color: var(--text-main); - margin-top: 5px; -} - -.continent-body, -.country-body { - display: none; - padding: 10px 0 10px 20px; -} - -.active>.continent-body, -.active>.country-body { - display: block; -} - -/* Detail Views */ -.detail-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20px; -} - -.detail-section h4 { - font-size: 13px; - margin-bottom: 10px; - color: var(--text-main); - border-left: 3px solid var(--primary-color); - padding-left: 8px; -} - -.data-table { - width: 100%; - border-collapse: collapse; - font-size: 12px; -} - -.data-table th, -.data-table td { - padding: 8px; - border-bottom: 1px solid var(--border-color); - text-align: left; -} - -.data-table th { - color: var(--text-sub); - font-weight: 600; -} - -/* Sync Button & Admin Info */ -.sync-btn { - display: flex; - align-items: center; - gap: var(--space-sm); - background-color: var(--primary-color); - color: #fff; - padding: 8px 16px; - border-radius: var(--radius-lg); - font-size: 13px; - font-weight: 600; - cursor: pointer; - border: none; - box-shadow: var(--box-shadow); -} - -.sync-btn:hover { - background-color: var(--primary-lv-8); -} - -.sync-btn.loading .spinner { - display: inline-block; -} - -.admin-info { - font-size: 13px; - color: var(--text-sub); - margin-left: var(--space-md); - padding: 6px 12px; - background: var(--bg-muted); - border-radius: var(--radius-sm); - border: 1px solid var(--border-color); -} - -.admin-info strong { - color: var(--primary-color); - font-weight: 700; -} - -.base-date-info { - font-size: 13px; - color: var(--text-sub); - background: #f8f9fa; - padding: 6px 15px; - border-radius: 6px; - border: 1px solid var(--border-color); -} - -.base-date-info strong { - color: #333; - font-weight: 700; - margin-left: 5px; -} +.data-table { width: 100%; border-collapse: collapse; font-size: 12px; } +.data-table th, .data-table td { padding: 10px 8px; border-bottom: 1px solid var(--border-color); text-align: left; } +.data-table th { color: var(--text-sub); font-weight: 600; background: #fcfcfc; } +.sync-btn { display: flex; align-items: center; gap: 8px; background-color: var(--primary-color); color: #fff; padding: 8px 16px; border-radius: 8px; font-size: 13px; font-weight: 600; cursor: pointer; border: none; transition: 0.2s; } +.admin-info { font-size: 12px; color: var(--text-sub); margin-left: 16px; padding: 6px 12px; background: #f8f9fa; border-radius: 4px; border: 1px solid var(--border-color); } +.admin-info strong { color: var(--primary-color); font-weight: 700; } +.base-date-info { font-size: 13px; color: var(--text-sub); background: #fdfdfd; padding: 6px 15px; border-radius: 6px; border: 1px solid var(--border-color); } +.base-date-info strong { color: #333; font-weight: 700; } diff --git a/templates/dashboard.html b/templates/dashboard.html index debd3a6..96fca23 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -42,7 +42,14 @@

- + +
+
+ +
+
+ + + + + + + - \ No newline at end of file + diff --git a/test_main_filtered.py b/test_main_filtered.py deleted file mode 100644 index ceed5ba..0000000 --- a/test_main_filtered.py +++ /dev/null @@ -1,33 +0,0 @@ -import asyncio, os, json, queue, threading -from crawler_service import crawler_thread_worker -from dotenv import load_dotenv - -load_dotenv() - -async def run_main_test(): - user_id = os.getenv("PM_USER_ID") - password = os.getenv("PM_PASSWORD") - msg_queue = queue.Queue() - - thread = threading.Thread(target=crawler_thread_worker, args=(msg_queue, user_id, password)) - thread.start() - - print(">>> 메인 워커 실행 중 (필리핀 사무소)...") - try: - while True: - msg_raw = await asyncio.to_thread(msg_queue.get, timeout=300) - if msg_raw is None: break - - msg = json.loads(msg_raw) - if msg["type"] == "log": - print(f"[LOG] {msg['message']}") - elif msg["type"] == "done": - print(f"\n[DONE] 최종 결과: {msg['data']}") - break - except queue.Empty: - print(">>> 타임아웃") - finally: - thread.join() - -if __name__ == "__main__": - asyncio.run(run_main_test())