
    HiO              	          d dl Z d dlZd dlZd dlZd dlZd dlZ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c mZ d dlmc mZ d dlZd dlZd dlmZ  ej4                  dej6                        Zd Zd Zd	 Zd
 Z de!de!de!fdZ"d Z#d Z$d Z%d Z&d/de!de!de!de!fdZ'de!de(de!fdZ)d0de!de!de(fdZ*d  Z+d!e!de(fd"Z,d#e!de!fd$Z-d1d%Z.d2d&Z/d' Z0d( Z1d)e!de2fd*Z3d+ Z4d, Z5d3d-Z6d. Z7y)4    N)FastMailMessageSchemaConnectionConfig)HTTPExceptionstatus)JSONResponse)datez^(Module|Lesson|Unit)\s+\d+\bc                      dj                  t        j                  t        j                  t        j
                  z   d            S )N    )k)joinrandomchoicesstringascii_uppercasedigits     3/var/www/html/content_weaver/dependencies/helper.pygenerate_random_textr      s+    776>>&"8"86=="HANOOr   c                 4    t        j                  dd|       }|S )Nz[^a-zA-Z0-9]_)resub)input_stringsanitized_strings     r   replace_special_charactersr      s    vvosLAr   c                 L    t        j                  dd|       j                         S )z(Remove invalid characters for filenames.z[\\/*?:"<>|]r   r   r   strip)names    r   sanitize_filenamer#      s    66/3-3355r   c                 T    | j                  dg       D cg c]  }|d   	 }}|S c c}w )Nerrorsmessage)get)responseerrormessagess      r   get_error_messager+      s0    .6ll8R.HIUi IHIO Js   %	recipientsubjectbodyc           
        K   t        t        j                  d      t        j                  d      t        j                  d      dt        j                  d      ddd      }t        || g|d	
      }t	        |      }	 |j                  |       d {    y 7 # t        $ r}t        dt        |            d }~ww xY ww)NSES_ACCESS_KEY_IDSES_SECRET_ACCESS_KEYSES_SENDER_EMAILi  SES_AWS_HOSTFT)MAIL_USERNAMEMAIL_PASSWORD	MAIL_FROM	MAIL_PORTMAIL_SERVERMAIL_STARTTLSMAIL_SSL_TLSUSE_CREDENTIALShtml)r-   
recipientsr.   subtypei  )status_codedetail)	r   osgetenvr   r   send_message	Exceptionr   str)r,   r-   r.   confr&   fmes          r   
send_emailrI   "   s     ii 34ii 78))./IIn-	D ;	G 
$B<oog&&& <CF;;<s<   A;C>B BB CB 	B>#B99B>>Cc                 n    t        |       5 }t        j                  |      cddd       S # 1 sw Y   yxY w)aj  
    Reads a JSON file and returns its content as a Python dictionary or list,
    depending on the JSON structure.

    Args:
        file_path (str): The path to the JSON file to be read.

    Returns:
        dict or list: The content of the JSON file. The type depends on the
                    structure of the JSON file (object or array at the root).
    N)openjsonload	file_pathfs     r   read_json_filerQ   ;   s-     
i Ayy|  s   +4c                 d    t        |       5 }|j                         cd d d        S # 1 sw Y   y xY wN)rK   readrN   s     r   read_txt_filerU   J   s)    	i Avvx  s   &/c                 T    t        j                  d      }|j                  d|       } | S )Nz]:\s+"(.*?)"\s+httpz]: http)r   compiler   )textregexs     r   parserZ   O   s&    JJ-.E99Y%DKr   c                     | yi }| d   j                         D ]   \  }}|| d   |   d   | d   |   d   d||<   " |j                  |d      S )a*  Constructs a citation dictionary from search results.
    Args:
        search_results (dict): Dictionary containing search results.
        index_to_find (int): Index of the citation to find.
    Returns:
        dict: A dictionary containing the citation information for the specified index.
    Nurl_to_unified_indexurl_to_infotitlesnippets)urlr^   r_   )itemsr'   )search_resultsindex_to_findcitation_dictr`   indexs        r   $construct_citation_dict_from_articlerf   T   s|     M$%;<BBD 

