Trent Mick avatar Trent Mick committed 0ad60ea

'pics co -p|--permission' option

Comments (0)

Files changed (3)

lib/picslib/shell.py

     @cmdln.option("-s", "--size", default="original",
         help="Specify a photo size to download. Valid values are 'small', "
              "'medium', and 'original' (default)")
+    @cmdln.option("-p", "--permission", default="all",
+        help="Photo permissions to retrieve. Valid values:\n"
+             "  'all' (default) all photos,\n"
+             "  'family' only photos that family would see,\n"
+             "  'friend' only photos that friends would see,\n"
+             "  'public' only public photos")
     @cmdln.alias("co")
     def do_checkout(self, subcmd, opts, url, path=None):
         """${cmd_name}: Checkout a working copy of photos
             t = datetime.datetime.strptime(opts.base_date_str, "%Y-%m-%d")
             base_date = datetime.date(t.year, t.month, t.day)
         size = opts.size or "original"
-        wc = WorkingCopy.create(path, repo_type, repo_user, base_date, size)
+        wc = WorkingCopy.create(path, repo_type, repo_user, base_date, size,
+            opts.permission)
         wc.update()
 
     @cmdln.alias("ls")
     @cmdln.option("-t", "--tags", action="store_true", default=False,
                   help="list tags as well")
     def do_list(self, subcmd, opts, *target):
-        """${cmd_name}: List photo entries. 
+        """${cmd_name}: List photo entries.
 
         ${cmd_usage}
         ${cmd_option_list}
 
     def do_info(self, subcmd, opts, *target):
         """${cmd_name}: Display info about a photo.
-        
+
         Currently this retrieves photo info from flickr rather than using
         the local cache. This is because not *all* photo data is currently
         being tracked by pics.
     #TODO: open the photo in a local register image app or viewer
     #def do_open(self, subcmd, opts, target=None):
     #    raise NotImplementedError("'open' command not yet implemented")
-        
+
     @cmdln.alias('b')
     def do_browse(self, subcmd, opts, target=None):
         """${cmd_name}: Open the given photo or dir URL in your browser.
         ${cmd_option_list}
         """
         raise NotImplementedError("commit")
-

lib/picslib/utils.py

         return datetime.date(now.year-1, m, 1)
     else:
         return datetime.date(now.year, now.month - (N-1), 1)
-    
+
 def timestamp_from_datetime(dt):
     import calendar
     return calendar.timegm(dt.timetuple())
         return r
     else:
         return a
-    
+
 # Recipe: relpath (0.2)
 def relpath(path, relto=None):
     """Relativize the given path to another (relto).
         else:
             path = parts[0]
             allparts.insert(0, parts[1])
-    allparts = [p for p in allparts if p] # drop empty strings 
+    allparts = [p for p in allparts if p] # drop empty strings
     return allparts
 
 
 
 def _walk(top, topdown=True, onerror=None, follow_symlinks=False):
     """A version of `os.walk()` with a couple differences regarding symlinks.
-    
+
     1. follow_symlinks=False (the default): A symlink to a dir is
        returned as a *non*-dir. In `os.walk()`, a symlink to a dir is
        returned in the *dirs* list, but it is not recursed into.
     2. follow_symlinks=True: A symlink to a dir is returned in the
        *dirs* list (as with `os.walk()`) but it *is conditionally*
        recursed into (unlike `os.walk()`).
-       
+
        A symlinked dir is only recursed into if it is to a deeper dir
        within the same tree. This is my understanding of how `find -L
        DIR` works.
                 # not:
                 #   script -r --include="*.py" DIR
                 if recursive and _should_include_path(path, [], excludes):
