<commit_before>#!/usr/bin/env python

import logging
import exifread
import os
import shutil
import hashlib
import sys
import time
import datetime
import ntpath
import glob

#logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.WARNING)
# create logger
logger = logging.getLogger('moveExifFiles')
logger.setLevel(logging.INFO)
logFormatter = logging.Formatter("%(asctime)s [%(levelname)-5.5s]  %(message)s")

fileHandler = logging.FileHandler("{0}/{1}.log".format("/home/storage/exif-logs", datetime.datetime.now().isoformat()))
fileHandler.setFormatter(logFormatter)
logger.addHandler(fileHandler)

consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
logger.addHandler(consoleHandler)

class InputArguments(object):

    # create logger
    logger = logging.getLogger('InputArguments')
    logger.setLevel(logging.INFO)

    def __init__(self, argv):
        if len(argv) < 3:
            print("Usage: moveExifFiles.py <directory to process> <destination directory>")
            exit(1)

        self.dirname = argv[1]
        self.destdirname = argv[2]

        logger.info("Using source directory (recursive): {}".format(self.dirname))
        logger.info("Using destination directory: {}".format(self.destdirname))

        if not self.dirname:
            print "source directory cannot be empty!"
            exit(1)

        if not self.destdirname:
            print "destination directory cannot be empty!"
            exit(1)

        if not os.path.isdir(self.dirname):
            print("source directory {} does not exist!".format(self.dirname))
            exit(1)

        if not os.path.isdir(self.destdirname):
            self.logger.info ("destination directory {} does not exist, creating it...".format(self.destdirname))
            print ("Created dir {}".format(self.destdirname))
            os.makedirs(self.destdirname)
    def abs_src_path(self, filename):
        return self.dirname + os.path.sep + filename

    def abs_dest_path(self, filename):
        return self.destdirname + os.path.sep + filename

class ExifFileReader(object):

    # create logger
    logger = logging.getLogger('ExifFileReader')
    logger.setLevel(logging.WARNING)

    def __init__(self, filename):
        self.filename = filename

    # returned date format YYYY-mm-dd
    def readExifDate(self):
        date = ""
        #open file, do EXIF stuf
        with open(self.filename,'rb') as file:
            data=exifread.process_file(file)
            self.logger.debug(data)
        if data:
            exifDateTimeField = data.get('EXIF DateTimeOriginal', "")
            if exifDateTimeField:
                self.logger.debug(self.filename + " - " + str(exifDateTimeField))
                date = str(exifDateTimeField).replace(":", "-").split()[0]
                self.logger.debug(date)
            else:
                self.logger.debug("no EXIF DateTimeOriginal field in the EXIF data")
        else:
            self.logger.debug("no EXIF in file {}".format(self.filename))

        return date

class Stats(object):
    # create logger
    logger = logging.getLogger('Stats')
    logger.setLevel(logging.WARNING)

    def __init__(self):
        self.stats = {}
        self.total = {}

    def report(self, key, name):
        dirStats = self.stats.get(key, {})
        curr = dirStats.get(name, 0)
        dirStats[name]=curr + 1
        self.stats[key]=dirStats
        curtype = self.total.get(name, 0)
        self.total[name]=curtype + 1

    def __repr__(self):
        str = []
        if self.stats:
            str.append("\nSummary: \n")
            for cdir, cdirStats in self.stats.items():
                str.append("\nDirectory {}".format(cdir))
                for op, count in cdirStats.items():
                    str.append("\t{} files {}".format(count, op))

            for op, count in self.total.items():
                str.append("\n\nTotal {} files {}".format(count, op))
        else:
            str.append("\nNo stats were updated.")

        out_str = ''.join(str)
        return out_str

def create_directories(args, date):
    # get year from date
    year = date.split("-")[0]
    dst_dir_path = args.abs_dest_path(year + os.path.sep + date)

    logger.debug("searching for directories with date {} ...".format(date))
    simdirs = glob.glob("{}*".format(dst_dir_path))
    # check if list is not empty
    if simdirs:
        logger.debug("found dirs: {}".format(simdirs))

        # choose the longest named dir, probably it has additional description in its name
        sorted(simdirs, key=len, reverse=True)
        dst_dir_path = simdirs[0]
    else:
        logger.debug("no directories exist.")

    # check if dir with date exists, if not create it
    if not os.path.isdir(dst_dir_path):
        logger.info ("Destination directory \'{}\' does not exist, creating it...".format(dst_dir_path))
        os.makedirs(dst_dir_path)
        print ("Created dir {}".format(dst_dir_path))
    else:
        logger.info('Destination directory \'{}\' already exists. Using it.'.format(dst_dir_path))
    return dst_dir_path

