# coding: utf-8 import os import _mysql, paramiko from Tkinter import * import tkFileDialog, tkMessageBox #import Image, ImageTk, urllib, cStringIO # ssh and sftp connection (paramiko module) ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect('aibkup.phys.au.dk', username='noodles', password='goFor2nd') sftp = ssh.open_sftp() # mysql connection (_mysql module) db = _mysql.connect(host='aibkup.phys.au.dk', user='simon', db='oro_archive') FILETYPES = ('fit', 'fits',)# 'fts', 'pic', 'bpm', 'jpg', 'jpeg', 'tif', 'tiff', 'crw', 'cr2', 'nef', 'nrw', 'dng') totalFilesPath = [] filesInfo = [] fileId = -1 def selectFiles(): global filesPath filesPath = list(tkFileDialog.askopenfilename(filetypes=[('FITS files','*')], multiple=1)) for f in enumerate(filesPath): if f[1].split('.')[-1] not in FILETYPES: filesPath.pop(f[0]) if filesPath: getFilesInfo() def selectDirectory(): global filesPath directory = tkFileDialog.askdirectory() if directory: files = os.listdir(directory) filesPath = list() for f in files: if f.split('.')[-1] in FILETYPES: filesPath.append(directory + '/' + f) if filesPath: getFilesInfo() def getFilesInfo(): global headerDict filesPath.sort() for f in filesPath: totalFilesPath.append(f) filesInfo.append([f.split('/')[-1].split('.')[:-1][0]]) filesInfo[-1].append(str.lower(f.split('.')[-1])) filesInfo[-1].append(os.path.getsize(f)) headerDict = dict() data = open(f, 'rb') simple = data.read(80) if simple[:8].strip() == 'SIMPLE' and simple[8:].split('/')[0].strip('= ') == 'T': while 1: line = data.read(80) if line[:3] != 'END': try: headerDict[line[:8].strip()] += '\n' + line[8:].split('/')[0].strip('\'= ') except: headerDict[line[:8].strip()] = line[8:].split('/')[0].strip('\'= ') else: break filesInfo[-1].append(int(checkKeyword('BITPIX',0))) filesInfo[-1].append(int(checkKeyword('NAXIS1',0))) filesInfo[-1].append(int(checkKeyword('NAXIS2',0))) filesInfo[-1].append(checkKeyword('OBJECT')) filesInfo[-1].append(checkKeyword('RA').replace(' ','')) filesInfo[-1].append(checkKeyword('DEC').replace(' ','')) filesInfo[-1].append(checkKeyword('EQUINOX')) filesInfo[-1].append(checkKeyword('OBSERVER')) filesInfo[-1].append(checkKeyword('DATE-OBS')) filesInfo[-1].append(checkKeyword('TELESCOP')) filesInfo[-1].append(checkKeyword('INSTRUME')) filesInfo[-1].append(float(checkKeyword('EXPTIME',0))) filesInfo[-1].append(checkKeyword('FILTER')) filesInfo[-1].append(checkKeyword('COMMENTS')) filesInfo[-1].append(checkKeyword('HISTORY')) for i in range(3): filesInfo[-1].append('') insertFilesInList() def checkKeyword(key,dv=''): # dv = defaultValue if key in headerDict: return headerDict[key] else: return dv def insertFilesInList(): listFiles.config(state='normal', bg='#FFFFFF') for f in filesPath: listFiles.insert(END,f.split('/')[-1]) uploadButton.config(state='normal') menuBar.entryconfig(2,state='normal') ################################################################################ def selectionChange(event=0): global fileId try: fileId = int(listFiles.curselection()[0]); for obj in enumerate(OBJECTS): obj[1].setState('normal') obj[1].rSet(filesInfo[fileId][obj[0]]) for obj in OBJECTS[:6]: obj.setState('disabled') removeButton.config(state='normal') except: pass def updateHeader(event=0): if fileId > -1: for obj in enumerate(OBJECTS): filesInfo[fileId][obj[0]] = obj[1].get().encode('ascii') afterId = 0 def setStatus(status,ac=1): global afterId if afterId: statusBar.after_cancel(afterId) statusBar.config(text=status) statusBar.update_idletasks() if ac: afterId = statusBar.after(3000,clearStatus) def clearStatus(): statusBar.config(text='') def removeFile(): global fileId totalFilesPath.pop(fileId) listFiles.delete(fileId) setStatus('Removed ' + filesInfo.pop(fileId)[0]) if listFiles.size() == 0: noFiles() else: unmark() def clearList(): global totalFilesPath, filesInfo listFiles.delete(0,END) totalFilesPath = [] filesInfo = [] noFiles() setStatus('Cleared list') def noFiles(): global fileId listFiles.config(state='disabled', bg='#d9d9d9'); fileId = -1 for obj in OBJECTS: obj.setState('normal') obj.rSet('') obj.setState('disabled') uploadButton.config(state='disabled') removeButton.config(state='disabled') menuBar.entryconfig(2,state='disabled') def unmark(): global fileId listFiles.selection_clear(0,END); fileId = -1 for obj in OBJECTS: obj.setState('normal') obj.rSet('') obj.setState('disabled') removeButton.config(state='disabled') ################################################################################ def edit_ckvfaf(): # change keyword for all files def changeValues(): updateHeader() unmark() for i in enumerate(filesInfo): filesInfo[i[0]][EDITABLE_OBJECT_NAMES.index(var.get())+6] = inputEntry.get() setStatus('Changed '+ var.get() + ' for all files') ckvfaf.destroy() ckvfaf = Tk() ckvfaf.geometry('400x90+'+str(screenWidth/2-200)+'+'+str(screenHeight/2-45)) ckvfaf.resizable(0, 0) ckvfaf.title('Change keyword value for all files') Frame(ckvfaf,height=10).pack(side=TOP) inputFrame = Frame(ckvfaf) var = StringVar(inputFrame); var.set(EDITABLE_OBJECT_NAMES[0]) chooseKeyword = OptionMenu(inputFrame, var, *EDITABLE_OBJECT_NAMES) chooseKeyword.config(width=13) chooseKeyword.grid(row=0,column=0) inputEntry = Entry(inputFrame,width=25) inputEntry.grid(row=0,column=1) inputFrame.pack(side=TOP) Frame(ckvfaf,height=10).pack(side=BOTTOM) buttonFrame = Frame(ckvfaf) updateButton = Button(buttonFrame, text='Change', command=changeValues) updateButton.grid(row=0,column=0) cancelButton = Button(buttonFrame, text='Cancel', command=ckvfaf.destroy) cancelButton.grid(row=0,column=1) buttonFrame.pack(side=BOTTOM) def edit_altcfaf(): # add line to comments for all files def addValues(): updateHeader() unmark() for i in enumerate(filesInfo): if filesInfo[i[0]][16] == '': filesInfo[i[0]][16] += (inputEntry.get()).rstrip() else: filesInfo[i[0]][16] += ('\n' + inputEntry.get()).rstrip() setStatus('Added line to comments for all files') altcfaf.destroy() altcfaf = Tk() altcfaf.geometry('400x90+'+str(screenWidth/2-200)+'+'+str(screenHeight/2-45)) altcfaf.resizable(0, 0) altcfaf.title('Add line to comments for all files') Frame(altcfaf,height=10).pack(side=TOP) inputFrame = Frame(altcfaf) lComments = Label(inputFrame, text='Comments') lComments.config(width=13) lComments.grid(row=0,column=0) inputEntry = Entry(inputFrame,width=25) inputEntry.grid(row=0,column=1) inputFrame.pack(side=TOP) Frame(altcfaf,height=10).pack(side=BOTTOM) buttonFrame = Frame(altcfaf) addButton = Button(buttonFrame, text='Add', command=addValues) addButton.grid(row=0,column=0) cancelButton = Button(buttonFrame, text='Cancel', command=altcfaf.destroy) cancelButton.grid(row=0,column=1) buttonFrame.pack(side=BOTTOM) ################################################################################ def uploadFiles(): global totalFilesPath, filesInfo updateHeader() unmark() listFiles.config(state='disabled', bg='#d9d9d9'); uploadButton.config(state='disabled') menuBar.entryconfig(1,state='disabled') menuBar.entryconfig(2,state='disabled') for f in filesInfo: if f[7]: try: f[7] = float(f[7]) * 360/24 except: pass # if f[7] and f[8]: # ra = [float(j) for j in f[7].split(':')] # f[7] = (ra[0] + ra[1]/60 + ra[2]/3600)*360/24 # decl = [float(j) for j in f[8].split(':')] # f[8] = (math.copysign(decl[0],1) + decl[1]/60 + decl[2]/3600)*math.copysign(1.0,decl[0]) error = 0 for f in enumerate(filesInfo): try: setStatus('Uploading ' + f[1][0] + ' (file ' + str(f[0]+1) + ' of ' + str(len(totalFilesPath)) + ')',0) db.query(str('insert into file values(null,' + '%r,'*21 + 'null)') % tuple(f[1])) sftp.put(totalFilesPath[f[0]],'/ai50/oro/'+f[1][0]+'_id'+str(db.insert_id())+'.'+f[1][1]) except: if not tkMessageBox.askyesno('','Error ... continue?'): error = 1 break clearStatus() listFiles.config(state='normal') listFiles.delete(0,END) listFiles.config(state='disabled') totalFilesPath = [] filesInfo = [] if not error: tkMessageBox.showinfo('','Upload complete') menuBar.entryconfig(1,state='normal') ################################################################################ class TkLabel: def __init__(self, txt, x, y): self.box = Label(tkFrame, text=txt) self.box.place(x=x,y=y) class TkEntry: def __init__(self, x, y, w, s=''): self.__var = StringVar(); self.__defaultVar = s; self.box = Entry(tkFrame, width=w, textvariable=self.__var, fg='#c0c0c0') if s: self.__var.set(s) self.box.bind('', self.focusin) self.box.place(x=x, y=y) def rSet(self,txt): if txt == '': self.box.config(fg='#c0c0c0') txt = self.__defaultVar else: self.box.config(fg='black') self.__var.set(txt) def get(self): if self.__var.get() != self.__defaultVar: return self.__var.get() else: return '' def setState(self, state): self.box.config(state=state) def focusin(self, event): self.box.config(fg='black') if self.box.get() == self.__defaultVar: self.__var.set('') class TkOptionMenu: def __init__(self, vals, x, y, w): self.__var = StringVar() self.vals = vals self.box = OptionMenu(tk, self.__var, *vals) self.box.config(width=w, padx=0, pady=0) self.box.place(x=x+18, y=y+30, anchor='w') def rSet(self, txt): self.__var.set(txt) def get(self): return self.__var.get() def setState(self, state): if state == 'disabled': self.box.config(state='disabled', bg='#d9d9d9') else: self.box.config(state='normal', bg='#ffffff') class TkText: def __init__(self, x, y, v=1): if v == 1: self.frame = Frame(tk) self.__SbV = Scrollbar(self.frame, orient=VERTICAL, width=10) self.box = Text(self.frame, width=19, height=3, yscrollcommand=self.__SbV.set); self.box.grid(row=0, column=0) self.__SbV.grid(row=0, column=1, sticky=N+S+W) self.__SbV.config(command=self.box.yview) self.frame.place(x=x, y=y+20) elif v == 0: self.box = Text(tk) def rSet(self,txt): self.box.delete(1.0, END) self.box.insert(1.0, txt) def get(self): return self.box.get(1.0, END).strip('\n') def setState(self, state): if state == 'disabled': self.box.config(state='disabled', bg='#d9d9d9') else: self.box.config(state='normal', bg='#ffffff') ################################################################################ tk = Tk() screenWidth, screenHeight = tk.winfo_screenwidth(), tk.winfo_screenheight(); tk.geometry('800x400+'+str(screenWidth/2-400)+'+'+str(screenHeight/2-200)) tk.resizable(0, 0) tk.title('ORO Archive') statusBar = Label(tk, text='', bd=1, relief=SUNKEN, anchor=W) statusBar.pack(side=BOTTOM, fill=X) menuBar = Menu(tk) fileMenu = Menu(menuBar, tearoff=0) fileMenu.add_command(label='Select files', command=selectFiles) fileMenu.add_command(label='Select directory', command=selectDirectory) fileMenu.add_separator() fileMenu.add_command(label='Exit', command=tk.destroy) menuBar.add_cascade(label='File', menu=fileMenu) editMenu = Menu(menuBar, tearoff=0) editMenu.add_command(label='Change keyword value for all files', command=edit_ckvfaf) editMenu.add_command(label='Add line to comments for all files', command=edit_altcfaf) editMenu.add_separator() editMenu.add_command(label='Clear list', command=clearList) menuBar.add_cascade(label='Edit', menu=editMenu) tk.config(menu=menuBar) tkFrame = Frame(tk,padx=20,pady=20) listFilesFrame = Frame(tkFrame) listFilesScrollbar = Scrollbar(listFilesFrame, orient=VERTICAL) listFiles = Listbox(listFilesFrame, height=21, bg='#d9d9d9', yscrollcommand=listFilesScrollbar.set) listFilesScrollbar.config(command=listFiles.yview) listFiles.bind('',updateHeader) listFiles.bind('',selectionChange) listFiles.bind('',updateHeader) listFiles.bind('',selectionChange) listFiles.grid(row=0, column=0) listFilesScrollbar.grid(row=0, column=1, sticky=N+S+W) listFilesFrame.pack(side=LEFT) sep1 = Frame(tkFrame,width=20) sep1.pack(side=LEFT) buttonFrame = Frame(tkFrame) uploadButton = Button(buttonFrame, text='Upload all files', width=25, command=uploadFiles) uploadButton.grid(row=0,column=0) removeButton = Button(buttonFrame, text='Remove this file', width=25, command=removeFile) removeButton.grid(row=0,column=1) buttonFrame.pack(side=BOTTOM) TkLabel('Filename',200,0) eFileName = TkEntry(320,0,18) TkLabel('File format',200,25) eFileFormat = TkEntry(320,25,18) TkLabel('File size',200,50) eFileSize = TkEntry(320,50,18,'[bytes]') TkLabel('Bit depth',200,75) eBitDepth = TkEntry(320,75,18) TkLabel('Dimensions',200,100) eX = TkEntry(320,100,7,'[pixels]'); TkLabel('x',390,100); eY = TkEntry(408,100,7,'[pixels]') TkLabel('Comments',200,150) tComments = TkText(340,150) TkLabel('History',200,225) tHistory = TkText(340,225) TkLabel('Object',490,0) eObject = TkEntry(610,0,18) TkLabel('Position',490,25) eRA = TkEntry(610,25,7,'[hours]'); TkLabel(',',680,25); eDecl = TkEntry(698,25,7,'[degrees]') TkLabel('Epoch',490,50) EPOCHS = ('2000', '1950') omEpoch = TkOptionMenu(EPOCHS,610,50,15) TkLabel('Observer',490,100) eObserver = TkEntry(610,100,18) TkLabel('Observation time',490,125) eObsTime = TkEntry(610,125,18,'(YY)YY-MM-DD (HH:MM:SS)') TkLabel('Telescope',490,175) eTelescope = TkEntry(610,175,18) TkLabel('Camera',490,200) eCamera = TkEntry(610,200,18) TkLabel('Exposure time',490,225) eExpTime = TkEntry(610,225,18,'[seconds]') TkLabel('Filter',490,250) FILTERS = ('L', 'Red', 'Green', 'Blue', 'C', 'U', 'B', 'V', 'R', 'I', 'H-alpha', 'H-beta', 'OIII', 'SII') omFilter = TkOptionMenu(FILTERS,610,250,15) tDesc3 = TkText(0,0,0) tDesc4 = TkText(0,0,0) tDesc5 = TkText(0,0,0) sep2 = Frame(tkFrame,height=20) sep2.pack(side=BOTTOM) tkFrame.pack(fill=BOTH) EDITABLE_OBJECT_NAMES = ('Object','R.A. position','Decl. position','Epoch','Observer','Observation time','Telescope','Camera','Exposure time','Filter','Comments','History') OBJECTS = [eFileName,eFileFormat,eFileSize,eBitDepth,eX,eY,eObject,eRA,eDecl,omEpoch,eObserver,eObsTime,eTelescope,eCamera,eExpTime,omFilter,tComments,tHistory,tDesc3,tDesc4,tDesc5] noFiles() tk.mainloop()