# __init__.py

#

Copyright (C) 2010 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

    import datetime
    import googlecl
    import googlecl.base
    import logging

    service_name = __name__.split('.')[-1]
    LOGGER_NAME = __name__
    SECTION_HEADER = service_name.upper()

    LOG = logging.getLogger(LOGGER_NAME)

#

Makes the given URL for a picasa image point to the download.

    def make_download_url(url):

#

      return url[:url.rfind('/')+1]+'d'+url[url.rfind('/'):]

#

    def _map_access_string(access_string, default_value='private'):
      if not access_string:
        return default_value

#

It seems to me that 'private' is less private than 'protected' but I'm going with what Picasa seems to be using.

      access_string_mappings = {'public': 'public',
                                'private': 'protected',
                                'protected': 'private',
                                'draft': 'private',
                                'hidden': 'private',
                                'link': 'private'}
      try:
        return access_string_mappings[access_string]
      except KeyError:
        import re
        if access_string.find('link') != -1:
          return 'private'
      return default_value

#

    class PhotoEntryToStringWrapper(googlecl.base.BaseEntryToStringWrapper):
      caption = googlecl.base.BaseEntryToStringWrapper.summary

#

The distance to the subject.

      @property
      def distance(self):

#

        return self.entry.exif.distance.text

#

Exposure value, if possible to calculate

      @property
      def ev(self):

#

        try:

#

Using the equation for EV I found on Wikipedia...

          N = float(self.fstop)
          t = float(self.exposure)
          import math       # import math if fstop and exposure work

#

str() actually "rounds" floats. Try repr(3.3) and print 3.3

          ev_long_str = str(math.log(math.pow(N,2)/t, 2))
          dec_point = ev_long_str.find('.')

#

In the very rare case that there is no decimal point:

          if dec_point == -1:

#

Technically this can return something like 10000, violating our desired precision. But not likely.

            return ev_long_str
          else:

#

return value to 1 decimal place

            return ev_long_str[0:dec_point+2]
        except Exception:

#

Don't really care what goes wrong -- result is the same.

          return None

#

The exposure time used.

      @property
      def exposure(self):

#

        return self.entry.exif.exposure.text
      shutter = exposure
      speed = exposure

#

Boolean value indicating whether the flash was used.

      @property
      def flash(self):

#

        return self.entry.exif.flash.text

#

The focal length used.

      @property
      def focallength(self):

#

        return self.entry.exif.focallength.text

#

The fstop value used.

      @property
      def fstop(self):

#

        return self.entry.exif.fstop.text

#

The unique image ID for the photo.

      @property
      def imageUniqueID(self):

#

        return self.entry.exif.imageUniqueID.text
      id = imageUniqueID

#

The iso equivalent value used.

      @property
      def iso(self):

#

        return self.entry.exif.iso.text

#

The make of the camera used.

      @property
      def make(self):

#

        return self.entry.exif.make.text

#

The model of the camera used.

      @property
      def model(self):

#

        return self.entry.exif.model.text

#

Tags / keywords or labels.

      @property
      def tags(self):

#

        tags_text = self.entry.media.keywords.text
        tags_text = tags_text.replace(', ', ',')
        tags_list = tags_text.split(',')
        return self.intra_property_delimiter.join(tags_list)
      labels = tags
      keywords = tags

#

The date/time the photo was taken.

      @property
      def time(self):

#

Represented as the number of milliseconds since January 1st, 1970. Note: The value of this element should always be identical to the value of the .

        return self.entry.exif.time.text
      when = time

#

Overload from base.EntryToStringWrapper to use make_download_url URL to the original uploaded image, suitable for downloading from.

      @property
      def url_download(self):

#

        return make_download_url(self.url_direct)

#

    class AlbumEntryToStringWrapper(googlecl.base.BaseEntryToStringWrapper):

#

Access level of the album, one of "public", "private", or "unlisted".

      @property
      def access(self):

#

Convert values to ones the user selects on the web

        txt = self.entry.access.text
        if txt == 'protected':
          return 'private'
        if txt == 'private':
          return 'anyone with link'
        return txt
      visibility = access

#

Location of the album (where pictures were taken).

      @property
      def location(self):

#

        return self.entry.location.text
      where = location

#

When the album was published/uploaded in local time.

      @property
      def published(self):

#

        date = datetime.datetime.strptime(self.entry.published.text,
                                          googlecl.calendar.date.QUERY_DATE_FORMAT)
        date = date - googlecl.calendar.date.get_utc_timedelta()
        return date.strftime('%Y-%m-%dT%H:%M:%S')
      when = published

#

###### ######################################################################### <span id="each-of-the-following-run*-functions-execute-a-particular-task.

keyword-arguments: -client:-client-to-the-service-being-used. -options:-contains-all-attributes-required-to-perform-the-task -args:-additional-arguments-passed-in-on-the-command-line,-may-or-may-not-be -------required" href="each-of-the-following-run*-functions-execute-a-particular-task.