def handle_file(args, stats, file):
    src_file_path = os.path.abspath(file)
    logger.debug("Handling file {} ...".format(src_file_path))

    exifReader = ExifFileReader(src_file_path)

    exifDate = exifReader.readExifDate()
    (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(file)

    # by default, use modified data in format
    date = datetime.datetime.fromtimestamp(mtime).strftime('%Y-%m-%d')
    if exifDate:
        # exif date if exists
        date = exifDate

    if date:
        dst_dir_path = create_directories(args, date)
        dst_file_path = dst_dir_path + os.path.sep + ntpath.basename(file)

        if os.path.isfile(dst_file_path):
            # files with the same name exist
            # check their hashes, whether they are the same
            currfileHash = md5(src_file_path)
            newfileHash = md5(dst_file_path)
            if currfileHash == newfileHash:
                logger.debug("file {} already exists, deleted from source {}".format(dst_file_path, src_file_path))
                stats.report(dst_dir_path, 'existed')
		stats.report(src_dir_path, 'deleted')
		os.remove(src_file_path)
            else:
                logger.debug("file {} already exists, but the source file {} itself is different, will be moved with different name".format(dst_file_path, src_file_path))
                i = 1
                new_dst_file_name = dst_file_path+'_'+str(i)
                while os.path.isfile(new_dst_file_name):
                    logger.debug("file {} already exists, incrementing 1 to suffix")
                    i = i + 1
                    new_dst_file_name = dst_file_path+'_'+str(i)

                shutil.move(src_file_path, new_dst_file_name)
                stats.report(dst_dir_path, 'renamed')

        else:
            shutil.move(src_file_path, dst_file_path)
            logger.debug("file {} moved to {}".format(src_file_path, dst_file_path))
            stats.report(dst_dir_path, 'moved')

def handle_dir(args, stats, dir):
    abs_dir = os.path.abspath(dir)
    start = time.time()
    logger.info("Processing folder {} ...".format(abs_dir))
    for root, subdirs, files in os.walk(abs_dir):
        logger.debug(subdirs)
        logger.debug(files)
        for f in files:
            file_path = os.path.join(root, f)
            handle_file(args, stats, file_path)

        for subdir in subdirs:
            subdir_path = os.path.join(root, subdir)
            handle_dir(args, stats, subdir_path)
    # remove empty dirs
    removeEmptyFolders(abs_dir)

    end = time.time()
    tookSec = end - start
    logger.info("Done with folder {} ({} sec).\n".format(abs_dir, tookSec))

def removeEmptyFolders(path, removeRoot=True):
    'Function to remove empty folders'
    if not os.path.isdir(path):
        return

    # remove empty subfolders
    files = os.listdir(path)
    if len(files):
        for f in files:
            fullpath = os.path.join(path, f)
            if os.path.isdir(fullpath):
                removeEmptyFolders(fullpath)

    # if folder empty, delete it
    files = os.listdir(path)
    if len(files) == 0 and removeRoot:
        logger.info("Removing empty folder: {}".format(path))
        os.rmdir(path)

def main(argv):

    args = InputArguments(argv)

    stats = Stats()

    abs_src_dir = os.path.abspath(args.dirname)
    handle_dir(args, stats, abs_src_dir)

    logger.info(stats)

def md5(fname):
    hash_md5 = hashlib.md5()
    with open(fname, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

if __name__ == "__main__":
    main(sys.argv)
<commit_msg>added remove file when it existed at destination<commit_after>#!/usr/bin/env python

import logging
import exifread
import os
import shutil
import hashlib
import sys
import time
import datetime
import ntpath
import glob

#logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.WARNING)
# create logger
logger = logging.getLogger('moveExifFiles')
logger.setLevel(logging.INFO)
logFormatter = logging.Formatter("%(asctime)s [%(levelname)-5.5s]  %(message)s")

fileHandler = logging.FileHandler("{0}/{1}.log".format("/home/storage/exif-logs", datetime.datetime.now().isoformat()))
fileHandler.setFormatter(logFormatter)
logger.addHandler(fileHandler)

consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
logger.addHandler(consoleHandler)

class InputArguments(object):

    # create logger
    logger = logging.getLogger('InputArguments')
    logger.setLevel(logging.INFO)

    def __init__(self, argv):
        if len(argv) < 3:
            print("Usage: moveExifFiles.py <directory to process> <destination directory>")
            exit(1)

        self.dirname = argv[1]
        self.destdirname = argv[2]

        logger.info("Using source directory (recursive): {}".format(self.dirname))
        logger.info("Using destination directory: {}".format(self.destdirname))

        if not self.dirname:
            print "source directory cannot be empty!"
            exit(1)

        if not self.destdirname:
            print "destination directory cannot be empty!"
            exit(1)

        if not os.path.isdir(self.dirname):
            print("source directory {} does not exist!".format(self.dirname))
            exit(1)

        if not os.path.isdir(self.destdirname):
            self.logger.info ("destination directory {} does not exist, creating it...".format(self.destdirname))
            print ("Created dir {}".format(self.destdirname))
            os.makedirs(self.destdirname)
    def abs_src_path(self, filename):
        return self.dirname + os.path.sep + filename

    def abs_dest_path(self, filename):
        return self.destdirname + os.path.sep + filename

class ExifFileReader(object):

    # create logger
    logger = logging.getLogger('ExifFileReader')
    logger.setLevel(logging.WARNING)

    def __init__(self, filename):
        self.filename = filename

    # returned date format YYYY-mm-dd
    def readExifDate(self):
        date = ""
        #open file, do EXIF stuf
        with open(self.filename,'rb') as file:
            data=exifread.process_file(file)
            self.logger.debug(data)
        if data:
            exifDateTimeField = data.get('EXIF DateTimeOriginal', "")
            if exifDateTimeField:
                self.logger.debug(self.filename + " - " + str(exifDateTimeField))
                date = str(exifDateTimeField).replace(":", "-").split()[0]
                self.logger.debug(date)
            else:
                self.logger.debug("no EXIF DateTimeOriginal field in the EXIF data")
        else:
            self.logger.debug("no EXIF in file {}".format(self.filename))

        return date

class Stats(object):
    # create logger
    logger = logging.getLogger('Stats')
    logger.setLevel(logging.WARNING)

    def __init__(self):
        self.stats = {}
        self.total = {}

    def report(self, key, name):
        dirStats = self.stats.get(key, {})
        curr = dirStats.get(name, 0)
        dirStats[name]=curr + 1
        self.stats[key]=dirStats
        curtype = self.total.get(name, 0)
        self.total[name]=curtype + 1

    def __repr__(self):
        str = []
        if self.stats:
            str.append("\nSummary: \n")
            for cdir, cdirStats in self.stats.items():
                str.append("\nDirectory {}".format(cdir))
                for op, count in cdirStats.items():
                    str.append("\t{} files {}".format(count, op))

            for op, count in self.total.items():
                str.append("\n\nTotal {} files {}".format(count, op))
        else:
            str.append("\nNo stats were updated.")

        out_str = ''.join(str)
        return out_str

def create_directories(args, date):
    # get year from date
    year = date.split("-")[0]
    dst_dir_path = args.abs_dest_path(year + os.path.sep + date)

    logger.debug("searching for directories with date {} ...".format(date))
    simdirs = glob.glob("{}*".format(dst_dir_path))
    # check if list is not empty
    if simdirs:
        logger.debug("found dirs: {}".format(simdirs))

        # choose the longest named dir, probably it has additional description in its name
        sorted(simdirs, key=len, reverse=True)
        dst_dir_path = simdirs[0]
    else:
        logger.debug("no directories exist.")

    # check if dir with date exists, if not create it
    if not os.path.isdir(dst_dir_path):
        logger.info ("Destination directory \'{}\' does not exist, creating it...".format(dst_dir_path))
        os.makedirs(dst_dir_path)
        print ("Created dir {}".format(dst_dir_path))
    else:
        logger.info('Destination directory \'{}\' already exists. Using it.'.format(dst_dir_path))
    return dst_dir_path

def handle_file(args, stats, file):
    src_file_path = os.path.abspath(file)
    logger.debug("Handling file {} ...".format(src_file_path))

    exifReader = ExifFileReader(src_file_path)

    exifDate = exifReader.readExifDate()
    (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(file)

    # by default, use modified data in format
    date = datetime.datetime.fromtimestamp(mtime).strftime('%Y-%m-%d')
    if exifDate:
        # exif date if exists
        date = exifDate

    if date:
        dst_dir_path = create_directories(args, date)
        dst_file_path = dst_dir_path + os.path.sep + ntpath.basename(file)

        if os.path.isfile(dst_file_path):
            # files with the same name exist
            # check their hashes, whether they are the same
            currfileHash = md5(src_file_path)
            newfileHash = md5(dst_file_path)
            if currfileHash == newfileHash:
                logger.debug("file {} already exists, deleted from source {}".format(dst_file_path, src_file_path))
                stats.report(dst_dir_path, 'existed')
		stats.report(src_file_path, 'deleted')
		os.remove(src_file_path)
            else:
                logger.debug("file {} already exists, but the source file {} itself is different, will be moved with different name".format(dst_file_path, src_file_path))
                i = 1
                new_dst_file_name = dst_file_path+'_'+str(i)
                while os.path.isfile(new_dst_file_name):
                    logger.debug("file {} already exists, incrementing 1 to suffix")
                    i = i + 1
                    new_dst_file_name = dst_file_path+'_'+str(i)

                shutil.move(src_file_path, new_dst_file_name)
                stats.report(dst_dir_path, 'renamed')

        else:
            shutil.move(src_file_path, dst_file_path)
            logger.debug("file {} moved to {}".format(src_file_path, dst_file_path))
            stats.report(dst_dir_path, 'moved')

def handle_dir(args, stats, dir):
    abs_dir = os.path.abspath(dir)
    start = time.time()
    logger.info("Processing folder {} ...".format(abs_dir))
    for root, subdirs, files in os.walk(abs_dir):
        logger.debug(subdirs)
        logger.debug(files)
        for f in files:
            file_path = os.path.join(root, f)
            handle_file(args, stats, file_path)

        for subdir in subdirs:
            subdir_path = os.path.join(root, subdir)
            handle_dir(args, stats, subdir_path)
    # remove empty dirs
    removeEmptyFolders(abs_dir)

    end = time.time()
    tookSec = end - start
    logger.info("Done with folder {} ({} sec).\n".format(abs_dir, tookSec))

def removeEmptyFolders(path, removeRoot=True):
    'Function to remove empty folders'
    if not os.path.isdir(path):
        return

    # remove empty subfolders
    files = os.listdir(path)
    if len(files):
        for f in files:
            fullpath = os.path.join(path, f)
            if os.path.isdir(fullpath):
                removeEmptyFolders(fullpath)

    # if folder empty, delete it
    files = os.listdir(path)
    if len(files) == 0 and removeRoot:
        logger.info("Removing empty folder: {}".format(path))
        os.rmdir(path)

def main(argv):

    args = InputArguments(argv)

    stats = Stats()

    abs_src_dir = os.path.abspath(args.dirname)
    handle_dir(args, stats, abs_src_dir)

    logger.info(stats)

def md5(fname):
    hash_md5 = hashlib.md5()
    with open(fname, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

if __name__ == "__main__":
    main(sys.argv)
<|endoftext|>


<commit_before>#! /usr/bin/env python

# -*- coding: utf-8 -*-
"""
    CigarBox
    ~~~~~~

    A smokin' fast personal photostream

    :copyright: (c) 2014 by Nathan Hubbard @n8foo.
    :license: Apache, see LICENSE for more details.
"""

from flask import Flask, request, session, g, redirect, url_for, abort, \
     render_template, flash
from cigarbox.util import *
from cigarbox.orm import *

# create the app
app = Flask(__name__)

# Load default config and override config from config file
app.config.from_object('config')

remoteArchivePath=app.config['REMOTEARCHIVEPATH']

# Utility Functions

def find(lst, key, value):
    for i, dic in enumerate(lst):
        if dic[key] == value:
            return i
    return -1

# URL Routing

@app.errorhandler(404)
def page_not_found(error):
    return render_template('404.html'), 404

@app.teardown_appcontext
def close_db(error):
    """Closes the database again at the end of the request."""
    db = getattr(g, '_database', None)
    if db is not None:
        db.close()

@app.route('/', defaults={'page': 1})
@app.route('/photostream', defaults={'page': 1})
@app.route('/photostream/page/<int:page>')
def photostream(page):
    """the list of the most recently added pictures"""
    baseurl = '%s/photostream' % (app.config['SITEURL'])
    photos = Photo.select()
    photos = photos.order_by(Photo.id.desc())
    photos = photos.paginate(page,app.config['PER_PAGE'])
    for photo in photos:
        (sha1Path,filename) = getSha1Path(photo.sha1)
        photo.uri = sha1Path + '/' + filename
    return render_template('photostream.html', photos=photos, page=page, baseurl=baseurl)

@app.route('/photos/<int:photo_id>')
def show_photo(photo_id):
    """a single photo"""
    photo = Photo.select().where(Photo.id == photo_id).get()
    (sha1Path,filename) = getSha1Path(photo.sha1)
    photo.uri = sha1Path + '/' + filename
    tags = Tag.select().join(PhotoTag).where(PhotoTag.photo == photo_id)
    return render_template('photos.html', photo=photo, tags=tags)

@app.route('/photos/<int:photo_id>/original')
def show_original_photo(photo_id):
    """a single authenticated original photo"""
    photo = Photo.select().where(Photo.id == photo_id).get()
    (sha1Path,filename) = getSha1Path(photo.sha1)
    S3Key = '/'+sha1Path+'/'+filename+'.'+photo.filetype
    if session:
        if session['logged_in'] == True:
            originalURL = cigarbox.aws.getPrivateURL(app.config,S3Key)
            return redirect(originalURL)
    else:
        return redirect('/login',code=302)

@app.route('/tags')
def show_tags():
    tags = Tag.select().annotate(PhotoTag)
    return render_template('tag_cloud.html', tags=tags)

@app.route('/tags/<string:tag>', defaults={'page': 1})
@app.route('/tags/<string:tag>/page/<int:page>')
def show_taged_photos(tag,page):
    baseurl = '%s/tags/%s' % (app.config['SITEURL'],tag)
    photos = Photo.select().join(PhotoTag).join(Tag)
    photos = photos.where(Tag.name == tag)
    photos = photos.order_by(Photo.id.desc())
    photos = photos.paginate(page,app.config['PER_PAGE'])
    for photo in photos:
        (sha1Path,filename) = getSha1Path(photo.sha1)
        photo.uri = sha1Path + '/' + filename
    return render_template('photostream.html', photos=photos, page=page, baseurl=baseurl)

@app.route('/photosets', defaults={'page': 1})
@app.route('/photosets/page/<int:page>')
def show_photosets(page):
    thumbCount = 2
    baseurl = '%s/photosets' % (app.config['SITEURL'])
    photosets = Photoset.select()
    photosets = photosets.order_by(Photoset.ts.desc())
    photosets = photosets.paginate(page,app.config['PER_PAGE'])
    for photoset in photosets:
        thumbs = Photo.select().join(PhotoPhotoset).join(Photoset)
        thumbs = thumbs.where(Photoset.id == photoset.id)
        thumbs = thumbs.limit(thumbCount)
        thumbs = thumbs.order_by(Photo.datetaken.asc())
        for thumb in thumbs:
            thumb.uri = '%s/%s_t.jpg' % (getSha1Path(thumb.sha1))
        photoset.thumbs = thumbs
    return render_template('photosets.html', photosets=photosets, page=page, baseurl=baseurl)

@app.route('/photosets/<int:photoset_id>', defaults={'page': 1})
@app.route('/photosets/<int:photoset_id>/page/<int:page>')
def show_photoset(photoset_id,page):
    photos = Photo.select().join(PhotoPhotoset).join(Photoset)
    photos = photos.where(Photoset.id == photoset_id)
    photos = photos.order_by(Photo.datetaken.asc())
    photos = photos.paginate(page,100)
    for photo in photos:
        (sha1Path,filename) = getSha1Path(photo.sha1)
        photo.uri = sha1Path + '/' + filename
    photoset = Photoset.select().where(Photoset.id == photoset_id)
    photoset = photoset.get()
    return render_template('photoset.html', photos=photos, photoset=photoset, page=page)

@app.route('/add', methods=['POST'])
def add_photo():
    if not session.get('logged_in'):
        abort(401)
    #db = get_db()
    #db.execute('insert into photos (title, text) values (?, ?)',
    #             [request.form['title'], request.form['text']])
    #db.commit()
    flash('New photo was successfully posted')
    return redirect(url_for('show_photos'))

@app.route('/about')
def about():
    return render_template('about.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == 'POST':
        if request.form['username'] != app.config['USERNAME']:
            error = 'Invalid username'
        elif request.form['password'] != app.config['PASSWORD']:
            error = 'Invalid password'
        else:
            session['logged_in'] = True
            flash('You were logged in')
            return redirect(url_for('photostream'))
    return render_template('login.html', error=error)

@app.route('/logout')
def logout():
    session.pop('logged_in', None)
    flash('You were logged out')
    return redirect(url_for('photostream'))

if __name__ == '__main__':
    #init_db()
    app.run()<commit_msg>small original display bug - class scope change<commit_after>#! /usr/bin/env python

# -*- coding: utf-8 -*-
"""
    CigarBox
    ~~~~~~

    A smokin' fast personal photostream

    :copyright: (c) 2014 by Nathan Hubbard @n8foo.
    :license: Apache, see LICENSE for more details.
"""

from flask import Flask, request, session, g, redirect, url_for, abort, \
     render_template, flash
from cigarbox.util import *
from cigarbox.orm import *

# create the app
app = Flask(__name__)

# Load default config and override config from config file
app.config.from_object('config')

remoteArchivePath=app.config['REMOTEARCHIVEPATH']

# Utility Functions

def find(lst, key, value):
    for i, dic in enumerate(lst):
        if dic[key] == value:
            return i
    return -1

# URL Routing

@app.errorhandler(404)
def page_not_found(error):
    return render_template('404.html'), 404

@app.teardown_appcontext
def close_db(error):
    """Closes the database again at the end of the request."""
    db = getattr(g, '_database', None)
    if db is not None:
        db.close()

@app.route('/', defaults={'page': 1})
@app.route('/photostream', defaults={'page': 1})
@app.route('/photostream/page/<int:page>')
def photostream(page):
    """the list of the most recently added pictures"""
    baseurl = '%s/photostream' % (app.config['SITEURL'])
    photos = Photo.select()
    photos = photos.order_by(Photo.id.desc())
    photos = photos.paginate(page,app.config['PER_PAGE'])
    for photo in photos:
        (sha1Path,filename) = getSha1Path(photo.sha1)
        photo.uri = sha1Path + '/' + filename
    return render_template('photostream.html', photos=photos, page=page, baseurl=baseurl)

@app.route('/photos/<int:photo_id>')
def show_photo(photo_id):
    """a single photo"""
    photo = Photo.select().where(Photo.id == photo_id).get()
    (sha1Path,filename) = getSha1Path(photo.sha1)
    photo.uri = sha1Path + '/' + filename
    tags = Tag.select().join(PhotoTag).where(PhotoTag.photo == photo_id)
    return render_template('photos.html', photo=photo, tags=tags)

@app.route('/photos/<int:photo_id>/original')
def show_original_photo(photo_id):
    """a single authenticated original photo"""
    photo = Photo.select().where(Photo.id == photo_id).get()
    (sha1Path,filename) = getSha1Path(photo.sha1)
    S3Key = '/'+sha1Path+'/'+filename+'.'+photo.filetype
    if session:
        if session['logged_in'] == True:
            originalURL = aws.getPrivateURL(app.config,S3Key)
            return redirect(originalURL)
    else:
        return redirect('/login',code=302)

@app.route('/tags')
def show_tags():
    tags = Tag.select().annotate(PhotoTag)
    return render_template('tag_cloud.html', tags=tags)

@app.route('/tags/<string:tag>', defaults={'page': 1})
@app.route('/tags/<string:tag>/page/<int:page>')
def show_taged_photos(tag,page):
    baseurl = '%s/tags/%s' % (app.config['SITEURL'],tag)
    photos = Photo.select().join(PhotoTag).join(Tag)
    photos = photos.where(Tag.name == tag)
    photos = photos.order_by(Photo.id.desc())
    photos = photos.paginate(page,app.config['PER_PAGE'])
    for photo in photos:
        (sha1Path,filename) = getSha1Path(photo.sha1)
        photo.uri = sha1Path + '/' + filename
    return render_template('photostream.html', photos=photos, page=page, baseurl=baseurl)

@app.route('/photosets', defaults={'page': 1})
@app.route('/photosets/page/<int:page>')
def show_photosets(page):
    thumbCount = 2
    baseurl = '%s/photosets' % (app.config['SITEURL'])
    photosets = Photoset.select()
    photosets = photosets.order_by(Photoset.ts.desc())
    photosets = photosets.paginate(page,app.config['PER_PAGE'])
    for photoset in photosets:
        thumbs = Photo.select().join(PhotoPhotoset).join(Photoset)
        thumbs = thumbs.where(Photoset.id == photoset.id)
        thumbs = thumbs.limit(thumbCount)
        thumbs = thumbs.order_by(Photo.datetaken.asc())
        for thumb in thumbs:
            thumb.uri = '%s/%s_t.jpg' % (getSha1Path(thumb.sha1))
        photoset.thumbs = thumbs
    return render_template('photosets.html', photosets=photosets, page=page, baseurl=baseurl)

@app.route('/photosets/<int:photoset_id>', defaults={'page': 1})
@app.route('/photosets/<int:photoset_id>/page/<int:page>')
def show_photoset(photoset_id,page):
    photos = Photo.select().join(PhotoPhotoset).join(Photoset)
    photos = photos.where(Photoset.id == photoset_id)
    photos = photos.order_by(Photo.datetaken.asc())
    photos = photos.paginate(page,100)
    for photo in photos:
        (sha1Path,filename) = getSha1Path(photo.sha1)
        photo.uri = sha1Path + '/' + filename
    photoset = Photoset.select().where(Photoset.id == photoset_id)
    photoset = photoset.get()
    return render_template('photoset.html', photos=photos, photoset=photoset, page=page)

@app.route('/add', methods=['POST'])
def add_photo():
    if not session.get('logged_in'):
        abort(401)
    #db = get_db()
    #db.execute('insert into photos (title, text) values (?, ?)',
    #             [request.form['title'], request.form['text']])
    #db.commit()
    flash('New photo was successfully posted')
    return redirect(url_for('show_photos'))

@app.route('/about')
def about():
    return render_template('about.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == 'POST':
        if request.form['username'] != app.config['USERNAME']:
            error = 'Invalid username'
        elif request.form['password'] != app.config['PASSWORD']:
            error = 'Invalid password'
        else:
            session['logged_in'] = True
            flash('You were logged in')
            return redirect(url_for('photostream'))
    return render_template('login.html', error=error)

@app.route('/logout')
def logout():
    session.pop('logged_in', None)
    flash('You were logged out')
    return redirect(url_for('photostream'))

if __name__ == '__main__':
    #init_db()
    app.run()<|endoftext|>


<commit_before># -*- coding: utf8 -*-
# This file is part of PyBossa.
#
# Copyright (C) 2013 SF Isle of Man Limited
#
# PyBossa is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyBossa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with PyBossa.  If not, see <http://www.gnu.org/licenses/>.

from sqlalchemy.sql import text
from pybossa.core import db
from pybossa.cache import memoize, ONE_HOUR
from pybossa.cache.projects import overall_progress

session = db.slave_session

@memoize(timeout=ONE_HOUR * 3)
def n_available_tasks(app_id, user_id=None, user_ip=None):
    """Returns the number of tasks for a given app a user can contribute to,
    based on the completion of the app tasks, and previous task_runs submitted
    by the user"""
    if user_id and not user_ip:
        query = text('''SELECT COUNT(id) AS n_tasks FROM task WHERE NOT EXISTS
                       (SELECT task_id FROM task_run WHERE
                       app_id=:app_id AND user_id=:user_id AND task_id=task.id)
                       AND app_id=:app_id AND state !='completed';''')
        result = session.execute(query, dict(app_id=app_id, user_id=user_id))
    else:
        if not user_ip:
            user_ip = '127.0.0.1'
        query = text('''SELECT COUNT(id) AS n_tasks FROM task WHERE NOT EXISTS
                       (SELECT task_id FROM task_run WHERE
                       app_id=:app_id AND user_ip=:user_ip AND task_id=task.id)
                       AND app_id=:app_id AND state !='completed';''')

        result = session.execute(query, dict(app_id=app_id, user_ip=user_ip))
    n_tasks = 0
    for row in result:
        n_tasks = row.n_tasks
    return n_tasks

def check_contributing_state(app, user_id=None, user_ip=None):
    """Returns the state of a given app for a given user, depending on whether
    the app is completed or not and the user can contribute more to it or not"""

    app_id = app['id'] if type(app) == dict else app.id
    states = ('completed', 'draft', 'can_contribute', 'cannot_contribute')
    if overall_progress(app_id) >= 100:
        return states[0]
    if _has_no_presenter(app) or _has_no_tasks(app_id):
        return states[1]
    if n_available_tasks(app_id, user_id=user_id, user_ip=user_ip) > 0:
        return states[2]
    return states[3]

def add_custom_contrib_button_to(app, user_id_or_ip):
    if type(app) != dict:
        app = app.dictize()
    app['contrib_button'] = check_contributing_state(app, **user_id_or_ip)
    return app

def _has_no_presenter(app):
    try:
        return 'task_presenter' not in app.info
    except AttributeError:
        try:
            return 'task_presenter' not in app.get('info')
        except AttributeError:
            return True

def _has_no_tasks(app_id):
    query = text('''SELECT COUNT(id) AS n_tasks FROM task
               WHERE app_id=:app_id;''')
    result = session.execute(query, dict(app_id=app_id))
    for row in result:
        n_tasks = row.n_tasks
    return n_tasks == 0
<commit_msg>Rename app to projects.<commit_after># -*- coding: utf8 -*-
# This file is part of PyBossa.
#
# Copyright (C) 2013 SF Isle of Man Limited
#
# PyBossa is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyBossa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with PyBossa.  If not, see <http://www.gnu.org/licenses/>.

from sqlalchemy.sql import text
from pybossa.core import db
from pybossa.cache import memoize, ONE_HOUR
from pybossa.cache.projects import overall_progress

session = db.slave_session

@memoize(timeout=ONE_HOUR * 3)
def n_available_tasks(project_id, user_id=None, user_ip=None):
    """Returns the number of tasks for a given project a user can contribute to,
    based on the completion of the project tasks, and previous task_runs submitted
    by the user"""
    if user_id and not user_ip:
        query = text('''SELECT COUNT(id) AS n_tasks FROM task WHERE NOT EXISTS
                       (SELECT task_id FROM task_run WHERE
                       app_id=:app_id AND user_id=:user_id AND task_id=task.id)
                       AND app_id=:project_id AND state !='completed';''')
        result = session.execute(query, dict(project_id=project_id,
                                             user_id=user_id))
    else:
        if not user_ip:
            user_ip = '127.0.0.1'
        query = text('''SELECT COUNT(id) AS n_tasks FROM task WHERE NOT EXISTS
                       (SELECT task_id FROM task_run WHERE
                       app_id=:app_id AND user_ip=:user_ip AND task_id=task.id)
                       AND app_id=:project_id AND state !='completed';''')

        result = session.execute(query, dict(project_id=project_id,
                                             user_ip=user_ip))
    n_tasks = 0
    for row in result:
        n_tasks = row.n_tasks
    return n_tasks

def check_contributing_state(app, user_id=None, user_ip=None):
    """Returns the state of a given app for a given user, depending on whether
    the app is completed or not and the user can contribute more to it or not"""

    app_id = app['id'] if type(app) == dict else app.id
    states = ('completed', 'draft', 'can_contribute', 'cannot_contribute')
    if overall_progress(app_id) >= 100:
        return states[0]
    if _has_no_presenter(app) or _has_no_tasks(app_id):
        return states[1]
    if n_available_tasks(app_id, user_id=user_id, user_ip=user_ip) > 0:
        return states[2]
    return states[3]

def add_custom_contrib_button_to(app, user_id_or_ip):
    if type(app) != dict:
        app = app.dictize()
    app['contrib_button'] = check_contributing_state(app, **user_id_or_ip)
    return app

def _has_no_presenter(app):
    try:
        return 'task_presenter' not in app.info
    except AttributeError:
        try:
            return 'task_presenter' not in app.get('info')
        except AttributeError:
            return True

def _has_no_tasks(app_id):
    query = text('''SELECT COUNT(id) AS n_tasks FROM task
               WHERE app_id=:app_id;''')
    result = session.execute(query, dict(app_id=app_id))
    for row in result:
        n_tasks = row.n_tasks
    return n_tasks == 0
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#!/usr/bin/python
##-------------------------------------------------------------------
## @copyright 2017 DennyZhang.com
## Licensed under MIT
##   https://raw.githubusercontent.com/DennyZhang/devops_public/master/LICENSE
##
## File : cleanup_old_files.py
## Author : Denny <denny@dennyzhang.com>
## Created : <2017-05-03>
## Updated: Time-stamp: <2017-05-08 17:15:52>
## Description :
##    Remove old files in a safe and organized way
## Sample:
##    # Remove files: Check /opt/app and remove files naming "app-.*-SNAPSHOT.jar". But keep latest 2 copies
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-*-SNAPSHOT.jar" \
##               --cleanup_type file --min_copies 3 --min_size_mb 10
##
##    # Only list delete candidates, instead of perform the actual changes
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-*-SNAPSHOT.jar" \
##               --examine_only
##
##    # Remove files: Only cleanup files over 200MB
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-*-SNAPSHOT.jar" \
##               --cleanup_type file --min_size_mb 200
##
##    # Remove folders: Cleanup subdirectories, keeping latest 2 directories
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "*" --cleanup_type directory
##-------------------------------------------------------------------
import os, sys
import argparse
import glob
import shutil

import logging
log_file = "/var/log/cleanup_old_files.log"

logging.basicConfig(filename=log_file, level=logging.DEBUG, format='%(asctime)s %(message)s')

def list_old_files(filename_pattern, min_copies, min_size_mb):
    l = []
    files = glob.glob(filename_pattern)
    files.sort(key=os.path.getmtime, reverse=True)
    i = 0
    for f in files:
        if os.path.isfile(f) is False:
            continue
        # skip too small files
        filesize_mb = os.stat(f).st_size/(1000*1000)
        if filesize_mb < min_size_mb:
            continue
        i = i + 1
        if i > min_copies:
            l.append(f)
    return l

def list_old_folders(filename_pattern, min_copies):
    l = []
    files = glob.glob(filename_pattern)
    files.sort(key=os.path.getmtime, reverse=True)
    i = 0
    for f in files:
        if os.path.isdir(f) is False:
            continue
        i = i + 1
        if i > min_copies:
            l.append(f)
    return l

if __name__ == '__main__':
    # get parameters from users
    parser = argparse.ArgumentParser()
    parser.add_argument('--working_dir', required=True, \
                        help="Perform cleanup under which directory", type=str)

    parser.add_argument('--examine_only', dest='examine_only', action='store_true', default=False, \
                        help="Only list delete candidates, instead perform the actual removal")

    parser.add_argument('--filename_pattern', required=False, default=".*", \
                        help="Filter files/directories by filename, before cleanup", type=str)
    parser.add_argument('--cleanup_type', required=False, default='file', \
                        help="Whether to perform the cleanup for files or directories", type=str)
    parser.add_argument('--min_copies', default=3, required=False, \
                        help='minimal copies to keep, before removal.', type=int)
    parser.add_argument('--min_size_mb', default='10', required=False, \
                        help='When remove files, skip files too small. It will be skipped when removing directories', type=str)
    l = parser.parse_args()

    working_dir = l.working_dir
    examine_only = l.examine_only
    cleanup_type = l.cleanup_type.lower()
    filename_pattern = l.filename_pattern
    min_copies = l.min_copies
    min_size_mb = l.min_size_mb

    logging.info("Start to run cleanup for folder: %s." % working_dir)
    if os.path.exists(working_dir) is False:
        logging.warning("Directory(%s) doesn't exists." % (working_dir))
        sys.exit(0)

    os.chdir(working_dir)
    if cleanup_type == 'file':
        l = list_old_files(filename_pattern, min_copies, min_size_mb)
    else:
        l = list_old_folders(filename_pattern, min_copies)

    if l == []:
        logging.info("No matched files/directories to be clean.")
        sys.exit(0)
    else:
        if examine_only is True:
            logging.info("Below files/directories are selected to be removed: %s.\n"\
                         "Skip following removal, since --examine_only has already been given." \
                         % ",".join(l))
            sys.exit(0)
        else:
            # Perform the actual removal
            for f in l:
                if cleanup_type == 'file':
                    logging.info("Remove file. %s/%s." % (working_dir, f))
                else:
                    logging.info("Remove folder: %s/%s." % (working_dir, f))
                # TODO: error handling
                shutil.rmtree(f)
            logging.info("Cleanup is done.")
## File : cleanup_old_files.py ends
<commit_msg>python log to both logfile and terminal<commit_after># -*- coding: utf-8 -*-
#!/usr/bin/python
##-------------------------------------------------------------------
## @copyright 2017 DennyZhang.com
## Licensed under MIT
##   https://raw.githubusercontent.com/DennyZhang/devops_public/master/LICENSE
##
## File : cleanup_old_files.py
## Author : Denny <denny@dennyzhang.com>
## Created : <2017-05-03>
## Updated: Time-stamp: <2017-05-08 17:19:20>
## Description :
##    Remove old files in a safe and organized way
## Sample:
##    # Remove files: Check /opt/app and remove files naming "app-.*-SNAPSHOT.jar". But keep latest 2 copies
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-*-SNAPSHOT.jar" \
##               --cleanup_type file --min_copies 3 --min_size_mb 10
##
##    # Only list delete candidates, instead of perform the actual changes
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-*-SNAPSHOT.jar" \
##               --examine_only
##
##    # Remove files: Only cleanup files over 200MB
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "app-*-SNAPSHOT.jar" \
##               --cleanup_type file --min_size_mb 200
##
##    # Remove folders: Cleanup subdirectories, keeping latest 2 directories
##    python cleanup_old_files.py --working_dir "/opt/app" --filename_pattern "*" --cleanup_type directory
##-------------------------------------------------------------------
import os, sys
import argparse
import glob
import shutil

import logging
log_file = "/var/log/cleanup_old_files.log"

logging.basicConfig(filename=log_file, level=logging.DEBUG, format='%(asctime)s %(message)s')
logging.getLogger().addHandler(logging.StreamHandler())

def list_old_files(filename_pattern, min_copies, min_size_mb):
    l = []
    files = glob.glob(filename_pattern)
    files.sort(key=os.path.getmtime, reverse=True)
    i = 0
    for f in files:
        if os.path.isfile(f) is False:
            continue
        # skip too small files
        filesize_mb = os.stat(f).st_size/(1000*1000)
        if filesize_mb < min_size_mb:
            continue
        i = i + 1
        if i > min_copies:
            l.append(f)
    return l

def list_old_folders(filename_pattern, min_copies):
    l = []
    files = glob.glob(filename_pattern)
    files.sort(key=os.path.getmtime, reverse=True)
    i = 0
    for f in files:
        if os.path.isdir(f) is False:
            continue
        i = i + 1
        if i > min_copies:
            l.append(f)
    return l

if __name__ == '__main__':
    # get parameters from users
    parser = argparse.ArgumentParser()
    parser.add_argument('--working_dir', required=True, \
                        help="Perform cleanup under which directory", type=str)

    parser.add_argument('--examine_only', dest='examine_only', action='store_true', default=False, \
                        help="Only list delete candidates, instead perform the actual removal")

    parser.add_argument('--filename_pattern', required=False, default=".*", \
                        help="Filter files/directories by filename, before cleanup", type=str)
    parser.add_argument('--cleanup_type', required=False, default='file', \
                        help="Whether to perform the cleanup for files or directories", type=str)
    parser.add_argument('--min_copies', default=3, required=False, \
                        help='minimal copies to keep, before removal.', type=int)
    parser.add_argument('--min_size_mb', default='10', required=False, \
                        help='When remove files, skip files too small. It will be skipped when removing directories', type=str)
    l = parser.parse_args()

    working_dir = l.working_dir
    examine_only = l.examine_only
    cleanup_type = l.cleanup_type.lower()
    filename_pattern = l.filename_pattern
    min_copies = l.min_copies
    min_size_mb = l.min_size_mb

    logging.info("Start to run cleanup for folder: %s." % working_dir)
    if os.path.exists(working_dir) is False:
        logging.warning("Directory(%s) doesn't exists." % (working_dir))
        sys.exit(0)

    os.chdir(working_dir)
    if cleanup_type == 'file':
        l = list_old_files(filename_pattern, min_copies, min_size_mb)
    else:
        l = list_old_folders(filename_pattern, min_copies)

    if l == []:
        logging.info("No matched files/directories to be clean.")
        sys.exit(0)
    else:
        if examine_only is True:
            logging.info("Below files/directories are selected to be removed: %s.\n"\
                         "Skip following removal, since --examine_only has already been given." \
                         % ",".join(l))
            sys.exit(0)
        else:
            # Perform the actual removal
            for f in l:
                if cleanup_type == 'file':
                    logging.info("Remove file. %s/%s." % (working_dir, f))
                else:
                    logging.info("Remove folder: %s/%s." % (working_dir, f))
                # TODO: error handling
                shutil.rmtree(f)
            logging.info("Cleanup is done.")
## File : cleanup_old_files.py ends
<|endoftext|>


<commit_before>#!/usr/bin/env python3

import shutil
import argparse
import subprocess
import os, os.path
from PIL import Image, ImageDraw

parser = argparse.ArgumentParser()
parser.add_argument('filename', type=str, help='movie filename')
parser.add_argument('-bw', '--barwidth', type=int,
                    help='Set the width of the bars in the barcode. Default is 5px.')
parser.add_argument('-ht', '--height', type=int,
                    help='Set the height of the barcode. Default is 1200px.')
parser.add_argument('-w', '--width', type=int,
                    help='Set the final width of the barcode. Default is 1920px.')
parser.add_argument('-nd', '--nodelete', action='store_true',
                    help='Won\'t delete the movie frames after done executing.')
parser.add_argument('-nf', '--noframes', action='store_true',
                    help='Won\'t create the frames. Must already have them in a directory called frames.')
parser.add_argument('-fr', '--framerate', type=int,
                    help='Set the framerate for breaking the movie into frames. Default is 25.')
args = parser.parse_args()

def create_movie_frames(infile):
    os.chdir('./frames')

    ffmpeg_args = ['ffmpeg', '-i', '../' + infile, '-f', 'image2', 'image-%07d.png']

    if args.framerate:
        ffmpeg_args += ['-r', framerate]

    output = open('ffmpeg_log.txt', 'w')

    print('Creating frames (this will take a while)...')
    subprocess.run(ffmpeg_args, stderr=output, stdout=subprocess.PIPE)

    output.close()

def find_frame_bar_color(infile, last_col=None):
    img = Image.open(infile)
    result = img.convert('P', palette=Image.ADAPTIVE)
    result.putalpha(0)
    colors = result.getcolors()

    if last_col == colors[0][1]:
        return last_col

    return colors[0][1]

def get_image_colors():
    colors = []
    last_col = None
    print('Getting frame colors (this could take a while)...')
    for img in os.listdir(os.getcwd()):
        if img.endswith('.png'):
            last_col = find_frame_bar_color(img, last_col=last_col)

            if not colors:
                colors.append(last_col)
            elif colors[-1] != last_col:
                colors.append(last_col)

    # return back to the original directory
    os.chdir('..')

    return colors

def create_barcode(colors, bar_width=5, height=1200, width=1920):
    barcode_width = len(colors) + bar_width
    bc = Image.new('RGB', (barcode_width, height))
    draw = ImageDraw.Draw(bc)

    posx = 0

    print('Creating barcode...')
    for color in colors:
        draw.rectangle([posx, 0, posx + bar_width, height], fill=color)
        posx += bar_width

    del draw

    bc.resize((width, height))
    bc.save('barcode.png', 'PNG')

def main():
    try:
        # attempt to create the directory structure if it doesn't exist
        os.mkdir('frames')
    except FileExistsError:
        pass

    fname = args.filename
    bar_width=5
    height=1200
    width=1920

    if args.barwidth: bar_width = args.barwidth
    if args.height: bar_height = args.height
    if args.width: width = args.width

    if not args.noframes:
        create_movie_frames(fname)

    colors = get_image_colors()
    create_barcode(colors, bar_width=bar_width, height=height, width=width)

    if not args.nodelete:
        print('Cleaning up...')
        shutil.rmtree('frames')

if __name__ == '__main__':
    main()<commit_msg>switched from subprocess.run to subprocess.call<commit_after>#!/usr/bin/env python3

import shutil
import argparse
import subprocess
import os, os.path
from PIL import Image, ImageDraw

parser = argparse.ArgumentParser()
parser.add_argument('filename', type=str, help='movie filename')
parser.add_argument('-bw', '--barwidth', type=int,
                    help='Set the width of the bars in the barcode. Default is 5px.')
parser.add_argument('-ht', '--height', type=int,
                    help='Set the height of the barcode. Default is 1200px.')
parser.add_argument('-w', '--width', type=int,
                    help='Set the final width of the barcode. Default is 1920px.')
parser.add_argument('-nd', '--nodelete', action='store_true',
                    help='Won\'t delete the movie frames after done executing.')
parser.add_argument('-nf', '--noframes', action='store_true',
                    help='Won\'t create the frames. Must already have them in a directory called frames.')
parser.add_argument('-fr', '--framerate', type=int,
                    help='Set the framerate for breaking the movie into frames. Default is 25.')
args = parser.parse_args()

def create_movie_frames(infile):
    os.chdir('./frames')

    ffmpeg_args = ['ffmpeg', '-i', '../' + infile, '-f', 'image2', 'image-%07d.png']

    if args.framerate:
        ffmpeg_args += ['-r', framerate]

    output = open('ffmpeg_log.txt', 'w')

    print('Creating frames (this will take a while)...')
    subprocess.call(ffmpeg_args, stderr=output, stdout=subprocess.PIPE)

    output.close()

def find_frame_bar_color(infile, last_col=None):
    img = Image.open(infile)
    result = img.convert('P', palette=Image.ADAPTIVE)
    result.putalpha(0)
    colors = result.getcolors()

    if last_col == colors[0][1]:
        return last_col

    return colors[0][1]

def get_image_colors():
    colors = []
    last_col = None
    print('Getting frame colors (this could take a while)...')
    for img in os.listdir(os.getcwd()):
        if img.endswith('.png'):
            last_col = find_frame_bar_color(img, last_col=last_col)

            if not colors:
                colors.append(last_col)
            elif colors[-1] != last_col:
                colors.append(last_col)

    # return back to the original directory
    os.chdir('..')

    return colors

def create_barcode(colors, bar_width=5, height=1200, width=1920):
    barcode_width = len(colors) + bar_width
    bc = Image.new('RGB', (barcode_width, height))
    draw = ImageDraw.Draw(bc)

    posx = 0

    print('Creating barcode...')
    for color in colors:
        draw.rectangle([posx, 0, posx + bar_width, height], fill=color)
        posx += bar_width

    del draw

    bc.resize((width, height))
    bc.save('barcode.png', 'PNG')

def main():
    try:
        # attempt to create the directory structure if it doesn't exist
        os.mkdir('frames')
    except FileExistsError:
        pass

    fname = args.filename
    bar_width=5
    height=1200
    width=1920

    if args.barwidth: bar_width = args.barwidth
    if args.height: bar_height = args.height
    if args.width: width = args.width

    if not args.noframes:
        create_movie_frames(fname)

    colors = get_image_colors()
    create_barcode(colors, bar_width=bar_width, height=height, width=width)

    if not args.nodelete:
        print('Cleaning up...')
        shutil.rmtree('frames')

if __name__ == '__main__':
    main()<|endoftext|>


<commit_before># encoding: utf-8
import operator

from django.conf import settings
from django.contrib import messages
from django.db.models import Q

from braces.views import AccessMixin, StaffuserRequiredMixin

from .forms import SearchForm

class MessageMixin(object):
    """
    Make it easy to display notification messages when using Class Based Views.
    """
    def delete(self, request, *args, **kwargs):
        if not self.request.is_ajax() and hasattr(self, 'success_message'):
            messages.success(self.request, self.success_message)
        return super(MessageMixin, self).delete(request, *args, **kwargs)

    def form_valid(self, form):
        if not self.request.is_ajax() and hasattr(self, 'success_message'):
            messages.success(self.request, self.success_message)
        return super(MessageMixin, self).form_valid(form)

    def forms_valid(self, *args, **kwargs):
        if not self.request.is_ajax() and hasattr(self, 'success_message'):
            messages.success(self.request, self.success_message)
        return super(MessageMixin, self).forms_valid(*args, **kwargs)

    def formset_valid(self, form):
        if not self.request.is_ajax() and hasattr(self, 'success_message'):
            messages.success(self.request, self.success_message)
        return super(MessageMixin, self).formset_valid(form)

    def form_invalid(self, form):
        if not self.request.is_ajax() and hasattr(self, 'error_message'):
            messages.error(self.request, self.error_message)
        return super(MessageMixin, self).form_invalid(form)

    def forms_invalid(self, *args, **kwargs):
        if not self.request.is_ajax() and hasattr(self, 'error_message'):
            messages.error(self.request, self.error_message)
        return super(MessageMixin, self).forms_invalid(*args, **kwargs)

    def formset_invalid(self, form):
        if not self.request.is_ajax() and hasattr(self, 'error_message'):
            messages.error(self.request, self.error_message)
        return super(MessageMixin, self).formset_invalid(form)

    def set_value(self, request, *args, **kwargs):
        if not self.request.is_ajax() and hasattr(self, 'success_message'):
            messages.success(self.request, self.success_message)
        return super(MessageMixin, self).set_value(request, *args, **kwargs)

class PermissionRequiredMixin(AccessMixin):
    """
    ** Modified brack3t django-braces mixin to allow a bypass **

    View mixin which verifies that the logged in user has the specified
    permission.

    Class Settings
    `permission_required` - the permission to check for or False to skip check
    `login_url` - the login url of site
    `redirect_field_name` - defaults to "next"
    `raise_exception` - defaults to False - raise 403 if set to True

    Example Usage

        class SomeView(PermissionRequiredMixin, ListView):
            ...
            # required
            permission_required = "app.permission"

            # optional
            login_url = "/signup/"
            redirect_field_name = "hollaback"
            raise_exception = True
            ...
    """
    permission_required = None  # Default required perms to none

    def dispatch(self, request, *args, **kwargs):
        # Make sure that the permission_required attribute is set on the
        # view, or raise a configuration error.
        if self.permission_required is None:
            raise ImproperlyConfigured(
                "'PermissionRequiredMixin' requires "
                "'permission_required' attribute to be set.")

        if self.permission_required is not False:
            # Check to see if the request's user has the required permission.
            has_permission = request.user.has_perm(self.permission_required)

            if not has_permission:  # If the user lacks the permission
                if self.raise_exception:  # *and* if an exception was desired
                    raise PermissionDenied  # return a forbidden response.
                else:
                    return redirect_to_login(request.get_full_path(),
                                             self.get_login_url(),
                                             self.get_redirect_field_name())

        return super(PermissionRequiredMixin, self).dispatch(
            request, *args, **kwargs)

class StaffViewMixin(MessageMixin, StaffuserRequiredMixin,
                     PermissionRequiredMixin):
    pass

class PaginateMixin(object):
    """
    Adds page size support to a ListView.
    """
    def get_paginate_by(self, *args, **kwargs):
        try:
            paginate_by = self.request.GET['per-page']
            paginate_by = int(paginate_by)
        except (KeyError, ValueError):
            try:
                paginate_by = settings.PAGINATION_PAGE_SIZES[0]
            except (AttributeError, IndexError):
                paginate_by = 20
        return paginate_by

class OrderByMixin(object):
    """
    Add support for ordering the queryset in your ListView.
    """
    def get_queryset(self):
        qs = super(OrderByMixin, self).get_queryset()
        if qs:
            sort_col = self.request.GET.get('sort-col')
            sort_dir = self.request.GET.get('sort-dir', '')
            if sort_col:
                order_by = '%s%s' % ('-' if sort_dir == 'desc' else '', sort_col)
                qs = qs.order_by(order_by)
        return qs

    def get_context_data(self, **kwargs):
        context_data = super(OrderByMixin, self).get_context_data(**kwargs)
        context_data.update({
            'sort-col': self.request.GET.get('sort-col', ''),
            'sort-dir': self.request.GET.get('sort-dir', ''),
        })
        return context_data

class SearchFormMixin(object):
    """
    Present a form element to filter a ListView, this class reimplements
    some of FormMixin due to it missing a super() call in get_context_data
    """
    search_form = None
    search_form_class = SearchForm
    search_filter = None
    initial = {}

    def get_initial(self):
        return self.initial.copy()

    def make_form(self, request):
        # Store the request so we can add success/failure messages.
        self.search_request = request
        # Searching uses GET at the moment. We create a bound form only if
        # any fields were passed, this prevents the form failing validation
        # when a user first hits a view we are mixed in to.
        if 'search' in request.GET:
            form = self.search_form_class(request.GET)
        else:
            form = self.search_form_class(self.get_initial())
        return form

    def get_queryset(self):
        # First handle any other processing that must be done
        qs = super(SearchFormMixin, self).get_queryset()
        if qs and self.search_filter:
            # If we have a queryset and a filter to apply to it, build a list
            # of fields to search and what to search them with.
            predicates = [
                (k + '__' + self.search_fields[k], self.search_filter) \
                for k in self.search_fields.keys()]
            # Build that into a list of Q filter objects
            qsfilter = [Q(x) for x in predicates]
            # Join them together with the or operator and pass them to filter
            newqs = qs.filter(reduce(operator.or_, qsfilter)).distinct()
            # If we get any items back, the search worked
            count = newqs.count()
            if count > 0:
                qs = newqs
                messages.success(
                    self.search_request, str(count) + ' item(s) found.')
            else:
                messages.warning(
                    self.search_request, 'Search returned no results.')
        return qs

    def get_context_data(self, **kwargs):
        # Add the search form to the page
        kwargs['search_form'] = self.search_form
        return super(SearchFormMixin, self).get_context_data(**kwargs)

    def get(self, request, *args, **kwargs):
        self.search_form = self.make_form(request)
        if self.search_form.is_valid():
            # If the user entered any data, store it so get_queryset can
            # use it.
            self.search_filter = self.search_form.cleaned_data['search']
        return super(SearchFormMixin, self).get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        self.search_form = self.make_form(request)
        if self.search_form.is_valid():
            self.search_filter = self.search_form.cleaned_data['search']
        return super(SearchFormMixin, self).post(request, *args, **kwargs)
<commit_msg>Add redirect mixin and apply to staff mixin.<commit_after># encoding: utf-8
import operator
import urlparse

from django.conf import settings
from django.contrib import messages
from django.core.urlresolvers import resolve, Resolver404
from django.db.models import Q

from braces.views import AccessMixin, StaffuserRequiredMixin

from .forms import SearchForm

class MessageMixin(object):
    """
    Make it easy to display notification messages when using Class Based Views.
    """
    def delete(self, request, *args, **kwargs):
        if not self.request.is_ajax() and hasattr(self, 'success_message'):
            messages.success(self.request, self.success_message)
        return super(MessageMixin, self).delete(request, *args, **kwargs)

    def form_valid(self, form):
        if not self.request.is_ajax() and hasattr(self, 'success_message'):
            messages.success(self.request, self.success_message)
        return super(MessageMixin, self).form_valid(form)

    def forms_valid(self, *args, **kwargs):
        if not self.request.is_ajax() and hasattr(self, 'success_message'):
            messages.success(self.request, self.success_message)
        return super(MessageMixin, self).forms_valid(*args, **kwargs)

    def formset_valid(self, form):
        if not self.request.is_ajax() and hasattr(self, 'success_message'):
            messages.success(self.request, self.success_message)
        return super(MessageMixin, self).formset_valid(form)

    def form_invalid(self, form):
        if not self.request.is_ajax() and hasattr(self, 'error_message'):
            messages.error(self.request, self.error_message)
        return super(MessageMixin, self).form_invalid(form)

    def forms_invalid(self, *args, **kwargs):
        if not self.request.is_ajax() and hasattr(self, 'error_message'):
            messages.error(self.request, self.error_message)
        return super(MessageMixin, self).forms_invalid(*args, **kwargs)

    def formset_invalid(self, form):
        if not self.request.is_ajax() and hasattr(self, 'error_message'):
            messages.error(self.request, self.error_message)
        return super(MessageMixin, self).formset_invalid(form)

    def set_value(self, request, *args, **kwargs):
        if not self.request.is_ajax() and hasattr(self, 'success_message'):
            messages.success(self.request, self.success_message)
        return super(MessageMixin, self).set_value(request, *args, **kwargs)

class PermissionRequiredMixin(AccessMixin):
    """
    ** Modified brack3t django-braces mixin to allow a bypass **

    View mixin which verifies that the logged in user has the specified
    permission.

    Class Settings
    `permission_required` - the permission to check for or False to skip check
    `login_url` - the login url of site
    `redirect_field_name` - defaults to "next"
    `raise_exception` - defaults to False - raise 403 if set to True

    Example Usage

        class SomeView(PermissionRequiredMixin, ListView):
            ...
            # required
            permission_required = "app.permission"

            # optional
            login_url = "/signup/"
            redirect_field_name = "hollaback"
            raise_exception = True
            ...
    """
    permission_required = None  # Default required perms to none

    def dispatch(self, request, *args, **kwargs):
        # Make sure that the permission_required attribute is set on the
        # view, or raise a configuration error.
        if self.permission_required is None:
            raise ImproperlyConfigured(
                "'PermissionRequiredMixin' requires "
                "'permission_required' attribute to be set.")

        if self.permission_required is not False:
            # Check to see if the request's user has the required permission.
            has_permission = request.user.has_perm(self.permission_required)

            if not has_permission:  # If the user lacks the permission
                if self.raise_exception:  # *and* if an exception was desired
                    raise PermissionDenied  # return a forbidden response.
                else:
                    return redirect_to_login(request.get_full_path(),
                                             self.get_login_url(),
                                             self.get_redirect_field_name())

        return super(PermissionRequiredMixin, self).dispatch(
            request, *args, **kwargs)

class RedirectToNextMixin(object):
    def post(self, *args, **kwargs):
        next = self.request.GET.get('next')
        if next:
            current_url = self.request.get_full_path()
            redirect_url = current_url.split('next=')[1]
            try:
                split = urlparse.urlsplit(redirect_url)
                resolve(split.path)
                self.success_url = split.path
            except Resolver404:
                pass
        return super(RedirectToNextMixin, self).post(*args, **kwargs)

class StaffViewMixin(MessageMixin, RedirectToNextMixin, StaffuserRequiredMixin,
                     PermissionRequiredMixin):
    pass

class PaginateMixin(object):
    """
    Adds page size support to a ListView.
    """
    def get_paginate_by(self, *args, **kwargs):
        try:
            paginate_by = self.request.GET['per-page']
            paginate_by = int(paginate_by)
        except (KeyError, ValueError):
            try:
                paginate_by = settings.PAGINATION_PAGE_SIZES[0]
            except (AttributeError, IndexError):
                paginate_by = 20
        return paginate_by

class OrderByMixin(object):
    """
    Add support for ordering the queryset in your ListView.
    """
    def get_queryset(self):
        qs = super(OrderByMixin, self).get_queryset()
        if qs:
            sort_col = self.request.GET.get('sort-col')
            sort_dir = self.request.GET.get('sort-dir', '')
            if sort_col:
                order_by = '%s%s' % ('-' if sort_dir == 'desc' else '', sort_col)
                qs = qs.order_by(order_by)
        return qs

    def get_context_data(self, **kwargs):
        context_data = super(OrderByMixin, self).get_context_data(**kwargs)
        context_data.update({
            'sort-col': self.request.GET.get('sort-col', ''),
            'sort-dir': self.request.GET.get('sort-dir', ''),
        })
        return context_data

class SearchFormMixin(object):
    """
    Present a form element to filter a ListView, this class reimplements
    some of FormMixin due to it missing a super() call in get_context_data
    """
    search_form = None
    search_form_class = SearchForm
    search_filter = None
    initial = {}

    def get_initial(self):
        return self.initial.copy()

    def make_form(self, request):
        # Store the request so we can add success/failure messages.
        self.search_request = request
        # Searching uses GET at the moment. We create a bound form only if
        # any fields were passed, this prevents the form failing validation
        # when a user first hits a view we are mixed in to.
        if 'search' in request.GET:
            form = self.search_form_class(request.GET)
        else:
            form = self.search_form_class(self.get_initial())
        return form

    def get_queryset(self):
        # First handle any other processing that must be done
        qs = super(SearchFormMixin, self).get_queryset()
        if qs and self.search_filter:
            # If we have a queryset and a filter to apply to it, build a list
            # of fields to search and what to search them with.
            predicates = [
                (k + '__' + self.search_fields[k], self.search_filter) \
                for k in self.search_fields.keys()]
            # Build that into a list of Q filter objects
            qsfilter = [Q(x) for x in predicates]
            # Join them together with the or operator and pass them to filter
            newqs = qs.filter(reduce(operator.or_, qsfilter)).distinct()
            # If we get any items back, the search worked
            count = newqs.count()
            if count > 0:
                qs = newqs
                messages.success(
                    self.search_request, str(count) + ' item(s) found.')
            else:
                messages.warning(
                    self.search_request, 'Search returned no results.')
        return qs

    def get_context_data(self, **kwargs):
        # Add the search form to the page
        kwargs['search_form'] = self.search_form
        return super(SearchFormMixin, self).get_context_data(**kwargs)

    def get(self, request, *args, **kwargs):
        self.search_form = self.make_form(request)
        if self.search_form.is_valid():
            # If the user entered any data, store it so get_queryset can
            # use it.
            self.search_filter = self.search_form.cleaned_data['search']
        return super(SearchFormMixin, self).get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        self.search_form = self.make_form(request)
        if self.search_form.is_valid():
            self.search_filter = self.search_form.cleaned_data['search']
        return super(SearchFormMixin, self).post(request, *args, **kwargs)
<|endoftext|>


<commit_before># Copyright 2015 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import logging

from subiquitycore import i18n
from subiquity.client.controller import SubiquityTuiController
from subiquity.ui.views.welcome import WelcomeView

log = logging.getLogger('subiquity.client.controllers.welcome')

class WelcomeController(SubiquityTuiController):

    endpoint_name = 'locale'

    async def make_ui(self):
        language = await self.endpoint.GET()
        i18n.switch_language(language)
        serial = self.app.opts.run_on_serial
        if serial:
            ssh_info = await self.app.client.meta.ssh_info.GET()
        else:
            ssh_info = None
        return WelcomeView(self, language, serial, ssh_info)

    def run_answers(self):
        if 'lang' in self.answers:
            self.done(self.answers['lang'])

    def done(self, code):
        log.debug("WelcomeController.done %s next_screen", code)
        i18n.switch_language(code)
        self.signal.emit_signal('l10n:language-selected', code)
        self.app.next_screen(self.endpoint.POST(code))

    def cancel(self):
        # Can't go back from here!
        pass
<commit_msg>remove emission of signal that nothing listens to any more<commit_after># Copyright 2015 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import logging

from subiquitycore import i18n
from subiquity.client.controller import SubiquityTuiController
from subiquity.ui.views.welcome import WelcomeView

log = logging.getLogger('subiquity.client.controllers.welcome')

class WelcomeController(SubiquityTuiController):

    endpoint_name = 'locale'

    async def make_ui(self):
        language = await self.endpoint.GET()
        i18n.switch_language(language)
        serial = self.app.opts.run_on_serial
        if serial:
            ssh_info = await self.app.client.meta.ssh_info.GET()
        else:
            ssh_info = None
        return WelcomeView(self, language, serial, ssh_info)

    def run_answers(self):
        if 'lang' in self.answers:
            self.done(self.answers['lang'])

    def done(self, code):
        log.debug("WelcomeController.done %s next_screen", code)
        i18n.switch_language(code)
        self.app.next_screen(self.endpoint.POST(code))

    def cancel(self):
        # Can't go back from here!
        pass
<|endoftext|>


<commit_before># From https://github.com/Victory/django-travis-saucelabs
import os
import sys
import re

from unittest2 import skipIf

try:
    from selenium import webdriver
    has_selenium = True
except Exception as ex:
    print ex
    has_selenium = False

from django.test import LiveServerTestCase

RUN_ON_SAUCE = os.environ.get('RUN_TESTS_SAUCELAB') == 'True'
RUN_LOCAL = not RUN_ON_SAUCE

if RUN_LOCAL:
    # could add Chrome, PhantomJS etc... here
    browsers = ['Firefox']
else:
    try:
        from sauceclient import SauceClient
        USERNAME = os.environ.get('SAUCE_USERNAME')
        ACCESS_KEY = os.environ.get('SAUCE_ACCESS_KEY')
        sauce = SauceClient(USERNAME, ACCESS_KEY)
    except Exception as ex:
        print "EX: ", ex
        pass

    if has_selenium:
        iphone = webdriver.DesiredCapabilities.IPHONE
        iphone['platform'] = "OS X 10.9"
        iphone['version'] = "8.1"
        iphone['browserName'] = "iPhone"
        iphone['device-orientation'] = 'portrait'

        ipad = webdriver.DesiredCapabilities.IPAD
        ipad['platform'] = "OS X 10.9"
        ipad['version'] = "8.1"
        ipad['browserName'] = "iPad"
        ipad['device-orientation'] = 'portrait'

        android_nexus = webdriver.DesiredCapabilities.ANDROID
        android_nexus['platform'] = "Linux"
        android_nexus['version'] = "4.4"
        android_nexus['deviceName'] = "Google Nexus 7 HD Emulator"
        android_nexus['device-orientation'] = 'portrait'

        browsers = [
            android_nexus,
            iphone,
            ipad,
            {"platform": "Mac OS X 10.9",
             "browserName": "chrome",
             "version": "35"},
            {"platform": "Windows 8.1",
             "browserName": "internet explorer",
             "version": "11"},
            {"platform": "Linux",
             "browserName": "firefox",
             "version": "29"}
        ]

        browsers = [
            {"platform": "Linux",
             "browserName": "firefox",
             "version": "29"}
        ]
    else:
        browsers = []

def on_platforms(platforms, local):
    if local:
        def decorator(base_class):
            module = sys.modules[base_class.__module__].__dict__
            for i, platform in enumerate(platforms):
                d = dict(base_class.__dict__)
                d['browser'] = platform
                name = "%s_%s" % (base_class.__name__, i + 1)
                module[name] = type(name, (base_class,), d)
            pass
        return decorator

    def decorator(base_class):
        module = sys.modules[base_class.__module__].__dict__
        for i, platform in enumerate(platforms):
            d = dict(base_class.__dict__)
            d['desired_capabilities'] = platform
            name = "%s_%s" % (base_class.__name__, i + 1)
            module[name] = type(name, (base_class,), d)

    return decorator

from django.conf import settings

class SeleniumLiveServerTestCase(LiveServerTestCase):

    def __init__(self, *args, **kwargs):
        super(SeleniumLiveServerTestCase, self).__init__(*args, **kwargs)
        if settings.DEBUG == False:
            settings.DEBUG = True

@on_platforms(browsers, RUN_LOCAL)
class CardOrderTest(SeleniumLiveServerTestCase):
    """
    Runs a test using travis-ci and saucelabs
    """

    def setUp(self):
        if RUN_LOCAL:
            self.setUpLocal()
        else:
            self.setUpSauce()

    def tearDown(self):
        if RUN_LOCAL:
            self.tearDownLocal()
        else:
            self.tearDownSauce()

    def setUpSauce(self):
        self.desired_capabilities['name'] = self.id()
        self.desired_capabilities['tunnel-identifier'] = \
            os.environ.get('TRAVIS_JOB_NUMBER', '')
        self.desired_capabilities['build'] = os.environ.get('TRAVIS_BUILD_NUMBER', '')
        self.desired_capabilities['tags'] = \
            [os.environ.get('TRAVIS_PYTHON_VERSION', ''), 'CI']

        print self.desired_capabilities

        sauce_url = "http://%s:%s@ondemand.saucelabs.com:80/wd/hub"
        self.driver = webdriver.Remote(
            desired_capabilities=self.desired_capabilities,
            command_executor=sauce_url % (USERNAME, ACCESS_KEY)
        )
        self.driver.implicitly_wait(5)

    def setUpLocal(self):
        self.driver = getattr(webdriver, self.browser)()
        self.driver.implicitly_wait(3)

    def tearDownLocal(self):
        self.driver.quit()

    def tearDownSauce(self):
        print("\nLink to your job: \n "
              "https://saucelabs.com/jobs/%s \n" % self.driver.session_id)
        try:
            if sys.exc_info() == (None, None, None):
                sauce.jobs.update_job(self.driver.session_id, passed=True)
            else:
                sauce.jobs.update_job(self.driver.session_id, passed=False)
        finally:
            self.driver.quit()

    def test_myuw(self):
        from time import sleep

        dates = [
            "2013-04-01",
            "2013-06-06",
            "2013-06-07",
            "2013-06-08",
            "2013-06-14",
            "2013-06-15",
            "2013-02-02",
            "2013-02-01",
            "2013-01-31",
            "2013-03-10",
            "2013-03-11",
            "2013-03-26",
            "2013-03-27",
            "2013-04-01",
        ]

        for date in dates:
            self.driver.get(self.live_server_url + '/mobile/admin/dates/')
            element = self.driver.find_element_by_xpath("//input[@name='date']")
            element.clear()
            element.send_keys(date)
            element.submit()
            self.driver.get(self.live_server_url + '/mobile/landing/')
            sleep(2)
            title = self.driver.title
            self.assertEquals(self.driver.title, "MyUW Mobile Home")

            divs = self.driver.find_elements_by_css_selector("#landing_content > div")
#            for div in divs:
#                print "D: ", div.get_attribute("id"), div.get_attribute("style")

<commit_msg>Updating the card order test to actually test it.<commit_after># From https://github.com/Victory/django-travis-saucelabs
import os
import sys
import re

from unittest2 import skipIf

try:
    from selenium import webdriver
    has_selenium = True
except Exception as ex:
    print ex
    has_selenium = False

from django.test import LiveServerTestCase

RUN_ON_SAUCE = os.environ.get('RUN_TESTS_SAUCELAB') == 'True'
RUN_LOCAL = not RUN_ON_SAUCE

if RUN_LOCAL:
    # could add Chrome, PhantomJS etc... here
    browsers = ['Firefox']
else:
    try:
        from sauceclient import SauceClient
        USERNAME = os.environ.get('SAUCE_USERNAME')
        ACCESS_KEY = os.environ.get('SAUCE_ACCESS_KEY')
        sauce = SauceClient(USERNAME, ACCESS_KEY)
    except Exception as ex:
        print "EX: ", ex
        pass

    if has_selenium:
        iphone = webdriver.DesiredCapabilities.IPHONE
        iphone['platform'] = "OS X 10.9"
        iphone['version'] = "8.1"
        iphone['browserName'] = "iPhone"
        iphone['device-orientation'] = 'portrait'

        ipad = webdriver.DesiredCapabilities.IPAD
        ipad['platform'] = "OS X 10.9"
        ipad['version'] = "8.1"
        ipad['browserName'] = "iPad"
        ipad['device-orientation'] = 'portrait'

        android_nexus = webdriver.DesiredCapabilities.ANDROID
        android_nexus['platform'] = "Linux"
        android_nexus['version'] = "4.4"
        android_nexus['deviceName'] = "Google Nexus 7 HD Emulator"
        android_nexus['device-orientation'] = 'portrait'

        browsers = [
            android_nexus,
            iphone,
            ipad,
            {"platform": "Mac OS X 10.9",
             "browserName": "chrome",
             "version": "35"},
            {"platform": "Windows 8.1",
             "browserName": "internet explorer",
             "version": "11"},
            {"platform": "Linux",
             "browserName": "firefox",
             "version": "29"}
        ]

        browsers = [
            {"platform": "Linux",
             "browserName": "firefox",
             "version": "29"}
        ]
    else:
        browsers = []

def on_platforms(platforms, local):
    if local:
        def decorator(base_class):
            module = sys.modules[base_class.__module__].__dict__
            for i, platform in enumerate(platforms):
                d = dict(base_class.__dict__)
                d['browser'] = platform
                name = "%s_%s" % (base_class.__name__, i + 1)
                module[name] = type(name, (base_class,), d)
            pass
        return decorator

    def decorator(base_class):
        module = sys.modules[base_class.__module__].__dict__
        for i, platform in enumerate(platforms):
            d = dict(base_class.__dict__)
            d['desired_capabilities'] = platform
            name = "%s_%s" % (base_class.__name__, i + 1)
            module[name] = type(name, (base_class,), d)

    return decorator

from django.conf import settings

class SeleniumLiveServerTestCase(LiveServerTestCase):

    def __init__(self, *args, **kwargs):
        super(SeleniumLiveServerTestCase, self).__init__(*args, **kwargs)
        if settings.DEBUG == False:
            settings.DEBUG = True

@on_platforms(browsers, RUN_LOCAL)
class CardOrderTest(SeleniumLiveServerTestCase):
    """
    Runs a test using travis-ci and saucelabs
    """

    def setUp(self):
        if RUN_LOCAL:
            self.setUpLocal()
        else:
            self.setUpSauce()

    def tearDown(self):
        if RUN_LOCAL:
            self.tearDownLocal()
        else:
            self.tearDownSauce()

    def setUpSauce(self):
        self.desired_capabilities['name'] = self.id()
        self.desired_capabilities['tunnel-identifier'] = \
            os.environ.get('TRAVIS_JOB_NUMBER', '')
        self.desired_capabilities['build'] = os.environ.get('TRAVIS_BUILD_NUMBER', '')
        self.desired_capabilities['tags'] = \
            [os.environ.get('TRAVIS_PYTHON_VERSION', ''), 'CI']

        print self.desired_capabilities

        sauce_url = "http://%s:%s@ondemand.saucelabs.com:80/wd/hub"
        self.driver = webdriver.Remote(
            desired_capabilities=self.desired_capabilities,
            command_executor=sauce_url % (USERNAME, ACCESS_KEY)
        )
        self.driver.implicitly_wait(5)

    def setUpLocal(self):
        self.driver = getattr(webdriver, self.browser)()
        self.driver.implicitly_wait(3)

    def tearDownLocal(self):
        self.driver.quit()

    def tearDownSauce(self):
        print("\nLink to your job: \n "
              "https://saucelabs.com/jobs/%s \n" % self.driver.session_id)
        try:
            if sys.exc_info() == (None, None, None):
                sauce.jobs.update_job(self.driver.session_id, passed=True)
            else:
                sauce.jobs.update_job(self.driver.session_id, passed=False)
        finally:
            self.driver.quit()

    def test_myuw(self):
        from time import sleep

        dates = [
            { 'date': "2013-04-01", 'user': 'javerage' },
            { 'date': "2013-04-25", 'user': 'javerage' }, # Same!
            { 'date': "2013-04-26", 'user': 'none' }, # Needs to be none to have no registrations, otherwise RegStatusCard is hidden
            { 'date': "2013-05-30", 'user': 'none' }, # Same!
            { 'date': "2013-03-10", 'user': 'none' }, # Need to go back in time, otherwise autumn makes this break - Same though
            { 'date': "2013-03-11", 'user': 'none' }, # Need to go back in time, otherwise autumn makes this break
            { 'date': "2013-06-07", 'user': 'javerage' }, # Same (ish)!
            { 'date': "2013-06-08", 'user': 'javerage' },
            { 'date': "2013-06-13", 'user': 'javerage' }, # Same!
            { 'date': "2013-06-15", 'user': 'javerage' },
            { 'date': "2013-08-27", 'user': 'javerage' }, # Need to go to the future - spring's grade submission deadline is always today actual.
            { 'date': "2013-08-28", 'user': 'javerage' }, # Need to go to the future - spring's grade submission deadline is always today actual.
            { 'date': "2013-09-24", 'user': 'javerage' }, # Same
            { 'date': "2013-09-25", 'user': 'javerage' },
        ]

        correct_cards = [
            [u'FutureQuarterCardA', u'VisualScheduleCard', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard'],
            [u'FutureQuarterCardA', u'VisualScheduleCard', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard'],
            [u'RegStatusCard', u'VisualScheduleCard', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard', u'FutureQuarterCard1'],
            [u'RegStatusCard', u'VisualScheduleCard', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard', u'FutureQuarterCard1'],
            [u'RegStatusCard', u'VisualScheduleCard', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard', u'FutureQuarterCard1'],
            [u'VisualScheduleCard', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard', u'FutureQuarterCard1'],
            [u'FutureQuarterCardA', u'VisualScheduleCard', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard'],
            [u'GradeCard', u'FinalExamCard', u'FutureQuarterCardA', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard'],
            [u'GradeCard', u'FinalExamCard', u'FutureQuarterCardA', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard'],
            [u'GradeCard', u'FutureQuarterCardA', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard'],
            [u'GradeCard', u'FutureQuarterCardA', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard'],
            [u'GradeCard', u'VisualScheduleCard', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard', u'FutureQuarterCard1'],
            [u'GradeCard', u'VisualScheduleCard', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard', u'FutureQuarterCard1'],
            [u'VisualScheduleCard', u'CourseCard', u'HFSCard', u'TuitionCard', u'LibraryCard', u'AcademicCard', u'FutureQuarterCard1'],
        ]

        index = 0
        for val in dates:
            date = val["date"]
            user = val["user"]
            self.driver.get(self.live_server_url + '/users/')
            element = self.driver.find_element_by_xpath("//input[@name='override_as']")
            element.clear()
            element.send_keys(user)
            element.submit()

            self.driver.get(self.live_server_url + '/mobile/admin/dates/')
            element = self.driver.find_element_by_xpath("//input[@name='date']")
            element.clear()
            element.send_keys(date)
            element.submit()
            self.driver.get(self.live_server_url + '/mobile/landing/')
            # XXX - this is lame.  need to add something to wait on here instead
            sleep(2)
            title = self.driver.title
            self.assertEquals(self.driver.title, "MyUW Mobile Home")

            divs = self.driver.find_elements_by_css_selector("#landing_content > div")

            displayed = []
            for div in divs:
                if div.get_attribute("style") != "display: none;":
                    displayed.append(div.get_attribute("id"))

            cards = correct_cards[index]
            for i in range(0, len(cards)-1):
                self.assertEquals(cards[i], displayed[i])

            index = index + 1
<|endoftext|>