
    jK%              	       v   d Z ddlZddlZddlZddlZddlmZ ej                  j                  dd      at        sej                  j                  ej                  j                  ej                  j                  ej                  j                  e            d            rr eej                  j                  ej                  j                  ej                  j                  e            d      d      j!                         j#                         add	lmZmZ ddlmZ  ej*                  ej                  j                  ej                  j                  e                    eej                  j                  d
d            Zej                  j                   ej0                         d      Zej                  j                   ej0                         d      Z ej6                         Zd Zd Zd Zd Z d Z! G d de      Z"e#dk(  r, e$de        de_%         edefe"      jM                          yy)u  
家庭练习 · 后端自动回传服务（纯标准库，零依赖）
- 静态托管各练习页 / DOCX
- POST /api/submit  单题结果上报（page 隔离；对/错）
- POST /api/submit  action=report 题目报错（答案/题面问题）
- GET  /api/results 全量结果（按卷分组）
- GET  /api/reports 报错清单
- GET  /api/history 按日汇总做题记录（{date:{page:{count,correct,items}}}）
存储：results.json = {page: {no: rec}}；reports.json = [ {...} ]
    N)datetime
AUTH_TOKEN z	token.txtutf-8encoding)ThreadingHTTPServerSimpleHTTPRequestHandlerPORTi  zresults.jsonzreports.jsonc                 $   | dz   }t        |dd      5 }t        j                  ||dd       |j                          t	        j
                  |j                                ddd       	 t        j                  j                  | d	z         rt        j                  | d	z   | d
z          t        j                  j                  |       rt        j                  | | d	z          t	        j                  ||        y# 1 sw Y   xY w# t        $ r Y .w xY w)u   先写临时文件再原子替换，写前保留上一版 .bak。
    避免『写到一半进程被杀 → 文件损坏 → load 静默返回空 → 整卷归零』。z.tmpwr   r   F   )ensure_asciiindentN.bak.bak2)openjsondumpflushosfsyncfilenopathexistsshutilcopyfile	Exceptionreplace)r   objtmpfs       /home/ubuntu/practice/app.pyatomic_writer$      s     -C	c3	) Q		#quQ7		
77>>$-(OOD6M4'>:77>>$OOD$-0 JJsD   s   AC7*A6D 7D 	DDc                 t    t        | t              sy | r$t        d | j                         D              rd| iS | S )Nc              3   J   K   | ]  }t        |t               xs d |v   yw)stateN)
isinstancedict).0vs     r#   	<genexpr>z_parse.<locals>.<genexpr>5   s&     OqZ4((:W\:Os   !#legacy)r(   r)   allvalues)ds    r#   _parser1   1   s4    aSOAHHJOO!}H    c            	          t         t         dz   t         dz   fD ]2  } 	 t        t        j                  t	        | d                  }||c S 4 i S # t
        $ r Y Bw xY w)uM   主文件损坏时回退 .bak，而不是静默返回空导致全卷丢失。r   r   r   r   )DATAr1   r   loadr   r   )pr0   s     r#   load_resultsr7   :   si    D6M4'>2 	tyya'!:;<A}  I  		s   ,A	AAc                  n    	 t        j                  t        t        d            S # t        $ r g cY S w xY w)Nr   r   )r   r5   r   RPTr    r2   r#   load_reportsr;   F   s1    yycG455 	s   #& 44c                    i }| j                         D ]A  \  }}|j                         D ]'  \  }}|j                  d      }	 t        j                  t	        |      dz        j                  d      }t        j                  t	        |      dz        j                  d      }|j                  |i       }	|	j                  |ddg d      }
