ó
¸9—^c           @   sî   d  Z  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 d d l	 m
 Z
 d d l m Z m Z d d l m Z d d l m Z y d d l Z Wn e k
 rÍ e Z n Xe Z d e f d	 „  ƒ  YZ d S(
   s	  
Sync Media to S3
================

Django command that scans all files in your settings.MEDIA_ROOT and
settings.STATIC_ROOT folders and uploads them to S3 with the same directory
structure.

This command can optionally do the following but it is off by default:
* gzip compress any CSS and Javascript files it finds and adds the appropriate
  'Content-Encoding' header.
* set a far future 'Expires' header for optimal caching.
* upload only media or static files.
* use any other provider compatible with Amazon S3.
* set other than 'public-read' ACL.

Note: This script requires the Python boto library and valid Amazon Web
Services API keys.

Required settings.py variables:
AWS_ACCESS_KEY_ID = ''
AWS_SECRET_ACCESS_KEY = ''
AWS_BUCKET_NAME = ''

When you call this command with the `--renamegzip` param, it will add
the '.gz' extension to the file name. But Safari just doesn't recognize
'.gz' files and your site won't work on it! To fix this problem, you can
set any other extension (like .jgz) in the `SYNC_S3_RENAME_GZIP_EXT`
variable.

Command options are:
  -p PREFIX, --prefix=PREFIX
                        The prefix to prepend to the path on S3.
  --gzip                Enables gzipping CSS and Javascript files.
  --expires             Enables setting a far future expires header.
  --force               Skip the file mtime check to force upload of all
                        files.
  --filter-list         Override default directory and file exclusion
                        filters. (enter as comma separated line)
  --renamegzip          Enables renaming of gzipped files by appending '.gz'.
                        to the original file name. This way your original
                        assets will not be replaced by the gzipped ones.
                        You can change the extension setting the
                        `SYNC_S3_RENAME_GZIP_EXT` var in your settings.py
                        file.
  --invalidate          Invalidates the objects in CloudFront after uploading
                        stuff to s3.
  --media-only          Only MEDIA_ROOT files will be uploaded to S3.
  --static-only         Only STATIC_ROOT files will be uploaded to S3.
  --s3host              Override default s3 host.
  --acl                 Override default ACL settings ('public-read' if
                        settings.AWS_DEFAULT_ACL is not defined).

TODO:
 * Use fnmatch (or regex) to allow more complex FILTER_LIST rules.

iÿÿÿÿN(   t   List(   t   settings(   t   BaseCommandt   CommandError(   t   StringIO(   t   signalcommandt   Commandc           B   sÂ   e  Z d  Z d  Z d  Z d  Z d  Z d  Z d d d d d g Z d Z	 g  Z
 d
 Z d
 Z d Z d Z e Z d „  Z e d „  ƒ Z d „  Z d „  Z d „  Z d „  Z d „  Z d „  Z d „  Z RS(   t    s	   .DS_Stores   .svns   .hgs   .gits	   Thumbs.dbs   text/csss   application/javascripts   application/x-javascripts   text/javascripti    sS   Syncs the complete MEDIA_ROOT structure and files to S3 into the given bucket name.t   bucket_namec      
   C   sâ  t  t |  ƒ j | ƒ | j d d d d d t t d d ƒ d d	 ƒ| j d
 d d d d d ƒ| j d d d d t t d d ƒ d d ƒ| j d d d d t t d d ƒ d d ƒ| j d d d d d d t d d ƒ| j d d d d d d t d d ƒ| j d d d d d  d t d d! ƒ| j d" d d d d# d t d d$ ƒ| j d% d d& d d' d d d d( ƒ| j d) d d* d t d d d d+ ƒ| j d, d d- d d d d d d. ƒ| j d/ d d0 d d d d d d1 ƒd  S(2   Ns   -ps   --prefixt   destt   prefixt   defaultt   SYNC_S3_PREFIXR   t   helps(   The prefix to prepend to the path on S3.s   -ds   --dirt   dirs#   Custom static root directory to uses   --s3hostt   s3hostt   AWS_S3_HOSTs;   The s3 host (enables connecting to other providers/regions)s   --aclt   aclt   AWS_DEFAULT_ACLs   public-reads.   Enables to override default acl (public-read).s   --gzipt   actiont
   store_truet   gzips*   Enables gzipping CSS and Javascript files.s   --renamegzipt
   renamegzipsJ   Enables renaming of gzipped assets to have '.gz' appended to the filename.s	   --expirest   expiress,   Enables setting a far future expires header.s   --forcet   forces7   Skip the file mtime check to force upload of all files.s   --filter-listt   filter_listt   storesV   Override default directory and file exclusion filters. (enter as comma seperated line)s   --invalidatet
   invalidates0   Invalidates the associated objects in CloudFronts   --media-onlyt
   media_onlys,   Only MEDIA_ROOT files will be uploaded to S3s   --static-onlyt   static_onlys-   Only STATIC_ROOT files will be uploaded to S3(   t   superR   t   add_argumentst   add_argumentt   getattrR   t   False(   t   selft   parser(    (    sO   /tmp/pip-unpacked-wheel-a0M10Y/django_extensions/management/commands/sync_s3.pyR   l   sh    		c         O   sŸ  t  s t d ƒ ‚ n  t t d ƒ s5 t t d ƒ rD t d ƒ ‚ n t j |  _ t j |  _ t t d ƒ sz t d ƒ ‚ n t j s’ t d ƒ ‚ n  t j |  _ t t d ƒ s¼ t d	 ƒ ‚ n t j sÔ t d	 ƒ ‚ n  t t d
 d ƒ |  _	 t t d d ƒ |  _
 | d |  _ | d |  _ | d |  _ | d |  _ | d |  _ | d |  _ | d |  _ | d |  _ | d |  _ | d |  _ t t d |  j ƒ |  _ | d } | r½| j d ƒ |  _ n  | d |  _ | d |  _ |  j rø|  j røt d ƒ ‚ nf |  j rt j g |  _ nK |  j r.t j g |  _ n0 |  j rI|  j g |  _ n t j t j g |  _ |  j ƒ  |  j r~|  j ƒ  n  d GHd |  j GHd |  j GHd  S(    Ns>   Please install the 'boto' Python library. ($ pip install boto)t   AWS_ACCESS_KEY_IDt   AWS_SECRET_ACCESS_KEYse   Missing AWS keys from settings file.  Please supply both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.t   AWS_BUCKET_NAMEs]   Missing bucket name from settings file. Please add the AWS_BUCKET_NAME to your settings file.s    AWS_BUCKET_NAME cannot be empty.t
   MEDIA_ROOTs(   MEDIA_ROOT must be set in your settings.t   AWS_CLOUDFRONT_DISTRIBUTIONR   t   SYNC_S3_RENAME_GZIP_EXTs   .gzt	   verbosityR
   R   R   R   R   R   R   R   R   t   FILTER_LISTR   t   ,R   R   sM   Can't use --media-only and --static-only together. Better not use anything...s   %d files uploaded.s   %d files skipped.(   t   HAS_BOTOR   t   hasattrR   R%   R&   R'   R(   R!   R)   R*   R+   R
   t   do_gzipt   rename_gzipt
   do_expirest   do_forceR   t   DIRECTORIESR   t   default_aclR,   t   splitR   R   t   STATIC_ROOTt   sync_s3t   invalidate_objects_cft   upload_countt
   skip_count(   R#   t   argst   optionsR   (    (    sO   /tmp/pip-unpacked-wheel-a0M10Y/django_extensions/management/commands/sync_s3.pyt   handle®   s`     		
			
	c         C   s   t  j |  j |  j ƒ S(   s'   Return an open connection to CloudFront(   t   botot   connect_cloudfrontR%   R&   (   R#   (    (    sO   /tmp/pip-unpacked-wheel-a0M10Y/django_extensions/management/commands/sync_s3.pyt   open_cfø   s    c         C   s‘   |  j  s t d ƒ ‚ n  d } |  j ƒ  } |  j } g  t d t | ƒ | ƒ D] } | | | | !^ qL } x! | D] } | j |  j  | ƒ qp Wd S(   s8   Split the invalidation request in groups of 1000 objectssr   An object invalidation was requested but the variable AWS_CLOUDFRONT_DISTRIBUTION is not present in your settings.iè  i    N(   R)   R   RA   t   uploaded_filest   ranget   lent   create_invalidation_request(   R#   t   chunkt   connt   objst   it   chunkst   paths(    (    sO   /tmp/pip-unpacked-wheel-a0M10Y/django_extensions/management/commands/sync_s3.pyR9   ý   s    		6c         C   sr   |  j  ƒ  \ } } xY |  j D]N } xE t j | ƒ D]4 \ } } } |  j | | |  j | f | | | ƒ q2 Wq Wd S(   s7   Walk the media/static directories and syncs files to S3N(   t   open_s3R4   t   ost   walkt	   upload_s3R'   (   R#   t   buckett   keyt	   directoryt   roott   dirst   files(    (    sO   /tmp/pip-unpacked-wheel-a0M10Y/django_extensions/management/commands/sync_s3.pyR8     s    c         C   sH   t  ƒ  } t j d d d d d | ƒ } | j | ƒ | j ƒ  | j ƒ  S(   s   Gzip a given string.t   modet   wbt   compressleveli   t   fileobj(   R   R   t   GzipFilet   writet   closet   getvalue(   R#   t   st   zbuft   zfile(    (    sO   /tmp/pip-unpacked-wheel-a0M10Y/django_extensions/management/commands/sync_s3.pyt   compress_string  s
    	
c         C   s#   i  } |  j  r |  j  | d <n  | S(   s"   Return connection kwargs as a dictt   host(   R   (   R#   t   kwargs(    (    sO   /tmp/pip-unpacked-wheel-a0M10Y/django_extensions/management/commands/sync_s3.pyt   get_s3connection_kwargs#  s    	c         C   s|   t  j |  j |  j |  j ƒ   } y | j |  j ƒ } Wn) t  j j k
 rb | j	 |  j ƒ } n X| t  j
 j j | ƒ f S(   s.   Open connection to S3 returning bucket and key(   R?   t
   connect_s3R%   R&   Rd   t
   get_bucketR'   t	   exceptiont   S3ResponseErrort   create_buckett   s3RQ   t   Key(   R#   RG   RP   (    (    sO   /tmp/pip-unpacked-wheel-a0M10Y/django_extensions/management/commands/sync_s3.pyRL   *  s    c      	   C   sÒ  | \ } } } } t  j j | ƒ |  j k rP t  j j | ƒ |  j k rP | 2d  S| j t  j j ƒ sx | t  j j } n  xS| D]K}	 i  }
 |	 |  j k r  q n  t  j j | |	 ƒ } t  j j	 | ƒ rÍ q n  | t
 | ƒ } |  j rü d |  j | f } n  |  j sŸ| j | ƒ } | rŸt j t j | j d ƒ d d !Œ  } t j j t  j | ƒ j ƒ } | | k  rœ|  j d 7_ |  j d k r d | GHq q qœqŸn  |  j d k rºd | GHn  t j | ƒ d } | rà| |
 d <n
 d	 |
 d <t | d
 ƒ } t  j | j ƒ  ƒ j } | j ƒ  } |  j r®| d k r®| |  j k r®|  j  | ƒ } |  j! rod | |  j" f } n  d |
 d <|  j d k r«d | d t
 | ƒ d f GHq«q®n  |  j# r6d t$ j% j& t j' t j j( ƒ  t j) d d ƒ j* ƒ  ƒ ƒ |
 d <d d! |
 d <|  j d k r6d |
 d GHd |
 d GHq6n  y, | | _+ | j, | |
 d t- d |  j. ƒWn< t/ j0 j1 k
 r†} d | GHn: t2 k
 r } | GH‚  n  X|  j3 d 7_3 |  j4 j5 | ƒ | j6 ƒ  q Wd  S("   Ns   %s/%ss   %a, %d %b %Y %H:%M:%S %Zi    i   i   s6   File %s hasn't been modified since last being uploadeds   Uploading %s...s   Content-Types   application/octet-streamt   rbi   s   %s.%sR   s   Content-Encodings   	gzipped: %dk to %dks   %s GMTt   daysim  i   t   Expiress
   max-age %di  i   s   Cache-Controls   	expires: %ss   	cache-control: %st   replacet   policys
   Failed: %siÚ  i€Q i€3ái gÂ(7   RM   t   patht   basenameR,   t   dirnameR4   t   endswitht   sept   joint   isdirRD   R
   R3   t   get_keyt   datetimet   timet   strptimet   last_modifiedt   utcfromtimestampt   statt   st_mtimeR;   R+   t	   mimetypest
   guess_typet   opent   fstatt   filenot   st_sizet   readR0   t   GZIP_CONTENT_TYPESRa   R1   R*   R2   t   emailt   Utilst
   formatdatet   mktimet   nowt	   timedeltat	   timetuplet   namet   set_contents_from_stringt   TrueR5   R?   Rg   t   S3CreateErrort	   ExceptionR:   RB   t   appendR\   (   R#   t   argRs   t   namesRT   RP   RQ   R   t   root_dirt   filet   headerst   filenamet   file_keyt   s3_keyt   s3_datetimet   local_datetimet   content_typet   file_objt	   file_sizet   filedatat   e(    (    sO   /tmp/pip-unpacked-wheel-a0M10Y/django_extensions/management/commands/sync_s3.pyRO   6  s€    6			
		
&	B	(   s   text/csss   application/javascripts   application/x-javascripts   text/javascript(   t   __name__t
   __module__R%   R&   R'   R)   R*   R4   R,   R‡   RB   R:   R;   R   R<   R‘   t   can_import_settingsR   R   R>   RA   R9   R8   Ra   Rd   RL   RO   (    (    (    sO   /tmp/pip-unpacked-wheel-a0M10Y/django_extensions/management/commands/sync_s3.pyR   R   s4      	BJ						(   t   __doc__Ry   Rˆ   R   R€   RM   Rz   t   typingR    t   django.confR   t   django.core.management.baseR   R   t   sixR   t"   django_extensions.management.utilsR   R?   t   ImportErrorR"   R.   R‘   R   (    (    (    sO   /tmp/pip-unpacked-wheel-a0M10Y/django_extensions/management/commands/sync_s3.pyt   <module>:   s"   
