<commit_before>package uk.co.anthonycampbell.grails.plugins.picasa

import java.net.URL;

import com.google.gdata.client.photos.*;
import com.google.gdata.data.photos.*;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.RedirectRequiredException;
import com.google.gdata.util.ServiceException;

import org.apache.commons.lang.StringUtils
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean

/**
 * Grails service to expose the required Picasa API methods to the
 * application.
 *
 * @author <NAME> (<EMAIL>)
 */
class PicasaService implements InitializingBean {

    // Declare service properties
    boolean transactional = true
    boolean serviceInitialised = false

    // Declare dependencies
    PicasawebService picasaWebService
    def grailsApplication
    def picasaUsername
    def picasaPassword
    def picasaApplicationName
    def picasaImgmax
    def picasaThumbsize

    // Declare logger to be used "after" properties set
    def logger = Logger.getLogger(this.getClass());

    /**
     * Initialise config properties.
     */
    @Override
    void afterPropertiesSet() {
        // Get configuration from Config.groovy
        this.picasaUsername = grailsApplication.config.picasa.username
        this.picasaPassword = grailsApplication.config.picasa.password
        this.picasaApplicationName = this.getClass().getPackage().getName() +
            "-" + grailsApplication.metadata['app.name'] +
            "-" + grailsApplication.metadata['app.version']
        this.picasaImgmax = grailsApplication.config.picasa.imgmax
        this.picasaThumbsize = grailsApplication.config.picasa.thumbsize

        // Validate properties and attempt to initialise the service
        validateAndInitialiseService()
    }

    /*
     * Attempt to re-connect to the Picasa web service using the new provided
     * configuration details.
     *
     * @return whether a new connection was successfully made.
     */
    boolean connect(String picasaUsername, String picasaPassword,
            String picasaApplicationName, String picasaImgmax, String picasaThumbsize) {
        this.picasaUsername = picasaUsername
        this.picasaPassword = <PASSWORD>
        this.picasaApplicationName = picasaApplicationName
        this.picasaImgmax = picasaImgmax
        this.picasaThumbsize = picasaThumbsize

        return validateAndInitialiseService()
    }

