o
    ›¨Êhée  ã                   @   sÂ   d dl Z d dlZd dlmZ d dlmZmZ d dlmZ d dl	m
Z
mZ d dlmZ d dlmZmZ d dlmZ G d	d
„ 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dS )é    N)Úreduce)Úconnection_routerÚconnections)ÚSQ)ÚDEFAULT_OPERATORÚITERATOR_LOAD_PER_QUERY)Ú
NotHandled)Ú	AutoQueryÚRaw)Úlogc                   @   s´  e Zd ZdZdkdd„Zdd„ Zdd„ Zd	d
„ Zdd„ Zdd„ Z	dd„ Z
dd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd „ Zd!d"„ Zd#d$„ Zd%d&„ Zd'd(„ Zd)d*„ Zd+d,„ Zd-d.„ Zd/d0„ Zd1d2„ Zd3d4„ Zd5d6„ Zd7d8„ Zd9d:„ Zd;d<„ Z d=d>„ Z!dld?d@„Z"dAdB„ Z#dmdDdE„Z$dFdG„ Z%dHdI„ Z&dJdK„ Z'dLdM„ Z(dndOdP„Z)dQdR„ Z*dSdT„ Z+dUdV„ Z,dWdX„ Z-dYdZ„ Z.d[d\„ Z/d]d^„ Z0d_d`„ Z1dadb„ Z2dldcdd„Z3dedf„ Z4dgdh„ Z5dldidj„Z6dS )oÚSearchQuerySetz‹
    Provides a way to specify search parameters and lazily load results.

    Supports chaining (a la QuerySet) to narrow the search.
    Nc                 C   sP   || _ d | _|  ¡  |d ur|| _g | _d | _d| _d| _d| _t 	d¡| _
d S )NFr   Úhaystack)Ú_usingÚqueryÚ_determine_backendÚ_result_cacheÚ_result_countÚ_cache_fullÚ	_load_allÚ_ignored_result_countÚloggingÚ	getLoggerr   )ÚselfÚusingr   © r   ú@/var/www/html/env/lib/python3.10/site-packages/haystack/query.pyÚ__init__   s   zSearchQuerySet.__init__c                 C   sn   | j d urt| j   ¡ | _d S i }| jr| jj|d< tjdi |¤Ž}| jr.| j |¡| _d S t|  ¡ | _d S )NÚmodelsr   )r   r   Ú	get_queryr   r   r   Úfor_readr   )r   ÚhintsÚbackend_aliasr   r   r   r   '   s   
z!SearchQuerySet._determine_backendc                 C   s&   t | ƒ | j ¡ }d|d< d|d< |S )z
        For pickling.
        NÚ_iterr   )ÚlenÚ__dict__Úcopy)r   Úobj_dictr   r   r   Ú__getstate__<   s
   
zSearchQuerySet.__getstate__c                 C   s   || _ t d¡| _dS )z!
        For unpickling.
        r   N)r$   r   r   r   )r   Ú	data_dictr   r   r   Ú__setstate__F   s   zSearchQuerySet.__setstate__c                 C   s   d| j | jf S )Nz$<SearchQuerySet: query=%r, using=%r>)r   r   ©r   r   r   r   Ú__repr__M   s   zSearchQuerySet.__repr__c                 C   s.   | j d u r| j ¡ | _ | j sd| _ | j | j S ©Nr   )r   r   Ú	get_countr   r*   r   r   r   Ú__len__P   s
   
zSearchQuerySet.__len__c                 C   s   |   ¡ r	t| jƒS |  ¡ S ©N)Ú_cache_is_fullÚiterr   Ú_manual_iterr*   r   r   r   Ú__iter__[   s   
zSearchQuerySet.__iter__c                 C   s0   t |tƒr	| ¡ S |  ¡ }|j |jtj¡ |S r/   )Ú
isinstanceÚEmptySearchQuerySetÚ_cloner   Úcombiner   ÚAND©r   ÚotherÚcombinedr   r   r   Ú__and__b   s
   
zSearchQuerySet.__and__c                 C   s,   |   ¡ }t|tƒr|S |j |jtj¡ |S r/   )r6   r4   r5   r   r7   r   ÚORr9   r   r   r   Ú__or__i   s
   