U#M237@&}5c::F 
e
 ]D11r   course_titlecontent_dirwiki_diroutput_filec                 R   t        j                  dd       t        j                  dd       t        j                  dd       t        j                  dd       t        j                  d	d
       t        j                  ddt        j                         j
                   ddddd
dd      }d}t        j                  |d      }dt        j                  |d      _        dt        j                  |d      _        t        j                  |d      }t        j                  |d      }t        j                  |d      }	| t        j                  |	d      _        t        j                  |d      }
t        j                  |
d      }t        j                  |d      }t        j                         }|j                  d      }| t        j                  |d      _        t        j                  |d      }t        j                  |d      }d t        j                  |d!      _        t        j                  |d"      }d#t        j                  |d      _        t        j                  |d$      }t        j                  |d%d&d'd(      }t        j                  |d)      }d*}d*}d*}i }t        j                  |      D ]  \  }}}t        j                  j                  |      }t        j                   d+|t        j"                        }|r]||vrYd,t%        |      d-z    }t        j                  |d.d/|i      ||<   |j'                         t        j                  ||   d0      _        t)        |      D ]  }|d-z  }d1| } d2| }!t        j                  j+                  t        j                  j-                  ||      |      j/                  d3d4      }"t        j                  j1                  |      d-   j3                         }#|j/                  d5d6      j/                  d7d      j/                  d8d      j'                         j/                  d9d6      }$t        j                   d:|t        j"                        rd;}%d<}&|$}'n1|#d7k(  r
d=}%d>}&d?|$ }'n"|#d@v r
dA}%d>}&dB|$ }'n|#dCv r
dA}%dD}&dE|$ }'nd<}%dA}&|$}'|r||   n|}(t        j                  |(d.| |!dF      })|'t        j                  |)d0      _        t        j                  |)dGdH|i      }*t        j                  |*dI       |!|&|"dJ}+|%r|%|+dK<   t        j                  |dL|+      },t        j                  |,dMdN|"i         t        j4                  |      }-t        j6                  |-dOd*P       |-j9                  |dQdRS       t;        dT|        yA)Ua  Creates an IMSCC manifest XML file for a course.
    Args:
        course_title (str): Title of the course.
        content_dir (str): Directory containing course content.
        wiki_dir (str): Directory for wiki content.
        output_file (str): Path to save the generated manifest file.
    Returns:
        None: The function writes the manifest XML to the specified output file.
    r   z1http://www.imsglobal.org/xsd/imsccv1p1/imscp_v1p1imsqti+http://www.imsglobal.org/xsd/ims_qtiasiv1p2lomz/http://ltsc.ieee.org/xsd/imsccv1p1/LOM/resourcelomimsccz/http://ltsc.ieee.org/xsd/imsccv1p1/LOM/manifestxsiz)http://www.w3.org/2001/XMLSchema-instancemanifestman_am  http://www.imsglobal.org/xsd/imsccv1p1/imscp_v1p1 http://www.imsglobal.org/profile/cc/ccv1p1/ccv1p1_imscp_v1p2_v1p0.xsd http://ltsc.ieee.org/xsd/imsccv1p1/LOM/resource http://www.imsglobal.org/profile/cc/ccv1p1/LOM/ccv1p1_lomresource_v1p0.xsd http://ltsc.ieee.org/xsd/imsccv1p1/LOM/manifest http://www.imsglobal.org/profile/cc/ccv1p1/LOM/ccv1p1_lommanifest_v1p0.xsd)