    /**
     * List the available albums for the configured Google Picasa account.
     *
     * @return list of albums from the Google Picasa web service.
     * @Exception PicasaServiceException when there's been a problem retrieving
     *      the list of available albums.
     */
    def List<Album> listAlbums() throws PicasaServiceException {
        if(serviceInitialised) {
            try {
                // Initialise result
                List<Album> albumListing = new ArrayList<Album>()

                // Declare feed
                URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" +
                    this.picasaUsername + "?kind=album&thumbsize=" + this.picasaThumbsize +
                    "&imgmax=" + this.picasaImgmax)

                log.debug("FeedUrl: " + feedUrl)

                // Get user feed
                UserFeed userFeed = picasaWebService.getFeed(feedUrl, UserFeed.class)

                for (AlbumEntry entry : userFeed.getAlbumEntries()) {
                    // Transfer entry into domain class
                    Album album = convertToAlbumDomain(entry)

                    // If we have a valid public entry add to listing
                    if (!album.hasErrors() && album.isPublic) {
                        albumListing.add(album)
                    }
                }

                // Return result
                return albumListing

            } catch (Exception ex) {
                def errorMessage = "Unable to list your Google Picasa Web Albums. A problem occurred " +
                    "when making the request through the Google Data API. (username=" +
                    this.picasaUsername + ")"

                log.error(errorMessage, ex)
                throw new PicasaServiceException(errorMessage, ex)
            }
        } else {
            def errorMessage = "Unable to list your Google Picasa Web Albums. Some of the plug-in " +
                "configuration is missing. Please refer to the documentation and ensure you have " +
                "declared all of the required configuration."

            log.error(errorMessage)
            throw new PicasaServiceException(errorMessage)
        }
    }

    /**
     * Get the Album for the provided ID through the Google Picasa web service.
     *
     * @param albumId the provided album ID.
     * @return the retrieved album from the Google Picasa web service.
     * @Exception PicasaServiceException when there's been a problem retrieving
     *      the selected album album.
     */
    def Album getAlbum(String albumId) throws PicasaServiceException {
        if(serviceInitialised) {
            try {
                // Validate ID
                if (!albumId || StringUtils.isEmpty(albumId)) {
                    def errorMessage = "Unable to retrieve your Google Picasa Web Album. The provided " +
                        "ID was invalid. (albumId=" + albumId + ")"

                    log.error(errorMessage, ex)
                    throw new PicasaServiceException(errorMessage, ex)
                }

                // Initialise result
                Album album = null

                // Declare feed
                URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" +
                    this.picasaUsername + "/albumid/" + albumId + "?thumbsize=" +
                    this.picasaThumbsize + "&imgmax=" + this.picasaImgmax)

                log.debug("FeedUrl: " + feedUrl)

                // Get album feed
                AlbumFeed albumFeed = picasaWebService.getFeed(feedUrl, AlbumFeed.class);

                // Transfer feed into domain class
                Album domain = convertToAlbumDomain(albumFeed)

                // If we have a valid public entry add to listing
                if (!domain.hasErrors() && domain.isPublic) {
                    album = domain
                }

                // Return result
                return album

            } catch (Exception ex) {
                def errorMessage = "Unable to retrieve your Google Picasa Web Album. A problem occurred " +
                    "when making the request through the Google Data API. (username=" +
                    this.picasaUsername + ", albumId=" + albumId + ")"

                log.error(errorMessage, ex)
                throw new PicasaServiceException(errorMessage, ex)
            }
        } else {
            def errorMessage = "Unable to retrieve your Google Picasa Web Album. Some of the plug-in " +
                "configuration is missing. Please refer to the documentation and ensure you have " +
                "declared all of the required configuration."

            log.error(errorMessage)
            throw new PicasaServiceException(errorMessage)
        }
    }

    /**
     * List the available photos for the provided Google Picasa web album.
     *
     * @return list of photos for the provided Google Picasa web service album.
     * @Exception PicasaServiceException when there's been a problem retrieving
     *      the list of available photos.
     */
    def List<Photo> listPhotosForAlbum(String albumId) throws PicasaServiceException {
        if(serviceInitialised) {
            // Validate ID
            if (albumId == null || StringUtils.isEmpty(albumId)) {
                def errorMessage = "Unable to retrieve your Google Picasa Web Album Photos. The " +
                    "provided ID was invalid. (albumId=" + albumId + ")"

                log.error(errorMessage)
                throw new PicasaServiceException(errorMessage)
            }

            try {
                // Initialise result
                List<Photo> photoListing = new ArrayList<Photo>()

                // Declare feed
                URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" +
                    this.picasaUsername + "/albumid/" + albumId + "?thumbsize=" +
                    this.picasaThumbsize + "&imgmax=" + this.picasaImgmax)

                log.debug("FeedUrl: " + feedUrl)

                // Get album feed
                AlbumFeed albumFeed = picasaWebService.getFeed(feedUrl, AlbumFeed.class)

                for (PhotoEntry entry : albumFeed.getPhotoEntries()) {
                    // Transfer entry into domain class
                    Photo photo = convertToPhotoDomain(entry)

                    // If we have a valid public entry add to listing
                    if (!photo.hasErrors() && photo.isPublic) {
                        photoListing.add(photo)
                    }
                }

                // Return result
                return photoListing

            } catch (Exception ex) {
                def errorMessage = "Unable to list your Google Picasa Web Album Photos. A problem occurred " +
                    "when making the request through the Google Data API. (username=" +
                    this.picasaUsername + ", albumId=" + albumId + ")"

                log.error(errorMessage, ex)
                throw new PicasaServiceException(errorMessage, ex)
            }
        } else {
            def errorMessage = "Unable to list your Google Picasa Web Album Photos. Some of the plug-in " +
                "configuration is missing. Please refer to the documentation and ensure you have " +
                "declared all of the required configuration."

            log.error(errorMessage)
            throw new PicasaServiceException(errorMessage)
        }
    }

    /**
     * Get the Photo for the provided IDs through the Google Picasa web service.
     *
     * @param albumId the provided album ID.
     * @param photoId the provided photo ID.
     * @return the retrieved photo from the Google Picasa web service.
     * @Exception PicasaServiceException when there's been a problem retrieving
     *      the selected photo.
     */
    def Photo getPhoto(String albumId, String photoId) throws PicasaServiceException {
        if(serviceInitialised) {
            // Validate IDs
            if (albumId == null || StringUtils.isEmpty(albumId) ||
                    photoId == null || StringUtils.isEmpty(photoId)) {
                def errorMessage = "Unable to retrieve your Google Picasa Web Album Photo. The " +
                    "provided IDs were invalid. (albumId=" + albumId + ", photoId=" + photoId + ")"

                log.error(errorMessage)
                throw new PicasaServiceException(errorMessage)
            }

            try {
                // Initialise result
                Photo photo = null

                // Declare feed
                URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" +
                    this.picasaUsername + "/albumid/" + albumId + "/photoid/" + photoId +
                    "?thumbsize=" + this.picasaThumbsize + "&imgmax=" + this.picasaImgmax)

                log.debug("FeedUrl: " + feedUrl)

                // Get album feed
                PhotoFeed photoFeed = picasaWebService.getFeed(feedUrl, PhotoFeed.class) 

                // Transfer feed into domain class
                Photo domain = convertToPhotoDomain(photoFeed)

                // If we have a valid public entry add to listing
                if (!domain.hasErrors() && domain.isPublic) {
                    photo = domain
                }

                // Check we have a photo to work with
                if (photo) {
                    // Second call to find out navigation based on position in album
                    feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" +
                        this.picasaUsername + "/albumid/" + albumId + "?thumbsize=" +
                        this.picasaThumbsize + "&imgmax=" + this.picasaImgmax)

                    log.debug("FeedUrl: " + feedUrl)

                    // Get album feed
                    AlbumFeed albumFeed = picasaWebService.getFeed(feedUrl, AlbumFeed.class)

                    // Initialise search variables
                    boolean found = false
                    String previous = ""
                    String current = ""
                    String next = ""

                    // Find photo and store previous and subsequent IDs (if available)
                    for (PhotoEntry entry : albumFeed.getPhotoEntries()) {
                        // Prepare ID
                        current = entry?.getId()?.substring(entry?.getId()?.lastIndexOf('/') + 1,
                            entry?.getId()?.length())

                        // If already found, store next ID and end search
                        if (found == true) {
                            next = current
                            break
                        }

                        if (current == photoId) {
                            found = true
                        }

                        if (found == false) {
                            previous = current
                        }
                    }

                    // Update photo with previous and next images
                    photo.previousPhotoId = previous
                    photo.nextPhotoId = next
                }

                // Return result
                return photo

            } catch (Exception ex) {
                def errorMessage = "Unable to retrieve your Google Picasa Web Album Photo. A problem occurred " +
                    "when making the request through the Google Data API. (username=" +
                    this.picasaUsername + ", albumId=" + albumId + ", photoId=" + photoId + ")"

                log.error(errorMessage, ex)
                throw new PicasaServiceException(errorMessage, ex)
            }
        } else {
            def errorMessage = "Unable to retrieve your Google Picasa Web Album Photo. Some of the plug-in " +
                "configuration is missing. Please refer to the documentation and ensure you have " +
                "declared all of the required configuration."

            log.error(errorMessage)
            throw new PicasaServiceException(errorMessage)
        }
    }
    
    /**
     * Validate the service properties and attempt to initialise.
     *
     * @return whether the service has been successfully initialised.
     */
    private boolean validateAndInitialiseService() {
        // Lets be optimistic
        def configValid = true

        logger.error("Begin PicasaService configuration validation.")

        // Validate properties
        if (!isConfigValid(this.picasaUsername)) {
            logger.error("Unable to connect to Google Picasa Web Albums - invalid username. Please " +
                "ensure you have declared the property picasa.username in your application's config.")
            configValid = false
        }
        if (!isConfigValid(this.picasaPassword)) {
            logger.error("Unable to connect to Google Picasa Web Albums - invalid password. Please " +
                "ensure you have declared the property picasa.password in your application's config.")
            configValid = false
        }
        if (!isConfigValid(this.picasaApplicationName)) {
            logger.error("Unable to connect to Google Picasa Web Albums - invalid application name. This " +<commit_msg>CHANGES update. Introduction of new Picasa API connection methods.<commit_after>package uk.co.anthonycampbell.grails.plugins.picasa

import java.net.URL;

import com.google.gdata.client.photos.*;
import com.google.gdata.data.photos.*;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.RedirectRequiredException;
import com.google.gdata.util.ServiceException;

import org.apache.commons.lang.StringUtils
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean

/**
 * Grails service to expose the required Picasa API methods to the
 * application.
 *
 * @author <NAME> (<EMAIL>)
 */
class PicasaService implements InitializingBean {

    // Declare service properties
    boolean transactional = true
    boolean serviceInitialised = false

    // Declare dependencies
    PicasawebService picasaWebService
    def grailsApplication
    def picasaUsername
    def picasaPassword
    def picasaApplicationName
    def picasaImgmax
    def picasaThumbsize

    // Declare logger to be used "after" properties set
    def logger = Logger.getLogger(this.getClass());

    /**
     * Initialise config properties.
     */
    @Override
    void afterPropertiesSet() {
        reset()
    }

    /*
     * Attempt to re-connect to the Picasa web service using the new provided
     * configuration details.
     *
     * @return whether a new connection was successfully made.
     */
    boolean connect(String picasaUsername, String picasaPassword,
            String picasaApplicationName, String picasaImgmax, String picasaThumbsize) {
        this.picasaUsername = picasaUsername
        this.picasaPassword = <PASSWORD>
        this.picasaApplicationName = picasaApplicationName
        this.picasaImgmax = picasaImgmax
        this.picasaThumbsize = picasaThumbsize

        return validateAndInitialiseService()
    }

    /*
     * Attempt to re-connect to the Picasa web service using the provided
     * connection details available in the grails-app/conf/Config.groovy file.
     *
     * @return whether a new connection was successfully made.
     */
    boolean reset() {
        // Get configuration from Config.groovy
        this.picasaUsername = grailsApplication.config.picasa.username
        this.picasaPassword = <PASSWORD>.config.picasa.password
        this.picasaApplicationName = this.getClass().getPackage().getName() +
            "-" + grailsApplication.metadata['app.name'] +
            "-" + grailsApplication.metadata['app.version']
        this.picasaImgmax = grailsApplication.config.picasa.imgmax
        this.picasaThumbsize = grailsApplication.config.picasa.thumbsize

        // Validate properties and attempt to initialise the service
        return validateAndInitialiseService()
    }

    /**
     * List the available albums for the configured Google Picasa account.
     *
     * @return list of albums from the Google Picasa web service.
     * @Exception PicasaServiceException when there's been a problem retrieving
     *      the list of available albums.
     */
    def List<Album> listAlbums() throws PicasaServiceException {
        if(serviceInitialised) {
            try {
                // Initialise result
                List<Album> albumListing = new ArrayList<Album>()

                // Declare feed
                URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" +
                    this.picasaUsername + "?kind=album&thumbsize=" + this.picasaThumbsize +
                    "&imgmax=" + this.picasaImgmax)

                log.debug("FeedUrl: " + feedUrl)

                // Get user feed
                UserFeed userFeed = picasaWebService.getFeed(feedUrl, UserFeed.class)

                for (AlbumEntry entry : userFeed.getAlbumEntries()) {
                    // Transfer entry into domain class
                    Album album = convertToAlbumDomain(entry)

                    // If we have a valid public entry add to listing
                    if (!album.hasErrors() && album.isPublic) {
                        albumListing.add(album)
                    }
                }

                // Return result
                return albumListing

            } catch (Exception ex) {
                def errorMessage = "Unable to list your Google Picasa Web Albums. A problem occurred " +
                    "when making the request through the Google Data API. (username=" +
                    this.picasaUsername + ")"

                log.error(errorMessage, ex)
                throw new PicasaServiceException(errorMessage, ex)
            }
        } else {
            def errorMessage = "Unable to list your Google Picasa Web Albums. Some of the plug-in " +
                "configuration is missing. Please refer to the documentation and ensure you have " +
                "declared all of the required configuration."

            log.error(errorMessage)
            throw new PicasaServiceException(errorMessage)
        }
    }

    /**
     * Get the Album for the provided ID through the Google Picasa web service.
     *
     * @param albumId the provided album ID.
     * @return the retrieved album from the Google Picasa web service.
     * @Exception PicasaServiceException when there's been a problem retrieving
     *      the selected album album.
     */
    def Album getAlbum(String albumId) throws PicasaServiceException {
        if(serviceInitialised) {
            try {
                // Validate ID
                if (!albumId || StringUtils.isEmpty(albumId)) {
                    def errorMessage = "Unable to retrieve your Google Picasa Web Album. The provided " +
                        "ID was invalid. (albumId=" + albumId + ")"

                    log.error(errorMessage, ex)
                    throw new PicasaServiceException(errorMessage, ex)
                }

                // Initialise result
                Album album = null

                // Declare feed
                URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" +
                    this.picasaUsername + "/albumid/" + albumId + "?thumbsize=" +
                    this.picasaThumbsize + "&imgmax=" + this.picasaImgmax)

                log.debug("FeedUrl: " + feedUrl)

                // Get album feed
                AlbumFeed albumFeed = picasaWebService.getFeed(feedUrl, AlbumFeed.class);

                // Transfer feed into domain class
                Album domain = convertToAlbumDomain(albumFeed)

                // If we have a valid public entry add to listing
                if (!domain.hasErrors() && domain.isPublic) {
                    album = domain
                }

                // Return result
                return album

            } catch (Exception ex) {
                def errorMessage = "Unable to retrieve your Google Picasa Web Album. A problem occurred " +
                    "when making the request through the Google Data API. (username=" +
                    this.picasaUsername + ", albumId=" + albumId + ")"

                log.error(errorMessage, ex)
                throw new PicasaServiceException(errorMessage, ex)
            }
        } else {
            def errorMessage = "Unable to retrieve your Google Picasa Web Album. Some of the plug-in " +
                "configuration is missing. Please refer to the documentation and ensure you have " +
                "declared all of the required configuration."

            log.error(errorMessage)
            throw new PicasaServiceException(errorMessage)
        }
    }

    /**
     * List the available photos for the provided Google Picasa web album.
     *
     * @return list of photos for the provided Google Picasa web service album.
     * @Exception PicasaServiceException when there's been a problem retrieving
     *      the list of available photos.
     */
    def List<Photo> listPhotosForAlbum(String albumId) throws PicasaServiceException {
        if(serviceInitialised) {
            // Validate ID
            if (albumId == null || StringUtils.isEmpty(albumId)) {
                def errorMessage = "Unable to retrieve your Google Picasa Web Album Photos. The " +
                    "provided ID was invalid. (albumId=" + albumId + ")"

                log.error(errorMessage)
                throw new PicasaServiceException(errorMessage)
            }

            try {
                // Initialise result
                List<Photo> photoListing = new ArrayList<Photo>()

                // Declare feed
                URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" +
                    this.picasaUsername + "/albumid/" + albumId + "?thumbsize=" +
                    this.picasaThumbsize + "&imgmax=" + this.picasaImgmax)

                log.debug("FeedUrl: " + feedUrl)

                // Get album feed
                AlbumFeed albumFeed = picasaWebService.getFeed(feedUrl, AlbumFeed.class)

                for (PhotoEntry entry : albumFeed.getPhotoEntries()) {
                    // Transfer entry into domain class
                    Photo photo = convertToPhotoDomain(entry)

                    // If we have a valid public entry add to listing
                    if (!photo.hasErrors() && photo.isPublic) {
                        photoListing.add(photo)
                    }
                }

                // Return result
                return photoListing

            } catch (Exception ex) {
                def errorMessage = "Unable to list your Google Picasa Web Album Photos. A problem occurred " +
                    "when making the request through the Google Data API. (username=" +
                    this.picasaUsername + ", albumId=" + albumId + ")"

                log.error(errorMessage, ex)
                throw new PicasaServiceException(errorMessage, ex)
            }
        } else {
            def errorMessage = "Unable to list your Google Picasa Web Album Photos. Some of the plug-in " +
                "configuration is missing. Please refer to the documentation and ensure you have " +
                "declared all of the required configuration."

            log.error(errorMessage)
            throw new PicasaServiceException(errorMessage)
        }
    }

    /**
     * Get the Photo for the provided IDs through the Google Picasa web service.
     *
     * @param albumId the provided album ID.
     * @param photoId the provided photo ID.
     * @return the retrieved photo from the Google Picasa web service.
     * @Exception PicasaServiceException when there's been a problem retrieving
     *      the selected photo.
     */
    def Photo getPhoto(String albumId, String photoId) throws PicasaServiceException {
        if(serviceInitialised) {
            // Validate IDs
            if (albumId == null || StringUtils.isEmpty(albumId) ||
                    photoId == null || StringUtils.isEmpty(photoId)) {
                def errorMessage = "Unable to retrieve your Google Picasa Web Album Photo. The " +
                    "provided IDs were invalid. (albumId=" + albumId + ", photoId=" + photoId + ")"

                log.error(errorMessage)
                throw new PicasaServiceException(errorMessage)
            }

            try {
                // Initialise result
                Photo photo = null

                // Declare feed
                URL feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" +
                    this.picasaUsername + "/albumid/" + albumId + "/photoid/" + photoId +
                    "?thumbsize=" + this.picasaThumbsize + "&imgmax=" + this.picasaImgmax)

                log.debug("FeedUrl: " + feedUrl)

                // Get album feed
                PhotoFeed photoFeed = picasaWebService.getFeed(feedUrl, PhotoFeed.class) 

                // Transfer feed into domain class
                Photo domain = convertToPhotoDomain(photoFeed)

                // If we have a valid public entry add to listing
                if (!domain.hasErrors() && domain.isPublic) {
                    photo = domain
                }

                // Check we have a photo to work with
                if (photo) {
                    // Second call to find out navigation based on position in album
                    feedUrl = new URL("http://picasaweb.google.com/data/feed/api/user/" +
                        this.picasaUsername + "/albumid/" + albumId + "?thumbsize=" +
                        this.picasaThumbsize + "&imgmax=" + this.picasaImgmax)

                    log.debug("FeedUrl: " + feedUrl)

                    // Get album feed
                    AlbumFeed albumFeed = picasaWebService.getFeed(feedUrl, AlbumFeed.class)

                    // Initialise search variables
                    boolean found = false
                    String previous = ""
                    String current = ""
                    String next = ""

                    // Find photo and store previous and subsequent IDs (if available)
                    for (PhotoEntry entry : albumFeed.getPhotoEntries()) {
                        // Prepare ID
                        current = entry?.getId()?.substring(entry?.getId()?.lastIndexOf('/') + 1,
                            entry?.getId()?.length())

                        // If already found, store next ID and end search
                        if (found == true) {
                            next = current
                            break
                        }

                        if (current == photoId) {
                            found = true
                        }

                        if (found == false) {
                            previous = current
                        }
                    }

                    // Update photo with previous and next images
                    photo.previousPhotoId = previous
                    photo.nextPhotoId = next
                }

                // Return result
                return photo

            } catch (Exception ex) {
                def errorMessage = "Unable to retrieve your Google Picasa Web Album Photo. A problem occurred " +
                    "when making the request through the Google Data API. (username=" +
                    this.picasaUsername + ", albumId=" + albumId + ", photoId=" + photoId + ")"

                log.error(errorMessage, ex)
                throw new PicasaServiceException(errorMessage, ex)
            }
        } else {
            def errorMessage = "Unable to retrieve your Google Picasa Web Album Photo. Some of the plug-in " +
                "configuration is missing. Please refer to the documentation and ensure you have " +
                "declared all of the required configuration."

            log.error(errorMessage)
            throw new PicasaServiceException(errorMessage)
        }
    }
    
    /**
     * Validate the service properties and attempt to initialise.
     *
     * @return whether the service has been successfully initialised.
     */
    private boolean validateAndInitialiseService() {
        // Lets be optimistic
        def configValid = true

        logger.info("Begin PicasaService configuration validation.")

        // Validate properties
        if (!isConfigValid(this.picasaUsername)) {
            logger.error("Unable to connect to Google Picasa Web Albums - invalid username. Please " +
                "ensure you have declared the property picasa.username in your application's config.")
            configValid = false
        }
        if (!isConfigValid(this.picasaPassword)) {
            logger.error("Unable to connect to Google Picasa Web Albums - invalid password. Please " +
                "ensure you have declared the property picasa.password in your application's config.")
            configValid = false
        }
        if (!isConfigValid(this.picasaApplicationName)) {
            logger.error("Unable to connect to Google Picasa Web Albums - invalid application name. This " +
{'fraction_non_alphanumeric': 0.07729833506436283, 'fraction_numerical': 0.0001258930538507538, 'mean_word_length': 1.6795412379827965, 'pattern_counts': {'":': 0, '<': 22, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2019635', 'n_tokens_mistral': 7860, 'n_tokens_neox': 7254, 'n_words': 2338}