home · contact · privacy
Add foreign key restraints, expand and fix tests, add deletion and forking.
[misc] / plomlib.py
index 6a13f05f4b20b148be7b8464a60f268ad9464626..2d92977a4b967b891c7052e7250b75a5f42f5de9 100644 (file)
@@ -1,5 +1,7 @@
 import os
-from http.server import BaseHTTPRequestHandler 
+from http.server import BaseHTTPRequestHandler
+from http.cookies import SimpleCookie
+import json
 
 
 class PlomException(Exception):
@@ -27,47 +29,60 @@ class PlomDB:
     def backup(self):
         import shutil
         from datetime import datetime, timedelta
+        if not os.path.exists(self.db_file):
+            return
+
         # collect modification times of numbered .bak files
+        # print('DEBUG BACKUP')
         bak_prefix = f'{self.db_file}.bak.'
-        backup_dates = []
-        i = 0
-        bak_as = f'{bak_prefix}{i}'
-        while os.path.exists(bak_as):
-            mod_time = os.path.getmtime(bak_as)
-            backup_dates += [str(datetime.fromtimestamp(mod_time))]
-            i += 1
-            bak_as = f'{bak_prefix}{i}'
+        # backup_dates = []
+        mtimes_to_paths = {}
+        for path in [path for path in os.listdir(os.path.dirname(bak_prefix))
+                     if path.startswith(os.path.basename(bak_prefix))]:
+            path = os.path.dirname(bak_prefix) + f'/{path}'
+            mod_time = os.path.getmtime(path)
+            # print(f'DEBUG pre-exists: {path} {mod_time}')
+            mtimes_to_paths[str(datetime.fromtimestamp(mod_time))] = path
+
+        # for mtime in sorted(mtimes_to_paths.keys()):
+        #     print(f'DEBUG mtimes_to_paths: {mtime}:{mtimes_to_paths[mtime]}')
 
         # collect what numbered .bak files to save: the older, the fewer; for each
         # timedelta, keep the newest file that's older
         ages_to_keep = [timedelta(minutes=4**i) for i in range(0, 8)]
-        now = datetime.now() 
-        to_save = []
+        # print(f'DEBUG ages_to_keep: {ages_to_keep}')
+        now = datetime.now()
+        to_save = {}
         for age in ages_to_keep:
-            limit = now - age 
-            for i, date in enumerate(reversed(backup_dates)):
-                if datetime.strptime(date, '%Y-%m-%d %H:%M:%S.%f') < limit:
-                    unreversed_i = len(backup_dates) - i - 1
-                    if unreversed_i not in to_save:
-                        to_save += [unreversed_i]
+            limit = now - age
+            for mtime in reversed(sorted(mtimes_to_paths.keys())):
+                # print(f'DEBUG checking if {mtime} < {limit} ({now} - {age})')
+                if len(mtime) < 20:
+                    mtime_test = mtime + '.000000'
+                else:
+                    mtime_test = mtime
+                if datetime.strptime(mtime_test, '%Y-%m-%d %H:%M:%S.%f') < limit:
+                    # print('DEBUG it is, adding!')
+                    to_save[mtime] = mtimes_to_paths[mtime]
                     break
 
-        # remove redundant backup files 
-        j = 0
-        for i in to_save:
-            if i != j:
-                source = f'{bak_prefix}{i}'
-                target = f'{bak_prefix}{j}'
+        for path in [path for path in mtimes_to_paths.values()
+                     if path not in to_save.values()]:
+            # print(f'DEBUG removing {path} cause not in to_save')
+            os.remove(path)
+
+        i = 0
+        for mtime in sorted(to_save.keys()):
+            source = to_save[mtime]
+            target = f'{bak_prefix}{i}'
+            # print(f'DEBUG to_save {source} -> {target}')
+            if source != target:
                 shutil.move(source, target)
-            j += 1
-        for i in range(j, len(backup_dates)):
-            try:
-                os.remove(f'{bak_prefix}{i}')
-            except FileNotFoundError:
-                pass
+            i += 1
 
-        # put copy of current state at end of bak list 
-        shutil.copy(self.db_file, f'{bak_prefix}{j}')
+        # put copy of current state at end of bak list
+        # print(f'DEBUG saving current state to {bak_prefix}{i}')
+        shutil.copy(self.db_file, f'{bak_prefix}{i}')
 
     def write_text_to_db(self, text, mode='w'):
         self.lock()
@@ -77,20 +92,51 @@ class PlomDB:
         self.unlock()
 
 
-class PlomHandler(BaseHTTPRequestHandler): 
+class PlomHandler(BaseHTTPRequestHandler):
     homepage = '/'
     html_head = '<!DOCTYPE html>\n<html>\n<meta charset="UTF-8">'
     html_foot = '</body>\n</html>'
-    
+
     def fail_400(self, e):
-        self.send_HTML(f'ERROR: {e}', 400)
+        self.send_HTML(f'ERROR BAR: {e}', 400)
 
     def send_HTML(self, html, code=200):
         self.send_code_and_headers(code, [('Content-type', 'text/html')])
         self.wfile.write(bytes(f'{self.html_head}\n{html}\n{self.html_foot}', 'utf-8'))
 
+    def ensure_cookies_to_set(self):
+        if not hasattr(self, 'cookies_to_set'):
+            self.cookies_to_set = []
+
+    def set_cookie(self, cookie_name, cookie_path, cookie_db):
+        self.ensure_cookies_to_set()
+        cookie = SimpleCookie()
+        cookie[cookie_name] = json.dumps(cookie_db)
+        cookie[cookie_name]['path'] = cookie_path
+        self.cookies_to_set += [cookie]
+
+    def unset_cookie(self, cookie_name, cookie_path):
+        self.ensure_cookies_to_set()
+        cookie = SimpleCookie()
+        cookie[cookie_name] = ''
+        cookie[cookie_name]['path'] = cookie_path
+        cookie[cookie_name]['expires'] = 'Thu, 01 Jan 1970 00:00:00 GMT'
+        self.cookies_to_set += [cookie]
+
+    def get_cookie_db(self, cookie_name):
+        cookie_db = {}
+        if 'Cookie' in self.headers:
+            cookie = SimpleCookie(self.headers['Cookie'])
+            if cookie_name in cookie:
+                cookie_db = json.loads(cookie[cookie_name].value)
+        return cookie_db
+
     def send_code_and_headers(self, code, headers=[]):
+        self.ensure_cookies_to_set()
         self.send_response(code)
+        for cookie in self.cookies_to_set:
+            for morsel in cookie.values():
+                self.send_header('Set-Cookie', morsel.OutputString())
         for fieldname, content in headers:
             self.send_header(fieldname, content)
         self.end_headers()
@@ -98,6 +144,12 @@ class PlomHandler(BaseHTTPRequestHandler):
     def redirect(self, url='/'):
         self.send_code_and_headers(302, [('Location', url)])
 
+    def try_do(self, do_method):
+        try:
+            do_method()
+        except PlomException as e:
+            self.fail_400(e)
+
 
 
 def run_server(port, handler_class):