Commits

David Warburton  committed cdcccb8

Collision detection.

  • Participants
  • Parent commits a76a502

Comments (0)

Files changed (1)

 import json, os, threading, re
 import urllib, base64, urllib2
 
+from datetime import tzinfo, timedelta, datetime
+from dateutil import parser
+
 try:
     import ssl
 except Exception, e:
     admin_url_template = "https://%s:%s@%s.myshopify.com/admin"
     use_ssl = True
 
+PACKAGE_NAME = 'Shopify'
+
 def load():
-    global stores, current_store, current_theme
+    global stores, current_store, current_theme, cache_data, cache_data_file
     store_template = """{
     "Human Readable Shop Name":{
         "API_KEY": "API-KEY-HERE",
 }
 """
     store_file = os.path.join(sublime.packages_path(), 'User', 'ShopifyStores.json')
+    cache_data_file = os.path.join(sublime.packages_path(), PACKAGE_NAME, 'cache_data.json')
+    cache_data = {}
+    if (os.path.exists(cache_data_file)):
+        with open(cache_data_file) as f:
+            cache_data = json.load(f)
 
     stores = None
 
         self.themes = json.load(urllib.urlopen("%s/themes.json" % self.admin_url))['themes']
         return self.themes
 
-    def download_assets(self, theme_id):
+    def refresh_assets(self, theme_id):
+        if (not hasattr(self, 'themes')):
+            self.download_themes()
+
         theme           = [t for t in self.themes if t['id'] == theme_id][0]
         assets_url      = "%s/themes/%s/assets.json" % (self.admin_url, theme_id)
         all_assets      = json.load(urllib.urlopen(assets_url))['assets']
 
         return theme['assets']
     
-    def pull_asset(self, key, theme_id):
+    def pull_asset_data(self, key, theme_id):
         asset_url = "%s/themes/%s/assets.json?asset[key]=%s" % (self.admin_url, theme_id, key)
         return json.load(urllib.urlopen(asset_url))['asset']
+    
+    def download_asset(self, asset_index):
+        key             = current_theme['assets'][asset_index]['key']
+        key1, key2      = key.split('/')
+        asset_file_name = os.path.join(self.name, str(current_theme['id']), key1, key2)
+        asset_full_name = os.path.join(cache_root(), asset_file_name)
+
+        save_timestamp_to_cache_data(asset_file_name)
+    
+        sublime.status_message('Downloading asset')
+        asset = current_store.pull_asset_data(key, theme_id = current_theme['id'])
+        if ('attachment' in asset.keys()):
+            value = base64.b64decode(asset['attachment'])
+        else:
+            value = asset['value']
+
+        if ( not os.path.exists(os.path.dirname(asset_full_name))):
+            os.makedirs(os.path.dirname(asset_full_name))
+
+        with open(asset_full_name, 'wb') as f:
+            f.write(value)
+        sublime.status_message('')
+        return asset_full_name
 
     def push_asset(self, key, theme_id, value):
         if (use_ssl):
             current_theme = current_store.themes[picked]
 
             sublime.status_message('Downloading list of assets')
-            all_assets = current_store.download_assets(theme_id = current_theme['id'])
+            all_assets = current_store.refresh_assets(theme_id = current_theme['id'])
             sublime.status_message('')
 
             self.window.run_command('shopify_show_assets')
         def on_asset_select(picked):
             if picked == -1:
                 return
-            key             = current_theme['assets'][picked]['key']
-            key1, key2      = key.split('/')
-            asset_file_name = os.path.join(current_store.name, str(current_theme['id']), key1, key2)
-            root            = os.path.join(sublime.packages_path(),'Shopify', 'cache')
-            asset_full_name = os.path.join(root,asset_file_name)
-
-            sublime.status_message('Downloading asset')
-            asset = current_store.pull_asset(key, theme_id = current_theme['id'])
-            if ('attachment' in asset.keys()):
-                value = base64.b64decode(asset['attachment'])
-            else:
-                value = asset['value']
-
-            if ( not os.path.exists(os.path.dirname(asset_full_name))):
-                os.makedirs(os.path.dirname(asset_full_name))
-
-            with open(asset_full_name, 'wb') as f:
-                f.write(value)
-            sublime.status_message('')
+            asset_full_name = current_store.download_asset(picked)
             self.window.open_file(asset_full_name)
 
         self.window.show_quick_panel(commands, on_asset_select)
 
+class UTC(tzinfo):
+    """UTC"""
+
+    def utcoffset(self, dt):
+        return timedelta(0)
+
+    def tzname(self, dt):
+        return "UTC"
+
+    def dst(self, dt):
+        return timedelta(0)
+
+def save_timestamp_to_cache_data(asset_file_name):
+    cache_data[asset_file_name] = {'retrieved_at': datetime.utcnow().replace(tzinfo = UTC()).isoformat() }
+    with open(cache_data_file, 'w') as f:
+        json.dump(cache_data, f)
+
+def cache_root():
+    return os.path.join(sublime.packages_path(),PACKAGE_NAME,'cache')
+
 class ShopifyUploadOnSave(sublime_plugin.EventListener):
     def on_post_save(self, view):
-        root = os.path.join(sublime.packages_path(),'Shopify','cache')
-        relpath = os.path.relpath(view.file_name(),root)
+        relpath = os.path.relpath(view.file_name(),cache_root())
         if relpath[0:2] != '..':
+            pusher = ShopifyPusher( relpath )
             sublime.status_message('Uploading asset')
-            store_name, theme_id, asset_type, asset_name = relpath.split(os.sep)
-            key = "%s/%s" %(asset_type, asset_name)
-            pusher = ShopifyPusher(store_name, theme_id, key, view.file_name())
             pusher.start()
         elif (os.path.basename(view.file_name()) == 'ShopifyStores.json'):
             load()
 
 class ShopifyPusher(threading.Thread):
-    def __init__(self, store_name, theme_id, key, filename):
-        self.store_name = store_name
-        self.theme_id = theme_id
-        self.key = key
-        self.filename = filename
+    def __init__(self, relpath):
+        self.relpath = relpath
+        self.store_name, theme_id, asset_type, asset_name = relpath.split(os.sep)
+        self.theme_id = int(theme_id)
+        self.key = "%s/%s" %(asset_type, asset_name)
+        self.filename = os.path.join(cache_root(), relpath)
         self.result = None
         threading.Thread.__init__(self)
 
     def run(self):
         store = [s for s in stores if s.name == self.store_name][0]
+        store.refresh_assets(theme_id = self.theme_id)
+        theme = [t for t in store.themes if t['id'] == self.theme_id][0]
+        asset = [a for a in theme['assets'] if a['key'] == self.key][0]
+        
+        mine = parser.parse(cache_data[self.relpath]['retrieved_at'])
+        theirs = parser.parse(asset['updated_at'])
+        if (theirs > mine):
+            #uhoh, we would clobber it
+            sublime.set_timeout(lambda:sublime.status_message('Upload aborted, server has more recent version.'),0)
+            return
 
         with open(self.filename, 'rb') as f:
             value = f.read()
         success = store.push_asset(self.key, self.theme_id, value)
         if (success):
             sublime.set_timeout(lambda:sublime.status_message("Upload to %s is successful" % store.display_name),0)
+            save_timestamp_to_cache_data(self.relpath)
         else:
             sublime.set_timeout(lambda:sublime.status_message("Upload to %s has failed" % store.display_name),0)
         self.result = True