zSearchQuerySet.__or__c                 C   sR   | j  ¡ sdS t| ƒdkrdS z	| j d ¡ W dS  ty(   t| jƒdk Y S w )NFr   T)r   Úhas_runr#   r   ÚindexÚ
ValueErrorr*   r   r   r   r0   p   s   
þzSearchQuerySet._cache_is_fullc                 c   sŽ   � d}d}	 t | jƒdkr$z| j d ¡}W n ty#   t | jƒ}Y nw ||k r6| j| V  |d7 }||k s(|  ¡ r<d S |  ||t ¡sFd S q)Nr   Té   )r#   r   r@   rA   r0   Ú_fill_cacher   )r   Úcurrent_positionÚcurrent_cache_maxr   r   r   r2   ~   s*   €ÿþÿízSearchQuerySet._manual_iterc           	   	   C   s   g }| j r(i }i }|D ]}| |jg ¡ |j¡ q|D ]}|  ||| ¡||< q|D ]S}| j rx| |ji ¡}|rdttt	|ƒƒƒ}||jƒ|_z||j |_
W n) tyc   |  jd7  _| j ¡  Y q*w | jd t|ƒ…= |  jt|ƒ7  _ |S | |¡ q*|S )NrB   )r   Ú
setdefaultÚmodelÚappendÚpkÚ_load_model_objectsÚgetÚtypeÚnextr1   Ú_objectÚKeyErrorr   r   Úpopr#   )	r   ÚresultsÚto_cacheÚ
models_pksÚloaded_objectsÚresultrG   Úmodel_objectsÚresult_klassr   r   r   Úpost_process_results›   s:   
ÿ
øþz#SearchQuerySet.post_process_resultsc                 C   sd   zt | jj  ¡ }| |¡}|j| jjd�}| |¡W S  ty1   | j 	d|¡ |j
 |¡ Y S w )N)r   z&Model '%s' not handled by the routers.)r   r   r   Úget_unified_indexÚ	get_indexÚread_querysetÚin_bulkr   r   ÚwarningÚ_default_manager)r   rG   ÚpksÚuir@   Úobjectsr   r   r   rJ   Í   s   
ýz"SearchQuerySet._load_model_objectsc                 K   sd  | j  ¡  |d u rd}|}|| j7 }|}|d ur|| j7 }| j  ||¡ | j jdi |¤Ž}|d u s7t|ƒdkrA| jd |… | _dS t| jƒdkrQd g| j  ¡  | _||}}|d u r_| j  ¡ }|}		 |  |¡}
|
| j|	|	t|
ƒ …< d | j||… v r®|}|t	7 }|	t|
ƒ7 }	| j  ¡  | j  ||¡ | j  ¡ }|d u s¢t|ƒdkr­| jd |	… | _	 dS n	 dS qb)Nr   FTr   )
r   Ú_resetr   Ú
set_limitsÚget_resultsr#   r   r-   rX   r   )r   ÚstartÚendÚkwargsÚquery_startÚ	query_endrQ   Ú
fill_startÚfill_endÚcache_startrR   r   r   r   rC   Ø   sJ   







ùézSearchQuerySet._fill_cachec                 C   s  t |ttfƒs	t‚t |tƒs|dks/t |tƒr+|jdu s!|jdkr+|jdu s/|jdks/J dƒ‚t |tƒrGd}|j}|jdurDt|jƒ}nd}nd}|}|d }t| jƒdkscd| j||… v ru|  ¡ suz|  	||¡ W n	 t
yt   Y nw |r~| j||… S | j| S )zE
        Retrieves an item or slice from the set of results.
        r   Nz#Negative indexing is not supported.TFrB   )r4   ÚsliceÚintÚ	TypeErrorre   Ústopr#   r   r0   rC   ÚStopIteration)r   ÚkÚis_slicere   Úboundr   r   r   Ú__getitem__  s8   ÿÿ

