a
    æý¼d.  ã                   @   sº   d dl Z d dlmZ d dlmZ d dlmZ d dlmZ d dlm	Z	 d dl
mZ d dl
mZ e  e¡Zed	œd
d„Zeedœdd„Zeedœdd„Zeeedœdd„Zdd„ Zdd„ ZdS )é    N)Údatetime©Úwraps)ÚBaseScheduler)Údb)Úsettings)Úformats©Útimezone)Úreturnc                   C   s   t  ttddƒ¡S )zOReturn the configured format for displaying datetimes in the Django admin viewsZAPSCHEDULER_DATETIME_FORMATzN j, Y, f:s a)r   Z
get_formatÚgetattrr   © r   r   úS/var/www/html/django/DPS/env/lib/python3.9/site-packages/django_apscheduler/util.pyÚget_dt_format   s    
ÿr   )Údtr   c                 C   s,   | rt jrt | ¡rt | ¡} t | tƒ ¡S )z1Get the datetime in the localized datetime format)r   ÚUSE_TZr
   Úis_awareÚ	localtimer   Zdate_formatr   ©r   r   r   r   Úget_local_dt_format   s    
r   c                 C   s<   | r8t jrt | ¡rt | ¡S t js8t | ¡r8t | ¡S | S )zµ
    Get the naive or aware version of the datetime based on the configured `USE_TZ` Django setting. This is also the
    format that Django uses to store datetimes internally.
    )r   r   r
   Úis_naiveÚ
make_awarer   Z
make_naiver   r   r   r   Úget_django_internal_datetime   s    

r   )r   Ú	schedulerr   c                 C   s"   | rt  | ¡rt j| |j dS | S )z„
    Make the datetime timezone aware (if necessary), using the same timezone as is currently configured for the
    scheduler.
    r	   )r
   r   r   )r   r   r   r   r   Úget_apscheduler_datetime,   s    r   c                    s   t ˆ ƒ‡ fdd„ƒ}|S )ay  
    This decorator can be used to wrap a database-related method so that it will be retried when a
    django.db.OperationalError or django.db.InterfaceError is encountered.

    The rationale is that these exceptions are usually raised when attempting to use an old database connection that
    the database backend has since closed. Closing the Django connection as well, and re-trying with a fresh connection,
    is usually sufficient to solve the problem.

    It is a reluctant workaround for users that persistently have issues with stale database connections (most notably:
    2006, 'MySQL server has gone away').

    The recommended approach is still to rather install a database connection pooler (like pgbouncer), to take care of
    database connection management for you, but the issue has been raised enough times by different individuals that a
    workaround is probably justified.

    CAUTION: any method that this decorator is applied to MUST be idempotent (i.e. the method can be retried a second
    time without any unwanted side effects). If your method performs any actions before the database exception is
    raised then those actions will be repeated. If you don't want that to happen then it would be best to handle the
    exception manually and call `db.close_old_connections()` in an appropriate fashion inside your own method instead.

    The following list of alternative workarounds were also considered:

    1. Calling db.close_old_connections() pre-emptively before the job store executes a DB operation: this would break
       Django's standard connection management. For example, if the `CONN_MAX_AGE` setting is set to 0, a new connection
       will be required for *every* database operation (as opposed to at the end of every *request* like in the Django
       standard). The database overhead, and associated performance penalty, that this approach would impose seem
       unreasonable. See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-CONN_MAX_AGE.

    2. Using a custom QuerySet or database backend that handles the relevant database exceptions automatically: this
       would be more convenient than having to decorate individual methods, but it would also break when a DB operation
       needs to be re-tried as part of an atomic transaction. See: https://github.com/django/django/pull/2740

    3. Pinging the database before each operation to see if it is still available: django-apscheduler used to make use
       of this approach (see: https://github.com/jcass77/django-apscheduler/blob/9ac06b33d19961da6c36d5ac814d4338beb11309/django_apscheduler/models.py#L16-L51).
       Injecting an additional database query, on an arbitrary schedule, seems like an unreasonable thing to do,
       especially considering that this would be unnecessary for users that already make use of a database connection
       pooler to manage their connections properly.
    c               
      st   zˆ | i |¤Ž}W n\ t jt jfyn } z<t dˆ j› d|› d¡ t  ¡  ˆ | i |¤Ž}W Y d }~n
d }~0 0 |S )NzDB error executing 'z' (z'). Retrying with a new DB connection...)r   ZOperationalErrorZInterfaceErrorÚloggerÚwarningÚ__name__Úclose_old_connections)ÚargsÚkwargsÚresultÚe©Úfuncr   r   Úfunc_wrapper_   s    ÿ$z3retry_on_db_operational_error.<locals>.func_wrapperr   ©r$   r%   r   r#   r   Úretry_on_db_operational_error7   s    (r'   c                    s   t ˆ ƒ‡ fdd„ƒ}|S )aU  
    A decorator that ensures that Django database connections that have become unusable, or are obsolete, are closed
    before and after a method is executed (see: https://docs.djangoproject.com/en/dev/ref/databases/#general-notes
    for background).

    This decorator is intended to be used to wrap APScheduler jobs, and provides functionality comparable to the
    Django standard approach of closing old connections before and after each HTTP request is processed.

    It only makes sense for APScheduler jobs that require database access, and prevents `django.db.OperationalError`s.
    c                     s2   t  ¡  zˆ | i |¤Ž}W t  ¡  n
t  ¡  0 |S )N)r   r   )r   r    r!   r#   r   r   r%   {   s
    z+close_old_connections.<locals>.func_wrapperr   r&   r   r#   r   r   o   s    	r   )Úloggingr   Ú	functoolsr   Zapscheduler.schedulers.baser   Údjangor   Zdjango.confr   Zdjango.utilsr   r
   Ú	getLoggerr   r   Ústrr   r   r   r   r'   r   r   r   r   r   Ú<module>   s   
8