#!/usr/bin/env python
#
# Copyright 2012 Ville Rantanen
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see .
import sys,os
import re
import urllib
import shutil
import csv
import subprocess
from math import ceil
from datetime import datetime
# (c) ville.rantanen@helsinki.fi
__version__='1.6'
webfilesearch=re.compile('.*index.html$|.*gallerystyle.css$|.*galleryscript.js$|.*descriptions.csv$|\..*',re.I)
imagesearch=re.compile('.*\.jpg$|.*\.jpeg$|.*\.gif$|.*\.png$|.*\.svg$|.*\.pdf$',re.I)
vectorsearch=re.compile('.*\.svg$|.*\.pdf$',re.I)
#gifsearch=re.compile('.*gif$',re.I)
excludepaths=re.compile('_med|_tn|\..*')
doublequotes=re.compile('"')
singlequotes=re.compile("'")
stripquotes=re.compile('^"|"$')
def getheader(path,parent,title=""):
if title=="":
title=unicode(os.path.basename(path),encoding="utf8").encode('ascii', 'xmlcharrefreplace')
return '''
'''+title+'''
'''
def getfooter():
return '''
'''
def getimagelist(path):
''' Returns a list of images matching the regex '''
list=os.listdir(path)
imgs=[]
for f in list:
if (imagesearch.match(f)) and (os.path.isfile(os.path.join(path,f))):
imgs.append(f)
if options.timesort:
imgs.sort(key=lambda f: os.path.getmtime(os.path.join(path, f)),reverse=options.reverse)
else:
imgs.sort(reverse=options.reverse)
return imgs
def getfiletimes(path,list):
''' Returns a list of modification times '''
times=[]
for p in list:
times.append(int(os.path.getmtime(os.path.join(path,p))))
return times
def getnonimagelist(path):
''' Returns a list of files not matching the image match regex '''
list=os.listdir(path)
files=[]
if not options.attachments:
return files
for f in list:
if (not webfilesearch.match(f)) and (not imagesearch.match(f)) and (os.path.isfile(os.path.join(path,f))):
files.append(f)
if options.timesort:
files.sort(key=lambda f: os.path.getmtime(os.path.join(path, f)),reverse=options.reverse)
else:
files.sort(reverse=options.reverse)
return files
def getpathlist(path):
''' Returns a list of subfolders not matching the exclusion regex '''
list=os.listdir(path)
paths=[]
for d in list:
if (not excludepaths.match(d)) and (os.path.isdir(os.path.join(path,d))):
paths.append(d)
if options.timesort:
paths.sort(key=lambda f: os.path.getmtime(f),reverse=options.reverse)
else:
paths.sort(reverse=options.reverse)
return paths
def pathscript(path,list):
''' Returns the javascript string of pathlist and pathimage arrays '''
scrstr=''
return scrstr
def pathlinks(path,list):
''' Returns the HTML string of subfolders '''
pathstr='
'
return pathstr
def imagescript(path,list):
''' Returns the javascript string of imagelist and imagedesc '''
strout=''
return strout
def imagelinks(path,list):
''' Returns the HTML string of images '''
strout=''
return strout
def filescript(path,list):
''' Returns the javascript string of filelist '''
strout=''
return strout
def filelinks(path,list):
''' Returns the HTML string of non image files '''
strout='
'
return strout
def createthumbs(path,list):
''' Runs imagemagick Convert to create medium sized and thumbnail images '''
if len(list)==0:
return
if not os.path.exists(os.path.join(path,'_tn')):
os.mkdir(os.path.join(path,'_tn'))
if not os.path.exists(os.path.join(path,'_med')):
os.mkdir(os.path.join(path,'_med'))
n=1
nsum=len(list)
r='1000'
for i in list:
outmedium=os.path.join(path,'_med','med_'+i+'.jpg')
outthumb=os.path.join(path,'_tn','tn_'+i+'.jpg')
inpath=os.path.join(path,i)
if (not os.path.exists(outmedium)):
print('Medium.. '+i+' '+str(n)+'/'+str(nsum))
create_medium_bitmap(inpath,outmedium,r,vector=vectorsearch.match(i))
if (not os.path.exists(outthumb)):
print('Thumbnail.. '+i+' '+str(n)+'/'+str(nsum))
create_thumb_bitmap(outmedium,outthumb,vector=vectorsearch.match(i))
n+=1
return
def create_medium_bitmap(infile,outfile,r,vector=False):
res=r+'x'+r+'>'
if vector:
convargs=['convert','-density','300x300',infile+'[0]','-background','white','-flatten','-resize',res,'-quality','97',outfile]
else:
convargs=['convert','-define','jpeg:size='+r+'x'+r,infile+'[0]','-background','white','-flatten','-resize',res,'-quality','85',outfile]
convp=subprocess.call(convargs)
return
def create_thumb_bitmap(infile,outfile,vector=False):
if vector:
convargs=['convert','-density','300x300',infile,'-background','white','-flatten','-thumbnail','90x90^','-gravity','Center','-crop','90x90+0+0','+repage','-quality','75',outfile]
else:
convargs=['convert','-define','jpeg:size=300x300',infile,'-background','white','-flatten','-thumbnail','90x90^','-gravity','Center','-crop','90x90+0+0','+repage','-quality','75',outfile]
convp=subprocess.call(convargs)
return
def getdescriptions(path,list):
''' Read descriptions.csv file and returns a list of descriptions.
Missing descriptions are replaced with the file name. '''
if not os.path.exists(os.path.join(path,'descriptions.csv')):
return list
desc=[i for i in list]
reader = csv.reader(open(os.path.join(path,'descriptions.csv'),'rb'),
delimiter='\t',
doublequote=False,
escapechar='\\',
quoting=csv.QUOTE_NONE)
for row in reader:
if len(row)>1:
if row[0] in list:
i=list.index(stripquotes.sub('',row[0]))
desc[i]=stripquotes.sub('',row[1])
return desc
def crumblinks(crumbs):
''' Create the HTML string for crumb trails '''
strout='
'
return strout
def nicestring(s):
''' Returns a nice version of a long string '''
if len(s)<20:
return s
s=s.replace("_"," ")
s=s.replace("-"," ")
if len(s)>30:
s=s[0:26]+".."+s[-3:]
return s
def sizestring(size):
''' Returns human readable file size string '''
for x in ['b','kb','Mb','Gb','Tb']:
if size < 1024.0:
if (x=='b') | (x=='kb'):
return "%d%s" % (size, x)
else:
return "%3.1f%s" % (size, x)
size /= 1024.0
def traverse(path,crumbs):
''' The recursive main function to create the index.html and seek sub folders '''
print(path)
if len(crumbs)==1:
header=getheader(path,'../'*(len(crumbs)-1),inputs[0][1])
else:
header=getheader(path,'../'*(len(crumbs)-1))
print(len(crumbs))
pathlist=getpathlist(path)
imagelist=getimagelist(path)
filelist=getnonimagelist(path)
print(str(len(pathlist))+' paths')
print(str(len(imagelist))+' images')
print(str(len(filelist))+' other files')
crumbstring=crumblinks(crumbs)
if len(pathlist)>0:
pathstring=pathlinks(path,pathlist)
pathjs=pathscript(path,pathlist)
else:
pathstring=''
pathjs=''
filestring=filelinks(path,filelist)
filejs=filescript(path,filelist)
if len(imagelist)>0:
imagestring=imagelinks(path,imagelist)
imagejs=imagescript(path,imagelist)
else:
imagestring=''
imagejs=''
f=open(os.path.join(path,"index.html"),"w")
f.write(header)
f.write('')
f.write(pathjs)
f.write(imagejs)
f.write(filejs)
f.write(crumbstring)
f.write(pathstring)
f.write('')
f.write('')
f.write(imagestring)
f.write(filestring)
f.write('')
f.write(getfooter())
f.close()
createthumbs(path,imagelist)
for p in pathlist:
nextcrumbs=[i for i in crumbs]
nextcrumbs.append(os.path.join(path,p))
traverse(os.path.join(path,p),nextcrumbs)
return
class AndurilOptions:
'''Object featuring same variables as arguments from command line.'''
def __init__(self, timesort, reverse):
'''Initializes the person's data.'''
self.timesort=timesort
self.reverse=reverse
self.attachments=True
def execute(cf):
global inputs
global options
inputs=[]
inputs.append((cf.get_input('folderRoot'),'',cf.get_input('csvRoot')))
for i in range(8):
inputs.append((cf.get_input('folder'+str(i+1)),cf.get_parameter('title'+str(i+1)),cf.get_input('csv'+str(i+1))))
fileCol=cf.get_parameter('fileCol')
annotationCol=cf.get_parameter('annotationCol')
options=AndurilOptions(cf.get_parameter('sortTime','boolean'),
cf.get_parameter('sortReverse','boolean'))
outDir = cf.get_output('gallery')
# the folderRoot demands that the outDir is not created earlier.
if inputs[0][0]==None:
os.mkdir(outDir)
else:
shutil.copytree(inputs[0][0],os.path.join(outDir,inputs[0][1]))
for d in inputs:
if (d[0]==None):
continue
if not os.path.exists(d[0]):
continue
print('Copying gallery '+d[1])
cf.write_log('Copying gallery '+d[1])
if not os.path.exists(os.path.join(outDir,d[1])):
shutil.copytree(d[0],os.path.join(outDir,d[1]))
# Find the annotations
if (d[2] is not None):
reader = csv.DictReader(open(d[2],'rb'),
delimiter='\t',
doublequote=False,
escapechar='\\',
quoting=csv.QUOTE_ALL)
for row in reader:
break
if (fileCol not in reader.fieldnames):
cf.write_error("Column \""+fileCol+"\" not found.")
exit()
if (annotationCol not in reader.fieldnames):
cf.write_error("Column \""+annotationCol+"\" not found.")
exit()
reader = csv.DictReader(open(d[2],'rb'),
delimiter='\t',
doublequote=False,
escapechar=None,
quotechar='"',
quoting=csv.QUOTE_NONE)
annotations=[]
header=['file','description']
for row in reader:
annotations.append( (stripquotes.sub('',row.get('"'+fileCol+'"')), stripquotes.sub('',row.get('"'+annotationCol+'"')) ) )
writefid=open(os.path.join(outDir,d[1],'descriptions.csv'),'wb')
writer = csv.writer(writefid,
delimiter='\t',
doublequote=False,escapechar='\\',
quotechar='"',
quoting=csv.QUOTE_NONE)
writer.writerow(header)
for r in annotations:
writer.writerow([r[0],r[1]])
writefid.close()
# Copy all resources to target folder
shutil.copyfile('gallerystyle.css',os.path.join(outDir,'gallerystyle.css'))
shutil.copyfile('galleryscript.js',os.path.join(outDir,'galleryscript.js'))
inputs[0]=((cf.get_input('folderRoot'),cf.get_parameter('titleRoot'),cf.get_input('csvRoot')))
startpath=os.path.abspath(outDir)
traverse(startpath,[startpath])
return 0
def execute_plain():
''' Execute this if run outside anduril '''
from optparse import OptionParser
usage='''Usage: %prog [options] folder
folder is the root folder of the image album.'''
parser=OptionParser(usage=usage,version=__version__)
parser.add_option("-r",action="store_true",dest="reverse",default=False,
help="Reverse sort orded")
parser.add_option("-t",action="store_true",dest="timesort",default=False,
help="Sort by file modification time")
parser.add_option("-a",action="store_false",dest="attachments",default=True,
help="Disable attachments")
global options
(options,args)=parser.parse_args()
if len(args) != 1:
#sys.exit(0)
startpath=os.path.abspath('.')
else:
startpath=os.path.abspath(args[0])
pathname=os.path.dirname(os.path.realpath(sys.argv[0]))
fullpath=os.path.abspath(pathname)
# Copy all resources to target folder
shutil.copyfile(os.path.join(fullpath,'gallerystyle.css'),os.path.join(startpath,'gallerystyle.css'))
shutil.copyfile(os.path.join(fullpath,'galleryscript.js'),os.path.join(startpath,'galleryscript.js'))
global inputs
inputs=[]
inputs.append((None,'Gallery',None))
traverse(startpath,[startpath])
return
try:
import component_skeleton.main
except ImportError:
execute_plain()
sys.exit(0)
component_skeleton.main.main(execute)