þ
zSearchQuerySet.__getitem__c                 C   s   |   ¡ S )z"Returns all results for the query.)r6   r*   r   r   r   Úall?  ó   zSearchQuerySet.allc                 C   s   | j td�S )z+Returns an empty result list for the query.©Úklass)r6   r5   r*   r   r   r   ÚnoneC  s   zSearchQuerySet.nonec                 O   s(   t dkr| j|i |¤ŽS | j|i |¤ŽS )zHNarrows the search based on certain attributes and the default operator.r=   )r   Ú	filter_orÚ
filter_and©r   Úargsrg   r   r   r   ÚfilterG  s   zSearchQuerySet.filterc                 O   s$   |   ¡ }|j t|i |¤Ž ¡ |S )úCNarrows the search by ensuring certain attributes are not included.©r6   r   Ú
add_filterr   ©r   r~   rg   Úcloner   r   r   ÚexcludeM  s   zSearchQuerySet.excludec                 O   s"   |   ¡ }|j t|i |¤Ž¡ |S )zENarrows the search by looking for (and including) certain attributes.r�   rƒ   r   r   r   r|   S  s   zSearchQuerySet.filter_andc                 O   s&   |   ¡ }|jjt|i |¤Ždd� |S )r€   T)Úuse_orr�   rƒ   r   r   r   r{   Y  s   zSearchQuerySet.filter_orc                 G   s"   |   ¡ }|D ]}|j |¡ q|S )z4Alters the order in which the results should appear.)r6   r   Úadd_order_by)r   r~   r„   Úfieldr   r   r   Úorder_by_  s   zSearchQuerySet.order_byc                 K   s   |   ¡ }|jjdi |¤Ž |S )z!Adds highlighting to the results.Nr   )r6   r   Úadd_highlight)r   rg   r„   r   r   r   Ú	highlighth  s   zSearchQuerySet.highlightc                 G   sJ   |   ¡ }|D ]}|t| jj  ¡  ¡ vrt d|f ¡ |j |¡ q|S )zFAccepts an arbitrary number of Model classes to include in the search.z*The model %r is not registered for search.)	r6   r   r   r   rY   Úget_indexed_modelsÚwarningsÚwarnÚ	add_model)r   r   r„   rG   r   r   r   r   n  s   
ýzSearchQuerySet.modelsc                 C   ó   |   ¡ }|j |¡ |S )zÔ
        Allows specifying a different class to use for results.

        Overrides any previous usages. If ``None`` is provided, Haystack will
        revert back to the default ``SearchResult`` object.
        )r6   r   Úset_result_class©r   ry   r„   r   r   r   Úresult_class  s   zSearchQuerySet.result_classc                 C   ó   |   ¡ }|j ||¡ |S )z%Boosts a certain aspect of the query.)r6   r   Ú	add_boost)r   ÚtermÚboostr„   r   r   r   r—   Š  ó   zSearchQuerySet.boostc                 K   s    |   ¡ }|jj|fi |¤Ž |S )z0Adds faceting to a query for the provided field.)r6   r   Úadd_field_facet)r   rˆ   Úoptionsr„   r   r   r   Úfacet�  s   zSearchQuerySet.facetc                 C   ó   |   ¡ }|j |||¡ |S )z1Spatial: Adds a bounding box search to the query.)r6   r   Ú
add_within)r   rˆ   Úpoint_1Úpoint_2r„   r   r   r   Úwithin–  ó   zSearchQuerySet.withinc                 C   rœ   )z3Spatial: Adds a distance-based search to the query.)r6   r   Úadd_dwithin)r   rˆ   ÚpointÚdistancer„   r   r   r   Údwithinœ  r¡   zSearchQuerySet.dwithinc                 C   s   | j |dd�S )z-Adds stats to a query for the provided field.N)Úfacet_fields)Ústats_facet)r   rˆ   r   r   r   Ústats¢  s   zSearchQuerySet.statsc                 C   sT   |   ¡ }g }z
| t|g ƒ¡ W n ty    |r| |¡ Y nw |j ||¡ |S )z\Adds stats facet for the given field and facet_fields represents
        the faceted fields.)r6   rH   Úsumro   r   Úadd_stats_query)r   rˆ   r¦   r„   Ústats_facetsr   r   r   r§   ¦  s   
€þzSearchQuerySet.stats_facetc                 C   r”   )zk
        Spatial: Denotes results must have distance measurements from the
        provided point.
        )r6   r   Úadd_distance)r   rˆ   r£   r„   r   r   r   r¤   ³  s   zSearchQuerySet.distancerB   c                 C   s"   |   ¡ }|jj|||||d� |S )z8Adds faceting to a query for the provided field by date.)Ú