identifierxmlnszxmlns:imsqtiz	xmlns:lomzxmlns:lomimsccz	xmlns:xsizxsi:schemaLocationz(http://canvas.instructure.com/x-manifestmetadatazIMS Common Cartridgeschemaz1.1.0schemaversionzlomimscc:lomzlomimscc:generalzlomimscc:titlezlomimscc:stringzlomimscc:lifeCyclezlomimscc:contributezlomimscc:datez%Y-%m-%dzlomimscc:dateTimezlomimscc:rightsz&lomimscc:copyrightAndOtherRestrictionsyeszlomimscc:valuezlomimscc:descriptionz>Private (Copyrighted) - http://en.wikipedia.org/wiki/Copyrightorganizationsorganizationorg_1zrooted-hierarchy)rs   	structure	resourcesr   z^(module|content)\s*\d+module_   itemrs   r^   item_res_\/r    .html.xml-z-(course[-_ ]?(description|overview|outcomes))syllabus
webcontentlessonz=associatedcontent/imscc_xmlv1p1/learning-application-resourcezLesson: )z.pdfz.docxz.pptxNzFile: )r   z.qtiz'imsqti_xmlv1p2/imscc_xmlv1p1/assessmentzQuiz: )rs   identifierref	extensionrt   module_item)rs   typehrefintendeduseresourcefiler     spacelevelutf-8Tencodingxml_declarationu    ✅ imsmanifest.xml created at: )ETregister_namespaceElementuuiduuid4hex
SubElementrX   r	   todaystrftimerA   walkpathbasenamer   match
IGNORECASElenr^   sortedrelpathr   replacesplitextlowerElementTreeindentwriteprint).rg   rh   ri   rj   rq   	CANVAS_NSru   rn   generalr^   	lifeCycle
contributelifeCycleDater   formatted_date	lomRights
copyrightscopyrightDescriptionry   orgresources_eliitem_countermodule_countermodulesdirpathr   	filenamesfolder_nameis_module_foldermod_idfilenameitem_idres_idrel_pathext
base_titler   rtypedisplay_titleparent_itemnested_itemext_el	res_attrsr   trees.                                                 r   create_manifestr   g   s$    "QR($QR%!RS*&WX%!LMzz*TZZ\--./DEFK@Y' H$ ;I}}Xz2H-CBMM(H%*4;BMM(O,1
--.
1CmmC!34GMM'#34E3?BMM%*+0c#78Iy*?@JMM*_=MJJLE^^J/N@N?OBMM-!45:c"34Iy)QRJ6;BMM*-.3==3IJ BBBMM&'89> MM(O<M
--~g\n7o
pC==;7L	ALNG!#!5 :@Igg&&w/88$>R]][7 :s7|A~./F#%==f|V>T#UGK @K@Q@Q@SBMM'+.8= y) /	@HALl^,GL>*Fwwrww||GX'FT\\]acfgH''""8,Q/557C!))#s3;;GRHPPQWY[\bbdllmpruvJ xxH(TVTaTab($ *&W"*:, 722"W"( 5(("A"( 5* *2B'+.K--VGfl=mnK7DBMM+w/4 ]];gy=QRFMM&-0 (.uhOI+6	-(}}\:yIHMM(FVX,>?_/	@:@x >>(#DIId$a(JJ{WdJC	,[M
:;r   module_name	questionsoutput_pathc           	         d}t        j                  dd|i      }t        j                  |dt        t	        j
                               | d      }t        j                  |dddi      }t        |d	
      D ]  \  }}t        j                  |dt        t	        j
                               d| d      }	t        j                  |	d      }
t        j                  t        j                  |
d      d      }|d   |_        t        j                  |
dd| dd      }t        j                  |d      }t        |d         D ]_  \  }}t        d|z         }t        j                  |dd|i      }t        j                  |d      }t        j                  |d      }||_        a t        j                  |	d      }t        j                  |d      }t        j                  |ddddd       t        j                  |d d!d"i      }t        j                  |d#      }|d$   t        j                  |d%d&d| i      _        dt        j                  |d'd(d)i      _         t        j                  |      }t        j                  |d*d+,       t        |       } |j                  t        j                  j                  ||  d-      d.d/0       y1)2ag  Creates a QTI XML file for a module with multiple-choice questions.
    Args:
        module_name (str): Name of the module.
        questions (list): List of dictionaries containing question data.
        output_path (str): Path to save the generated QTI XML file.
    Returns:
        None: The function writes the QTI XML to the specified output path.
    rm   questestinteroprt   
