o
    Wi#                     @   s  d dl mZmZmZ d dlmZmZ d dlmZ d dl	m
Z
 d dlmZmZ d dlZd dlZd dlZd dlZd dlmZmZ e Zeje
dgd	dgdgd
 ejdedddd ejdedddd ejejejedZejejejedZG dd deZG dd deZ G dd deZ!G dd deZ"G dd deZ#dee! fdd Z$d!ee! fd"d#Z%de&fd$d%Z'dee# fd&d'Z(d(ee# fd)d*Z)e*d+d,d- Z+ej*d.ed/d0efd1d2Z,ej*d3ee! d4d5d6 Z-ej.d7e!d8d9d:e fd;d<Z/ej0d=e!d4d>e&d?e"fd@dAZ1ej2d=dBdCd>e&fdDdEZ3ej*dFee# d4dGdH Z4ej.dFe#d8d9dIe#fdJdKZ5dS )L    )FastAPIRequestHTTPException)HTMLResponseFileResponse)StaticFiles)CORSMiddleware)ListOptionalN)	BaseModel	validator*T)allow_originsallow_credentialsallow_methodsallow_headersz/staticstatic)	directory)namez/assetsztemplates/assetsassetszquiz_questions.jsonzquiz_scoreboard.jsonc                   @   sJ   e Zd ZU eed< ee ed< eed< eddd Zeddd ZdS )	QuizQuestionBasequestionoptionsanswerc                 C   s,   dt |  krdkstd td|S N      z)Number of options must be between 2 and 8len
ValueErrorclsv r#   %/var/www/OAT-AI-Tools/oat-quiz/app.pycheck_options_count%   s
   z$QuizQuestionBase.check_options_countc                 C   s    d|v r||d vrt d|S )Nr   z*Answer must be one of the provided optionsr   r!   r"   valuesr#   r#   r$   check_answer_in_options+   s   z(QuizQuestionBase.check_answer_in_optionsN)	__name__
__module____qualname__str__annotations__r	   r   r%   r)   r#   r#   r#   r$   r       s   
 
r   c                   @   s   e Zd ZdS )QuizQuestionCreateN)r*   r+   r,   r#   r#   r#   r$   r/   1   s    r/   c                   @   s   e Zd ZU eed< dS )QuizQuestionInDBidN)r*   r+   r,   r-   r.   r#   r#   r#   r$   r0   4   s   
 r0   c                   @   sb   e Zd ZU dZee ed< dZeee  ed< dZ	ee ed< e
ddd Ze
ddd ZdS )	QuizQuestionUpdateNr   r   r   c                 C   s4   |d urdt |  krdkstd td|S r   r   r    r#   r#   r$   check_options_count_optional<   s
   z/QuizQuestionUpdate.check_options_count_optionalc                 C   s4   |d urd|v r|d d ur||d vrt d|S )Nr   zLAnswer must be one of the provided options if options are also being updatedr&   r'   r#   r#   r$    check_answer_in_options_optionalB   s   (z3QuizQuestionUpdate.check_answer_in_options_optional)r*   r+   r,   r   r
   r-   r.   r   r	   r   r   r3   r4   r#   r#   r#   r$   r2   7   s   
 
r2   c                   @   sL   e Zd ZU eed< eed< eddd Zeddd ZG dd dZ	d	S )
ScoreboardEntryinitialstimec                 C   s>   |   }t|dkrtdtdd |D std|S )N   z%Initials must be exactly 3 charactersc                 s   s    | ]}|  V  qd S N)isalnum).0cr#   r#   r$   	<genexpr>S   s    z4ScoreboardEntry.validate_initials.<locals>.<genexpr>z.Initials must contain only letters and numbers)stripupperr   r   allr    r#   r#   r$   validate_initialsM   s   z!ScoreboardEntry.validate_initialsc                 C   s   |dk rt d|S )Nr   zTime cannot be negativer&   r    r#   r#   r$   validate_timeW   s   zScoreboardEntry.validate_timec                   @   s   e Zd ZddddiZdS )zScoreboardEntry.ConfigexampleA1Bx   )r6   r7   N)r*   r+   r,   json_schema_extrar#   r#   r#   r$   Config]   s    rG   N)
r*   r+   r,   r-   r.   intr   rA   rB   rG   r#   r#   r#   r$   r5   I   s   
 
	
r5   returnc               	   C   sx   t jtsg S z%ttd} t| }dd |D W  d    W S 1 s&w   Y  W d S  tjtfy;   g  Y S w )Nrc                 S      g | ]	}t d i |qS r#   )r0   r;   qr#   r#   r$   
<listcomp>l       z(_load_quiz_questions.<locals>.<listcomp>)	ospathexistsQUESTIONS_FILEopenjsonloadJSONDecodeError	TypeError)fdatar#   r#   r$   _load_quiz_questionsf   s   
(r\   	questionsc                 C   sJ   t td}tjdd | D |dd W d    d S 1 sw   Y  d S )Nwc                 S      g | ]}|  qS r#   dictrM   r#   r#   r$   rO   s       z(_save_quiz_questions.<locals>.<listcomp>   indent)rU   rT   rV   dump)r]   rZ   r#   r#   r$   _save_quiz_questionsq   s   "rg   c                   C   s   t t S r9   )r-   uuiduuid4r#   r#   r#   r$   _generate_question_idu   s   rj   c               
   C   s"  t jts-tdt d ttd} tg |  W d    g S 1 s&w   Y  g S z%ttd} t	| }dd |D W  d    W S 1 sKw   Y  W d S  tj