gap_amount)r6   r   Úadd_date_facet)r   rˆ   Ú
start_dateÚend_dateÚgap_byr­   r„   r   r   r   Ú
date_facet¼  s
   
ÿzSearchQuerySet.date_facetc                 C   r”   )zDAdds faceting to a query for the provided field with a custom query.)r6   r   Úadd_query_facet)r   rˆ   r   r„   r   r   r   Úquery_facetÄ  r˜   zSearchQuerySet.query_facetc                 C   s@   t |tƒr| j ¡ }| ¡  | |j¡}|  ¡ }|j |¡ |S )z.Pushes existing facet choices into the search.)r4   r   r   r6   rb   Úas_query_stringÚbuild_query_fragmentÚadd_narrow_query)r   r   Úempty_queryr„   r   r   r   ÚnarrowÊ  s   

zSearchQuerySet.narrowc                 K   s   | j t|fi |¤Žd�S )z+Passes a raw query directly to the backend.©Úcontent)r   r
   )r   Úquery_stringrg   r   r   r   Ú
raw_search×  s   zSearchQuerySet.raw_searchc                 C   s   |   ¡ }d|_|S )z8Efficiently populates the objects in the search results.T)r6   r   ©r   r„   r   r   r   Úload_allÛ  s   zSearchQuerySet.load_allr»   c                 C   s   |t |ƒi}| jdi |¤ŽS )z©
        Performs a best guess constructing the search query.

        This method is somewhat naive but works well enough for the simple,
        common cases.
        Nr   )r	   r   )r   r¼   Ú	fieldnamerg   r   r   r   Ú
auto_queryá  s   zSearchQuerySet.auto_queryc                 K   sp   |   ¡ }g }| ¡ D ]$\}}| d¡D ]}|j | ¡ ¡}|r-||i}| tdi |¤Ž¡ qq
| t	t
j|ƒ¡S )z¨
        A shortcut method to perform an autocomplete search.

        Must be run against fields that are either ``NgramField`` or
        ``EdgeNgramField``.
        ú Nr   )r6   ÚitemsÚsplitr   ÚcleanÚstriprH   r   r   r   Úoperatorr<   )r   rg   r„   Ú
query_bitsÚ
field_namer   ÚwordÚbitr   r   r   Úautocompleteë  s   €üzSearchQuerySet.autocompletec                 C   s    |   ¡ }| j |¡|_||_|S )ze
        Allows switching which connection the ``SearchQuerySet`` uses to
        search in.
        )r6   r   r   r   )r   Úconnection_namer„   r   r   r   r   þ  s   zSearchQuerySet.usingc                 C   s   t | ƒS )z-Returns the total number of matching results.)r#   r*   r   r   r   Úcount
  rw   zSearchQuerySet.countc                 C   s   | d S )z:Returns the best/top search result that matches the query.r   r   r*   r   r   r   Ú
best_match  rw   zSearchQuerySet.best_matchc                 C   s*   |   ¡ }|j ¡  |j d| ¡ | ¡ S )z=Returns the most recent search result that matches the query.z-%s)r6   r   Úclear_order_byr‡   rÏ   )r   Ú
date_fieldr„   r   r   r   Úlatest  s   
zSearchQuerySet.latestc                 C   r�   )z.Finds similar results to the object passed in.)r6   r   Úmore_like_this)r   Úmodel_instancer„   r   r   r   rÓ     s   zSearchQuerySet.more_like_thisc                 C   ó&   | j  ¡ r
| j  ¡ S |  ¡ }|j  ¡ S )z«
        Returns the facet counts found by the query.

        This will cause the query to execute and should generally be used when
        presenting the data.
        )r   r?   Úget_facet_countsr6   r¾   r   r   r   Úfacet_counts  s   


zSearchQuerySet.facet_countsc                 C   rÕ   )z?
        Returns the stats results found by the query.
        )r   r?   Ú	get_statsr6   r¾   r   r   r   Ústats_results,  s   