-                    for dirpath, dirnames, filenames in _walk(path, 
+                    for dirpath, dirnames, filenames in _walk(path,
                             follow_symlinks=follow_symlinks):
                         dir_indeces_to_remove = []
                         for i, dirname in enumerate(dirnames):
         else:
             path = parts[0]
             allparts.insert(0, parts[1])
-    allparts = [p for p in allparts if p] # drop empty strings 
+    allparts = [p for p in allparts if p] # drop empty strings
     return allparts
 
 
     # - Add 'c-string' style.
     # - Add _escaped_html_from_text() with a similar call sig.
     import re
-    
+
     if isinstance(escapes, basestring):
         if escapes == "eol":
             escapes = {'\r\n': "\\r\\n\r\n", '\n': "\\n\n", '\r': "\\r\r"}
 def one_line_summary_from_text(text, length=78,
         escapes={'\n':"\\n", '\r':"\\r", '\t':"\\t"}):
     r"""Summarize the given text with one line of the given length.
-    
+
         "text" is the text to summarize
         "length" (default 78) is the max length for the summary
         "escapes" is a mapping of chars in the source text to
         return indentstr.join(lines)
     else:
         return indentstr + indentstr.join(lines)
-
-

lib/picslib/workingcopy.py

 #TODO: 'fetch' in the name of internal methods that call to the remote server
 class WorkingCopy(object):
     """API for a pics working copy directory.
-    
+
     Usage:
         # Create a new working copy directory (used by `pics co`).
         wc = WorkingCopy.create(...)
-        
+
         # Or, for an existing working copy.
         wc = WorkingCopy(base_dir)
     """
         self.base_dir = normpath(base_dir)
         self.fs = FileSystem(log.debug)
         self._cache = {}
-        
+
         db_path = self._db_path_from_base_dir(self.base_dir)
         if exists(db_path):  # Otherwise `.create()` will set `self.db`.
             self.db = Database(db_path)
 
     @classmethod
-    def create(cls, base_dir, ilk, user, base_date=None, size="original"):
+    def create(cls, base_dir, ilk, user, base_date=None, size="original",
+            permission="all"):
         """Create a working copy and return a `WorkingCopy` instance for it.
-        
+
         @param base_dir {str} The base directory for the working copy.
         @param ilk {str} The type of the pics repo. Currently only
             "flickr" is supported.
             that the former two mean depends on the pics repository. Default
             is "original".
             TODO: specify the sizes for flickr.
+        @param permission {str} Photo permissions to retrieve.
+            Valid values:
+                'all' (default) all photos,
+                'family' only photos that family would see,
+                'friend' only photos that friends would see,
+                'public' only public photos
         @returns {WorkingCopy} The working copy instance.
         """
         # Sanity checks.
         assert ilk == "flickr", "unknown pics repo ilk: %r" % ilk
         assert isinstance(base_date, (type(None), datetime.date))
         assert size in ("small", "medium", "original")
+        assert permission in ("all", "family", "friend", "public")
         if exists(base_dir):
             raise PicsError("cannot create working copy: `%s' exists" % base_dir)
-        
+
         self = cls(base_dir)
 
         # Create base structure.
         d = join(self.base_dir, ".pics")
         self.fs.mkdir(d, hidden=True)
         open(join(d, "version"), 'w').write(self.VERSION+'\n')
-        
+
         # Main working copy database.
         db_path = self._db_path_from_base_dir(self.base_dir)
         self.db = Database(db_path)
             self.db.set_meta("ilk", ilk)
             self.db.set_meta("user", user)
             self.db.set_meta("size", size)
+            self.db.set_meta("permission", permission)
             if base_date:
                 self.db.set_meta("base_date", base_date)
 
             #TODO: cache this auth token in the pics user data dir
             self._api_cache.get_auth_token("read")
         return self._api_cache
-    _api_cache = None 
+    _api_cache = None
 
     # Getter and setter for `last-update`, a `datetime.datetime` field for
     # the latest photo update sync'd to the working copy.
                 self.fs.mkdir(dir)
             if not exists(pics_dir):
                 self.fs.mkdir(pics_dir, hidden=True)
-        
+
             # Get the photo itself.
             #TODO: add a reporthook for progressbar (unless too quick to bother)
             #TODO: handle ContentTooShortError (py2.5)
             else:
                 comments = None
             self._save_photo_data(d, id, info, comments=comments)
-            
+
         #print "... %s" % id
         #print "originalsecret: %s <- %s" % (info.get("originalsecret"), local_info.get("originalsecret"))
         #print "secret: %s <- %s" % (info.get("secret"), local_info.get("secret"))
 
     def _get_photo_data(self, datedir, id, type):
         """Read and return the given photo data.
-        
+
         Photo data is one or more XML files in the photos dirs ".pics" subdir.
-        
+
         @param datedir {str} A datedir of the form YYYYMM in which the photo
             lives.
         @param id {int} The photo's id.
 
     def _local_photo_dirs_and_ids_from_target(self, target):
         """Yield the identified photos from the given target.
-        
+
         Yields 2-tuples: <pics-wc-dir>, <photo-id>
         """
         if isdir(target):
 
     def _photo_data_from_local_path(self, path):
         """Yield photo data for the given path.
-        
+
         If the given path does not identify a photo then the following
         is returned:
             {"id": path}
     def update(self, dry_run=False):
         #TODO: when support local edits, need to check for conflicts
         #      and refuse to update if hit one
-        
+
         # Determine start date from which we need to update.
         last_update = self.get_last_update()
         if last_update:
         log.debug("update: min_date=%s (%s)", min_date, d)
 
         with self.db.connect(not dry_run) as cu:
+            permission = self.db.get_meta("permission", cu=cu)
+
             # Gather all updates to do.
             # After commiting this it is okay if this script is aborted
             # during the actual update: a subsequent 'pics up' will
                 extras="last_update")
             for elem in recents:
                 id = elem.get("id")
+                isfamily = bool(int(elem.get("isfamily")))
+                isfriend = bool(int(elem.get("isfriend")))
+                ispublic = bool(int(elem.get("ispublic")))
+                if permission == "all":
+                    pass
+                elif ispublic:
+                    pass
+                elif permission == "family" and isfamily:
+                    pass
+                elif permission == "friend" and isfriend:
+                    pass
+                else:
+                    continue
                 cu.execute("INSERT OR REPLACE INTO pics_update VALUES (?)", (id,))
             if not dry_run:
                 cu.connection.commit()
     def _mode_str_from_photo_dict(self, photo):
         """Photo mode string:
             'pfF' for ispublic, isfriend, isfamily
-        
+
         TODO: Would be nice to have iscontact, something for copyright?,
         taken date of photo, date made a fav (if available)
         """
         #TODO: error handling?
         with self.connect(True) as cu:
             cu.executescript(self.schema)
-            cu.execute("INSERT INTO pics_meta(key, value) VALUES (?, ?)", 
+            cu.execute("INSERT INTO pics_meta(key, value) VALUES (?, ?)",
                 ("version", self.VERSION))
 
     def reset(self, backup=True):
 
     def get_meta(self, key, default=None, cu=None):
         """Get a value from the meta table.
-        
+
         @param key {str} The meta key.
         @param default {str} Default value if the key is not found in the db.
         @param cu {sqlite3.Cursor} An existing cursor to use.
             if row is None:
                 return default
             return row[0]
-    
+
     def set_meta(self, key, value, cu=None):
         """Set a value into the meta table.
-        
+
         @param key {str} The meta key.
         @param default {str} Default value if the key is not found in the db.
         @param cu {sqlite3.Cursor} An existing cursor to use.
         @returns {str} The value in the database for this key, or `default`.
         """
         with self.connect(True, cu=cu) as cu:
-            cu.execute("INSERT INTO pics_meta(key, value) VALUES (?, ?)", 
+            cu.execute("INSERT INTO pics_meta(key, value) VALUES (?, ?)",
                 (key, value))
 
     def del_meta(self, key):
         """Delete a key/value pair from the meta table.
-        
+
         @param key {str} The meta key.
         """
         with self.connect(True) as cu:
 def _photo_last_update_from_info(info):
     lastupdate = info.find("dates").get("lastupdate")
     return datetime.datetime.utcfromtimestamp(float(lastupdate))
-            
+
 def _photo_num_comments_from_info(info):
     """The number of comments from the <photo> elem."""
     comments = info.findtext("comments")
 
 def _find_wc_base_dir(path):
     """Determine the working copy base dir from the given path.
-    
+
     If "path" isn't specified, the CWD is used. Returns None if no
     pics working copy base dir could be found.
     """
         return md5(f.read()).hexdigest()
     finally:
         f.close()
-
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.