tfys } ztdt|  g W  Y d }~S d }~w ty } ztdt|  g W  Y d }~S d }~ww )	NzScoreboard file z$ does not exist, creating empty filer^   rJ   c                 S   rK   rL   )r5   r;   entryr#   r#   r$   rO      rP   z$_load_scoreboard.<locals>.<listcomp>zError loading scoreboard: z%Unexpected error loading scoreboard: )rQ   rR   rS   SCOREBOARD_FILElogginginforU   rV   rf   rW   rX   rY   errorr-   	Exception)rZ   r[   er#   r#   r$   _load_scoreboardy   s,   

(rs   entriesc              
   C   s   z7t dt|  dt  ttd}tjdd | D |dd W d    n1 s+w   Y  t d W d S  tyS } zt d	t	|  t
d
ddd }~ww )NzWriting z entries to r^   c                 S   r_   r#   r`   rk   r#   r#   r$   rO      rb   z$_save_scoreboard.<locals>.<listcomp>rc   rd   z$Scoreboard file written successfullyzError saving scoreboard:   zFailed to save scoreboardstatus_codedetail)rn   ro   r   rm   rU   rV   rf   rq   rp   r-   r   )rt   rZ   rr   r#   r#   r$   _save_scoreboard   s   ry   z/favicon.icoc                      s
   t dS )Nztemplates/assets/favicon.ico)r   r#   r#   r#   r$   favicon   s   rz   z	/oat_quiz)response_classrequestc                    s`   t jt jt jtdd}t|d}t| ddW  d    S 1 s)w   Y  d S )N	templatesz	quiz.htmlrJ      )contentrw   )	rQ   rR   joindirnameabspath__file__rU   r   read)r|   quiz_html_file_path	html_filer#   r#   r$   get_quiz_page   s
    $r   z/api/quiz/questions)response_modelc                      s   t  S r9   )r\   r#   r#   r#   r$   get_quiz_questions   s   r   z/api/admin/quiz/questions   )r   rw   question_datac                    s6   t  }tddt i|  }|| t| |S )Nr1   r#   )r\   r0   rj   ra   appendrg   )r   r]   new_questionr#   r#   r$   create_quiz_question   s   
r   z'/api/admin/quiz/questions/{question_id}question_idquestion_updatec                    s   t  }d}t|D ]\}}|j| kr|} nq
|dkr"tddd|| }|jdd}| }|| |d}	|d}
|	rM|
rM|
|	vrMtd	d
d|	rbdt|	  kr[dksbn td	ddtdi |||< t	| || S )N  Question not foundrv   T)exclude_unsetr   r   i  z+Answer must be one of the provided options.r   r   z*Number of options must be between 2 and 8.r#   )
r\   	enumerater1   r   ra   updategetr   r0   rg   )r   r   r]   question_indexirN   current_questionupdate_dataupdated_question_datatemp_optionstemp_answerr#   r#   r$   update_quiz_question   s.   



r      )rw   c                    sV   t  }d}t|D ]\}}|j| kr|| d} nq
|s%tdddt| d S )NFTr   r   rv   )r\   r   r1   popr   rg   )r   r]   question_foundr   rN   r#   r#   r$   delete_quiz_question   s   

r   z/api/quiz/scoreboardc               
      s|   zt d t } | jdd d t dt|  d | W S  ty= } zt dt|  tdt|d	d }~ww )
NzFetching scoreboard entriesc                 S      | j S r9   r7   xr#   r#   r$   <lambda>       z get_scoreboard.<locals>.<lambda>keyz
Returning z scoreboard entrieszError in get_scoreboard: ru   rv   )	rn   ro   rs   sortr   rq   rp   r-   r   )rt   rr   r#   r#   r$   get_scoreboard   s   
r   rl   c              
      s   z9t d|    t }t d|  ||  |jdd d t dt| d t| t d | W S  tyX } zt 	d	t
|  td
t
|dd }~ww )Nz%Attempting to save scoreboard entry: zCurrent scoreboard entries: c                 S   r   r9   r   r   r#   r#   r$   r      r   z&add_scoreboard_entry.<locals>.<lambda>r   zSaving z entries to scoreboardz#Scoreboard entry saved successfullyzError adding scoreboard entry: ru   rv   )rn   ro   ra   rs   r   r   r   ry   rq   rp   r-   r   )rl   rt   rr   r#   r#   r$   add_scoreboard_entry   s    

r   )6fastapir   r   r   fastapi.responsesr   r   fastapi.staticfilesr   fastapi.middleware.corsr   typingr	   r
   rQ   rV   rh   rn   pydanticr   r   appadd_middlewaremountrR   r   r   r   r   rT   rm   r   r/   r0   r2   r5   r\   rg   r-   rj   rs   ry   r   rz   r   r   postr   putr   deleter   r   r   r#   r#   r#   r$   <module>   s`    	