assessment)identr^   sectionr   root_sectionr   startr   z	Question presentationmaterialmattextquestionresponse_lidQSingle)r   rcardinalityrender_choiceoptionsA   response_labelresprocessingoutcomesdecvar1000SCORE)maxvalueminvaluevarnamerespconditioncontinueNoconditionvaranswervarequal	respidentsetvaractionSetr   r   r   z	_quiz.xmlr   Tr   N)r   r   r   rE   r   r   	enumeraterX   chrr   r   r   r   rA   r   r   )r   r   r   qtinsr   r   r   r   qr   r   r   r   r   joptr   respmatr   r   r   r   r   s                           r   create_qti_for_moduler    se    :Ejj!2We4DEODJJLHYdo>pqJmmJ	G^3LMG)1- O1}}WfDJJL8IV_`a_bTc.de}}T>:--lJ GS}}}.asGX*V
 lOD)- 	FAsQKE==0@7EBRSD--j1CmmC3GGL	 dO<==
;
hu#Za*bcm_zSWFXY}}]NCOPQY{lJ!g0FGLINmX%/@AF7O: >>/*DIId$a(,[9KJJrww||KK=	)BCggkJlr   folder_path	base_pathreturnc                 
   g }t        t        j                  |       d      D ]]  \  }}|j                         j	                  d      s&t        j
                  j                  ||      }|j                  d| d|d       _ |S )a)  Generates a list of resources from a folder containing HTML files.
    Args:
        folder_path (str): Path to the folder containing HTML files.
        base_path (str): Base path to prepend to the resource hrefs.
    Returns:
        list: A list of dictionaries representing the resources.
    r   r   r   r   r   )idr   r   )r   rA   listdirr   endswithr   r   append)r	  r
  r}   r   r   	full_paths         r   generate_resources_from_folderr  3  s     I K!8B 	8~~((1GGLLH5	* 
 		 r   c                 L    t        j                  dd|       j                         S )Nz\s+r   r    )r   s    r   normalize_spacesr  H  s    66&#|,2244r   md_textc                    t        j                  d| t         j                        }g }t        dt	        |      d      D ]B  }||   }|dz   t	        |      k  r|||dz      z  }|j                  |j                                D |S )zBSplits the markdown into sections based on top-level (#) headings.z(?=^(##\s.*|### Module \d+:))flagsr      r   )r   split	MULTILINEranger   r  r!   )r  partssectionsr   r   s        r   split_markdown_sectionsr  K  s     HH5wbllSEH1c%j!$ )(q53u:uQU|#G(	)
 Or   
md_contentc                 v    g d}|D ]/  }d| d}t        j                  |d| t         j                        } 1 | S )N)zQuestion \d+:zCorrect Answer:z
Subtopics:zDescription:z	^(?P<key>)z**\g<key>**r  )r   r   r  )r   keywordskeywordpatterns       r   bold_markdown_keywordsr&  Y  sF    RH Uwiq)VVG^Zr||T
U
 r   c                 b    t        |       |kD  r | d| }t        j                  d| d       |S | S )zTruncate filename to max_length to ensure the filename won't exceed the file system limit.

    Args:
        filename: str
        max_length: int, default to 125 (usual path length limit is 255 chars)
    Nz/Filename is too long. Filename is truncated to .)r   loggingwarning)r   
max_lengthtruncated_filenames      r   truncate_filenamer-  d  sE     8}z!%kz2=>P=QQRS	
 "!Or   c                      t        j                  dd      }t         j                  j                  |d      }t	        |      }t         fd|D        d      }|r|S y)a6  
    Retrieves a prompt from the prompts directory based on the prompt name and topic.
    
    Args:
        prompt_name (str): The name of the prompt key.
        prompt_type (str): The type of the prompt, e.g., 'course', 'module', etc.
        
    Returns:
        str: The content of the prompt file.
    PROMPTS_DIRpromptszprompt_data.jsonc              3   L   K   | ]  }|d    k(  s|d   k(  s|d     yw)r"   r   promptNr   ).0r   prompt_nameprompt_types     r   	<genexpr>zget_prompt.<locals>.<genexpr>  s>       DDd6lVaFafjkqfr  xC  gCh  Ds   $$
$NF)rA   rB   r   r   rQ   next)r4  r5  prompts_dirprompt_fileprompt_contentprompt_values   ``    r   
get_promptr<  u  s`     ))M95K'',,{.>@K#K0N  DN  D  FJ  KLr   c                     | j                         j                  dd      j                  dd      j                  dd      S )Nr   r   :r   ,)r   r   rX   s    r   slugifyrA    s5    ::<S)11#r:BB3KKr   c                 ^   | j                         j                         j                  dd      j                  dd      j                  dd      j                  dd      j                  dd      j                  dd      j                  d	d      j                  d
d      j                  dd      S )Nr   r   r>  r   r?  r(  r   ?!'")r   r!   r   r@  s    r   	make_slugrG    s    ::<	c		b		b		b		c		b		b		r		b		r   r^   c                 0    t         j                  |       d uS rS   )CONTAINER_REr   )r^   s    r   is_container_headingrJ    s    e$D00r   c                    g }g }d }i }| j                  g d      D ]  }|j                  j                  d      r!t        |j                  d         }|j	                         j                         }t        |      }|}	|s|j                  |      r|dz   }	|r)|d   d   |	k\  r|j                          |r|d   d   |	k\  r|r|d   d   nd}
t        |      }|
r|
 d| n|}|	|||dg d	}|r|d   d
   j                  |       n|j                  |       |j                  |       |}|s|||<   t        |j                               D ]  }||kD  s	||=  A|sE|j                  dv rL|j                  g d      h|dxx   t        |j	                         j                               dz   z  cc<   |j                  dv s|j                  dd      }|D ]/  }|dxx   d|j	                         j                          dz  cc<   1 |dxx   dz  cc<    fd|D cg c]
  } |       c}S c c}w )N)	h1h2h3h4h5h6pulolhr   r   r   r   r   )r   r^   slugr   contentchildrenrY  )rR  )rS  rT  lirX  z

)rS  rT  rZ  F)	recursivez- 
c                 n    | j                  dd        | d   D cg c]
  } |       c}| d<   | S c c}w )Nr   rY  )pop)nodechildstrip_path_keys     r   ra  z(build_nested_toc.<locals>.strip_path_key  s?     @DJ?OPeN51PZ Qs   2)find_allr"   