|
dxx   dz  cc<   |
dxx   |j                  d      dk(  rdndz  cc<   |
d   j                  ||j                  d      |j                  d      |j                  d      |j                  d      |d       * D |j                         D ]-  }	|	j                         D ]  }
|
d   j                  d         / t        t        |j                         d            S # t        $ r4 |j                  d      xs ddd }|j                  d      xs dd	d
 }Y Uw xY w)ug   按日期汇总做题记录：{date: {page: {count, correct, items:[{no,subject,kp,pick,state,hhmm}]}}}tsi  z%Y-%m-%dz%H:%Mrecv_at?N
         r   )countcorrectitemsrC   r   rD   r'      对rE   subjectkppick)norG   rH   rI   r'   timec                     t        | j                  d      xs d      t        | d         j                         rt        | d         fS dfS )NrG   r   rJ   r   )strgetisdigitint)xs    r#   <lambda>zbuild_history.<locals>.<lambda>b   sF    3quuY/?/E2+FX[\]^b\cXdXlXlXnAdG*v tu*v r2   )keyT)reverse)rE   rN   r   fromtimestamprP   strftimer   
setdefaultappendr/   sortr)   sorted)reshistpageqsrJ   rr=   dayhhmmr0   r6   s              r#   build_historyrb   M   s   DIIK ^bXXZ 	^EBtB8,,SWt^<EEjQ--c"gn=FFwO R(ATQ1r#JKAgJ!OJiLw5!8Aa?LgJRAEE)4DAEERVK'(uuV}quuW~W[] ^	^^  [[] x 	xAgJOO vOw	xx tzz|T233  8uuY'.34i(/CB78s   A F<<9G98G9c                   L     e Zd ZdZ fdZd Zd	dZd Z fdZd Z	d Z
 xZS )
HandlerzHTTP/1.1c                     | j                   j                  d      d   j                         }|dk(  s|j                  d      r| j	                  dd       t
        |           y )Nr?   r   /z.htmlCache-Controlz#no-cache, no-store, must-revalidate)r   splitlowerendswithsend_headersuperend_headers)selfr6   	__class__s     r#   rm   zHandler.end_headersh   sQ    IIOOC #))+8qzz'*_.STr2   c                 V    t         sy| j                  j                  dd      t         k(  S )NTzX-Auth-Tokenr   )r   headersrN   rn   s    r#   _auth_okzHandler._auth_oko   s&     ||3zAAr2   c                    t        j                  |d      j                  d      }| j                  |       | j	                  dd       | j	                  dt        t        |                   | j	                  dd       | j	                  d	d
       | j	                  dd       | j	                  dd       | j	                  dd       | j                          | j                  j                  |       y )NF)r   r   zContent-Typezapplication/json; charset=utf-8Content-Lengthrg   zno-storezAccess-Control-Allow-Origin*zAccess-Control-Allow-HeaderszContent-Type, X-Auth-TokenzAccess-Control-Allow-MethodszGET, POST, OPTIONSzAccess-Control-Max-Age86400)
r   dumpsencodesend_responserk   rM   lenrm   wfilewrite)rn   r    codebs       r#   _jsonzHandler._jsonv   s    JJs/66w?4 )JK)3s1v;7*5 	6<79UV79MN17;

r2   c                 *    | j                  ddi       y )NokT)r   rr   s    r#   
do_OPTIONSzHandler.do_OPTIONS   s    

D$< r2   c                 x   | j                   j                  d      r| j                         s| j                  dddd      S | j                   dk(  r)t        5  | j                  t                      d d d        y | j                   dk(  r)t        5  | j                  t                      d d d        y | j                   dk(  r2t        5  | j                  t        t                            d d d        y | j                  dd	dd
       y t        | %          y # 1 sw Y   y xY w# 1 sw Y   y xY w# 1 sw Y   y xY w)Nz/api/Funauthorizedr   err  z/api/resultsz/api/reportsz/api/history	not found  )
r   
startswithrs   r   LOCKr7   r;   rb   rl   do_GET)rn   ro   s    r#   r   zHandler.do_GET   s    99(==?zz~"FLLyyN* /JJ|~./ /n, /JJ|~./ /n, >JJ}\^<=> > 