zSearchQuerySet.stats_resultsc                 C   r�   )a`  Set the exact text to be used to generate spelling suggestions

        When making complicated queries, such as the alt parser mechanism
        used by Solr dismax/edismax, this provides a convenient way to set
        the a simple text string which will be used to generate spelling
        suggestions without including unnecessary syntax.
        )r6   r   Úset_spelling_query)r   Úspelling_queryr„   r   r   r   rÚ   6  s   z!SearchQuerySet.set_spelling_queryc                 C   s*   | j  ¡ r| j  |¡S |  ¡ }|j  |¡S )aN  
        Returns the spelling suggestion found by the query.

        To work, you must set ``INCLUDE_SPELLING`` within your connection's
        settings dictionary to ``True``. Otherwise, ``None`` will be returned.

        This will cause the query to execute and should generally be used when
        presenting the data.
        )r   r?   Úget_spelling_suggestionr6   )r   Úpreferred_queryr„   r   r   r   Úspelling_suggestionB  s   

z"SearchQuerySet.spelling_suggestionc                 G   s   | j td�}|j |¡ |S )z—
        Returns a list of dictionaries, each containing the key/value pairs for
        the result, exactly like Django's ``ValuesQuerySet``.
        rx   )r6   ÚValuesSearchQuerySetÚ_fieldsÚextend)r   ÚfieldsÚqsr   r   r   ÚvaluesR  s   zSearchQuerySet.valuesc                 O   sF   |  dd¡}|rt|ƒdkrtdƒ‚| jtd�}|j |¡ ||_|S )a)  
        Returns a list of field values as tuples, exactly like Django's
        ``QuerySet.values``.

        Optionally accepts a ``flat=True`` kwarg, which in the case of a
        single field being provided, will return a flat list of that field
        rather than a list of tuples.
        ÚflatFrB   zH'flat' is not valid when values_list is called with more than one field.rx   )rP   r#   ro   r6   ÚValuesListSearchQuerySetrà   rá   Ú_flat)r   râ   rg   rå   rã   r   r   r   Úvalues_list[  s   	ÿzSearchQuerySet.values_listc                 C   s.   |d u r| j }| j ¡ }||d�}| j|_|S )N)r   )Ú	__class__r   r6   r   )r   ry   r   r„   r   r   r   r6   r  s   

zSearchQuerySet._clone)NNr/   )rB   rº   )7Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r'   r)   r+   r.   r3   r<   r>   r0   r2   rX   rJ   rC   ru   rv   rz   r   r…   r|   r{   r‰   r‹   r   r“   r—   r›   r    r¥   r¨   r§   r¤   r²   r´   r¹   r½   r¿   rÁ   rÌ   r   rÎ   rÏ   rÒ   rÓ   r×   rÙ   rÚ   rÞ   rä   rè   r6   r   r   r   r   r      sj    

2<+	

	



	r   c                       sB   e Zd ZdZdd„ Zdd„ Zd‡ fdd„	Zd	d
„ Zdd„ Z‡  Z	S )r5   z\
    A stubbed SearchQuerySet that behaves as normal but always returns no
    results.
    c                 C   ó   dS r,   r   r*   r   r   r   r.   ‚  ó   zEmptySearchQuerySet.__len__c                 C   rî   )NTr   r*   r   r   r   r0   …  s   z"EmptySearchQuerySet._cache_is_fullNc                    s   t ƒ j|d�}g |_|S ©Nrx   )Úsuperr6   r   r’   ©ré   r   r   r6   ‰  s   zEmptySearchQuerySet._clonec                 C   rî   )NFr   )r   re   rf   r   r   r   rC   Ž  rï   zEmptySearchQuerySet._fill_cachec                 C   s   i S r/   r   r*   r   r   r   r×   ‘  rï   z EmptySearchQuerySet.facet_countsr/   )
rê   rë   rì   rí   r.   r0   r6   rC   r×   Ú__classcell__r   r   rò   r   r5   |  s    r5   c                       sB   e Zd ZdZ‡ fdd„Zd‡ fdd„	Z‡ fdd„Zd	d
„ Z‡  ZS )ræ   z€
    A ``SearchQuerySet`` which returns a list of field values as tuples, exactly
    like Django's ``ValuesListQuerySet``.
    c                    s,   t ƒ j|i |¤Ž d| _g | _g d¢| _d S )NF)ÚidÚ	django_ctÚ	django_idÚscore)rñ   r   rç   rà   Ú_internal_fieldsr}   rò   r   r   r   ›  s   z!ValuesListSearchQuerySet.__init__Nc                    s"   t ƒ j|d�}| j|_| j|_|S rð   )rñ   r6   rà   rç   r’   rò   r   r   r6   ¥  s   zValuesListSearchQuerySet._clonec                    s4   t | jƒ}| | j¡ d|i}tƒ j||fi |¤ŽS ©Nrâ   )Úsetrø   Úupdaterà   rñ   rC   ©r   re   rf   Úquery_fieldsrg   rò   r   r   rC   «  s   
z$ValuesListSearchQuerySet._fill_cachec                    s>   g }| j r	|j}n|j}|D ]‰ |‡ fdd„| jD ƒƒ q|S )Nc                    s   g | ]}t ˆ |d ƒ‘qS r/   ©Úgetattr©Ú.0Úi©rU   r   r   Ú
<listcomp>º  s    zAValuesListSearchQuerySet.post_process_results.<locals>.<listcomp>)rç   rá   rH   rà   )r   rQ   rR   Úaccumr   r  r   rX   ±  s   z-ValuesListSearchQuerySet.post_process_resultsr/   )	rê   rë   rì   rí   r   r6   rC   rX   ró   r   r   rò   r   ræ   •  s    
ræ   c                       s(   e Zd ZdZ‡ fdd„Zdd„ Z‡  ZS )rß   zª
    A ``SearchQuerySet`` which returns a list of dictionaries, each containing
    the key/value pairs for the result, exactly like Django's
    ``ValuesQuerySet``.
    c                    s8   t | jƒ}| | j¡ d|i}tt| ƒj||fi |¤ŽS rù   )rú   rø   rû   rà   rñ   ræ   rC   rü   rò   r   r   rC   Æ  s   
z ValuesSearchQuerySet._fill_cachec                    s,   g }|D ]‰ |  ‡ fdd„| jD ƒ¡ q|S )Nc                    s   i | ]	}|t ˆ |d ƒ“qS r/   rþ   r   r  r   r   Ú
<dictcomp>Ð  s    z=ValuesSearchQuerySet.post_process_results.<locals>.<dictcomp>)rH   rà   )r   rQ   rR   r   r  r   rX   Ì  s   z)ValuesSearchQuerySet.post_process_results)rê   rë   rì   rí   rC   rX   ró   r   r   rò   r   rß   ¿  s    rß   c                       s>   e Zd ZdZ‡ fdd„Zdd„ Zdd„ Zd‡ fd	d
„	Z‡  ZS )ÚRelatedSearchQuerySetzO
    A variant of the SearchQuerySet that can handle `load_all_queryset`s.
    c                    s"   t ƒ j|i |¤Ž i | _g | _d S r/   )rñ   r   Ú_load_all_querysetsr   r}   rò   r   r   r   Ú  s   
zRelatedSearchQuerySet.__init__c                 C   s`   || j v r| j |  |¡S zt| jj  ¡ }| |¡}| ¡ }| |¡W S  ty/   i  Y S w r/   )	r  r\   r   r   r   rY   rZ   Úload_all_querysetr   )r   rG   r_   r`   r@   rã   r   r   r   rJ   ß  s   

üz)RelatedSearchQuerySet._load_model_objectsc                 C   s   |   ¡ }||j|< |S )a(  
        Allows for specifying a custom ``QuerySet`` that changes how ``load_all``
        will fetch records for the provided model.

        This is useful for post-processing the results from the query, enabling
        things like adding ``select_related`` or filtering certain data.
        )r6   r  )r   rG   Úquerysetr„   r   r   r   r	  ñ  s   
z'RelatedSearchQuerySet.load_all_querysetNc                    s   t ƒ j|d�}| j|_|S rð   )rñ   r6   r  r’   rò   r   r   r6   ý  s   zRelatedSearchQuerySet._cloner/   )	rê   rë   rì   rí   r   rJ   r	  r6   ró   r   r   rò   r   r  Õ  s    r  )rÇ   r�   Ú	functoolsr   r   r   r   Úhaystack.backendsr   Úhaystack.constantsr   r   Úhaystack.exceptionsr   Úhaystack.inputsr	   r
   Úhaystack.utilsr   r   r   r5   ræ   rß   r  r   r   r   r   Ú<module>   s$        s*