startswithintget_textr!   rJ  r'   r^  rG  r  listkeysfind_parentr&  )soupstackrootcurrentcontainer_at_leveltagr   r^   is_containereffective_levelparent_pathcurrent_slugr  r_  r   ra   r   nra  s                     @r   build_nested_tocrt    s`   EDG}}RS ;+88s#$ELLN((*E/6L $O$6$:$:5$A"'!) E"Ig./A		 E"Ig./A 05%)F+"K$U+L;F;-q7LI )!!D b	*%,,T2D!LLG ,0"5)05578 2A5y.q12 xx5 ??#56>I&*@AUAUAW*X[a*aa&\)TU;! KDI&Bt}}/D/D/F.Gr*JJ&K	"d*"w;+z (,,!N1,,,s   ,H>c                 2   | j                  d      D ]  }|j                  s|j                  d      |j                  j                  d      k(  s@| j                  d      }|j                  j                  d      |_        |j                  |        | S )NrR  T)r!   rO  )rb  strongre  new_tagr   replace_with)ri  rn  rw  s      r   normalize_headingsry    s    }}S! &::#,,T,2cjj6I6IPT6I6UUll4(G ZZ00t0<GNW%& Kr   c                 F    |st         j                  }t        ||| d      S )N)r   r&   r?   rX  )r   HTTP_400_BAD_REQUESTr   )r&   r?   s     r   errorResponser}    s+    11!
 r   c                 \    t        t        j                  t        j                  | |d      S )N)r   r&   datar{  )r   r   HTTP_200_OK)r&   r  s     r   successResponser    s,    &&((
	 r   )zimsmanifest.xml)wiki_content)}   rS   )i  )8rL   rA   r   r   r   base64fastapi_mailr   r   r   fastapir   r   fastapi.responsesr   xml.etree.ElementTreeetreer   r   xml.dom.minidomdomminidomr   r)  datetimer	   rW   r   rI  r   r   r#   r+   rE   rI   rQ   rU   rZ   rf   r   rf  r  r  r  r  r&  r-  r<  rA  rG  boolrJ  rt  ry  r}  r  r   r   r   <module>r     s]    	   	  B B ) * " " ! !   rzz:BMMJP
6< <c < <2

2&H<# H<C H<3 H<UX H<t0ms 0mt 0m# 0md  Y] *5S T 	s 	s 	",L
1 1 1J-X
r   