%<cBGN/ // /> >s$   DD$#D0D!$D-0D9c           
      p   | j                   dk7  r| j                  dddd      S | j                         s| j                  dddd      S 	 t        | j                  j                  dd	            }t        j                  | j                  j                  |            }t        |j                  d      xs d      }t        5  |j                  d      }|dk(  rt               }|j                  ||j                  d      t        |j                  d      xs d      d d |j                  d      t        j                          j#                  d      d       t%        t&        |       n|dk(  r|j                  d      xs g }t)               }|j+                  |i       }d	}|D ]  }	t-        |	t.              s|	j                  d      |	j                  d      dvr9dD 
ci c]  }
|
|	v s|
|	|
    }}
t        j                          j#                  d      |d<   ||t        |	d         <   |dz  } t%        t0        |       | j                  d|d       cd d d        S |d!k(  r<t)               }i ||<   t%        t0        |       | j                  d|d"      cd d d        S t)               }|j+                  |i       }|j                  d      rd|j                  d      dv rQdD 
ci c]  }
|
|v s|
||
    }}
t        j                          j#                  d      |d<   ||t        |d         <   n| j                  dd#dd      cd d d        S t%        t0        |       d d d        | j                  d$di       y # t        $ r | j                  dd
dd      cY S w xY wc c}
w c c}
w # 1 sw Y   KxY w)%Nz/api/submitFr   r   r   r   r   ru   r   zbad bodyi  r]   r-   actionreportrJ   noter      r=   seconds)timespec)r]   rJ   r   r=   r>   syncrE   r'   )rF   u   错)rG   rH   diffrI   rS   r'   r=   r>   r   T)r   syncedclear)r   clearedinvalidr   )r   r   rs   rP   rq   rN   r   loadsrfilereadr   rM   r   r;   rX   r   now	isoformatr$   r9   r7   rW   r(   r)   r4   )rn   npayloadr]   actrptrE   r0   pgitkrecs               r#   do_POSTzHandler.do_POST   sl   99%::U;?EE}}::U>BCHH	EDLL$$%5q9:Ajj!34G 7;;v&2(3 +	&++h'Ch"n

 D(9F 3 9r:4C@!++d+'||~777K	  S#&G,2 N\\$+ B%b$/ vvd|+rvvgn/T -dphimoho1be8pCp%-\\^%=%=y%=%QC	N(+Bs2d8}%FA T1%zz";<7+	& +	&8  N$T1%zz$"?@C+	& +	&F !N\\$+;;t$W)=)O2izQmnrymy1gaj=zCz%-\\^%=%=y%=%QC	N-0Bs74=)*::U9&EsKU+	& +	&V T1%W+	&X 	

D$< _  	E::U:>DD	E2 q  {M+	& +	&s_   AM> D N,"	N",N"4A$N,"7N,#AN,'	N'1N'9AN,N,>NN"
N,,N5c                      y )Nr:   )rn   as     r#   log_messagezHandler.log_message   s    r2   )r   )__name__
__module____qualname__protocol_versionrm   rs   r   r   r   r   r   __classcell__)ro   s   @r#   rd   rd   f   s.    !B!$7!rr2   rd   __main__zserving on 0.0.0.0:   z0.0.0.0)'__doc__r   r   r   	threadingr   environrN   r   r   r   joindirnameabspath__file__r   r   striphttp.serverr	   r
   chdirrP   r   getcwdr4   r9   Lockr   r$   r1   r7   r;   rb   rd   r   printrequest_queue_sizeserve_foreverr:   r2   r#   <module>r      s  	 # "  ZZ^^L"-
bggnnRWW\\"''//"''//RZB[2\^i%jkbggll277??277??83L#M{[fmnssu{{}J E  2	3 4
2::>>&$'(	ww||IBIIK0ggll9299;/y~~(	42n& nb z	v
&'-0*D)73AAC r2   