keyword-arguments: -client:-client-to-the-service-being-used. -options:-contains-all-attributes-required-to-perform-the-task -args:-additional-arguments-passed-in-on-the-command-line,-may-or-may-not-be -------required"> Each of the following run* functions execute a particular task.

Keyword arguments: client: Client to the service being used. options: Contains all attributes required to perform the task args: Additional arguments passed in on the command line, may or may not be required

    def _run_create(client, options, args):

#

Paths to media might be in options.src, args, both, or neither. But both are guaranteed to be lists.

      media_list = options.src + args

      album = client.create_album(title=options.title, summary=options.summary,
                                  access=options.access, date=options.date)
      if media_list:
        client.InsertMediaList(album, media_list=media_list,
                               tags=options.tags)
      LOG.info('Created album: %s' % album.GetHtmlLink().href)

#

    def _run_delete(client, options, args):
      if options.query or options.photo:
        entry_type = 'media'
        search_string = options.query
      else:
        entry_type = 'album'
        search_string = options.title

      titles_list = googlecl.build_titles_list(options.title, args)
      entries = client.build_entry_list(titles=titles_list,
                                        query=options.query,
                                        photo_title=options.photo)
      if not entries:
        LOG.info('No %ss matching %s' % (entry_type, search_string))
      else:
        client.DeleteEntryList(entries, entry_type, options.prompt)

#

    def _run_list(client, options, args):
      titles_list = googlecl.build_titles_list(options.title, args)
      entries = client.build_entry_list(user=options.owner or options.user,
                                        titles=titles_list,
                                        query=options.query,
                                        force_photos=True,
                                        photo_title=options.photo)
      for entry in entries:
        print googlecl.base.compile_entry_string(PhotoEntryToStringWrapper(entry),
                                                 options.fields.split(','),
                                                 delimiter=options.delimiter)

#

    def _run_list_albums(client, options, args):
      titles_list = googlecl.build_titles_list(options.title, args)
      entries = client.build_entry_list(user=options.owner or options.user,
                                        titles=titles_list,
                                        force_photos=False)
      for entry in entries:
        print googlecl.base.compile_entry_string(AlbumEntryToStringWrapper(entry),
                                                 options.fields.split(','),
                                                 delimiter=options.delimiter)

#

    def _run_post(client, options, args):
      media_list = options.src + args
      if not media_list:
        LOG.error('Must provide paths to media to post!')
      album = client.GetSingleAlbum(user=options.owner or options.user,
                                    title=options.title)
      if album:
        client.InsertMediaList(album, media_list, tags=options.tags,
                               user=options.owner or options.user,
                               photo_name=options.photo, caption=options.summary)
      else:
        LOG.error('No albums found that match ' + options.title)

#

    def _run_get(client, options, args):
      if not options.dest:
        LOG.error('Must provide destination of album(s)!')
        return

      titles_list = googlecl.build_titles_list(options.title, args)
      client.DownloadAlbum(options.dest,
                           user=options.owner or options.user,
                           video_format=options.format or 'mp4',
                           titles=titles_list,
                           photo_title=options.photo)

#

    def _run_tag(client, options, args):
      titles_list = googlecl.build_titles_list(options.title, args)
      entries = client.build_entry_list(user=options.owner or options.user,
                                        query=options.query,
                                        titles=titles_list,
                                        force_photos=True,
                                        photo_title=options.photo)
      if entries:
        client.TagPhotos(entries, options.tags, options.summary)
      else:
        LOG.error('No matches for the title and/or query you gave.')

    TASKS = {'create': googlecl.base.Task('Create an album',
                                          callback=_run_create,
                                          required='title',
                                          optional=['src', 'date',
                                                    'summary', 'tags', 'access']),
             'post': googlecl.base.Task('Post photos to an album',
                                        callback=_run_post,
                                        required=['title', 'src'],
                                        optional=['tags', 'owner', 'photo',
                                                  'summary']),
             'delete': googlecl.base.Task('Delete photos or albums',
                                          callback=_run_delete,
                                          required=[['title', 'query']],
                                          optional='photo'),
             'list': googlecl.base.Task('List photos', callback=_run_list,
                                        required=['fields', 'delimiter'],
                                        optional=['title', 'query',
                                                  'owner', 'photo']),
             'list-albums': googlecl.base.Task('List albums',
                                               callback=_run_list_albums,
                                               required=['fields', 'delimiter'],
                                               optional=['title', 'owner']),
             'get': googlecl.base.Task('Download albums', callback=_run_get,
                                       required=['title', 'dest'],
                                       optional=['owner', 'format', 'photo']),
             'tag': googlecl.base.Task('Tag/caption photos', callback=_run_tag,
                                       required=[['title', 'query'],
                                                 ['tags', 'summary']],
                                       optional=['owner', 'photo'])}