""" Python API based agSRB Browser @author Written by Eric Liao at the Vislab at the University Of Queensland. Either one of the following two licences may be applied to the contents written by Eric Liao. * Copyright (c) 2006 The University of Queensland, Australia * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by * Vislab at The University of Queensland and its contributors. * 4. Neither the name of The University of Queensland nor the names of Vislab * and contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY VISLAB AT THE UNIVERSITY OF QUEENSLAND AND * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL VISLAB AT THE * UNIVERSITY OF QUEENSLAND OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * Copyright (C) 2006 The University of Queensland, Australia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. The following copyright is for the portions of the methods marked with "here is the pySRB-based code..." Copyright (c) 2005, Steven Johnston All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Steven Johnston nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following copyright is for the portions of the methods marked with "here is the Access Grid Toolkit-based code..." Access Grid Toolkit Public License (AGTPL) Copyright (c) 2000-2006 University of Chicago. All Rights Reserved. 1) The "Software", below, refers to the Access Grid Toolkit (in either source-code, or binary form and related documentation) and a "work based on the Software" means a work based on either the Software, on part of the Software, or on any derivative work of the Software under copyright law: that is, a work containing all or a portion of the Software either verbatim or with modifications. Each licensee is addressed as "you" or "Licensee." 2) The University of Chicago as Operator of Argonne National Laboratory is the copyright holder in the Software. The holder reserves all rights except those expressly granted to the Licensee herein and license rights held by the U.S. Government. 3) A copy or copies of the Software may be given to others, if you meet the following conditions: a) Copies in source code form must include the copyright notice and this license. b) Copies in binary form must include the copyright notice and this license in the documentation. 4) All advertising materials, journal articles and documentation mentioning features derived from or use of the Software must display the following acknowledgement: "This product includes software developed by and/or derived from the Access Grid project (http://www.accessgrid.org)." In the event that the product being advertised includes an intact Access Grid Toolkit distribution (with copyright and license included) then this clause is waived. 5) You may make modifications to the Software, however, if you modify a copy or copies of the Software or any portion of it, thus forming a work based on the Software, and give a copy or copies of such work to others, either in source code or binary form, you must meet the following conditions: a) The Software must carry prominent notices stating that you changed portions of the Software. b) The Software must display the following acknowledgement: "This product includes software developed by and/or derived from the Access Grid Project (http://www.accessgrid.org) to which the U.S. Government retains certain rights." 6) LICENSEE AGREES THAT THE EXPORT OF GOODS AND/OR TECHNICAL DATA FROM THE UNITED STATES MAY REQUIRE SOME FORM OF EXPORT CONTROL LICENSE FROM THE U.S. GOVERNMENT AND THAT FAILURE TO OBTAIN SUCH EXPORT CONTROL LICENSE MAY RESULT IN CRIMINAL LIABILITY UNDER U.S. LAWS. 7) Portions of the Software resulted from work developed under a U.S. Government contract and are subject to the following license: the Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable worldwide license in this computer software to reproduce, prepare derivative works, and perform publicly and display publicly. 8) The Software was prepared, in part, as an account of work sponsored by an agency of the United States Government. Neither the United States, nor the University of Chicago, nor any contributors to the Access Grid Project or Access Grid Toolkit, nor any of their employees, makes any warranty express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately owned rights. 9) IN NO EVENT WILL THE UNITED STATES, THE UNIVERSITY OF CHICAGO OR ANY CONTRIBUTORS TO THE ACCESS GRID PROJECT OR ACCESS GRID TOOLKIT BE LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES RESULTING FROM EXERCISE OF THIS LICENSE AGREEMENT OR THE USE OF THE SOFTWARE. END OF LICENSE ----------------------------------------------------------------------------------------- """ # Library imports import os import sys import traceback import threading import time import getopt import string import cPickle import sets import tempfile # SRB binding import import srb # wxPython import import wx import wx.lib.buttons as buttons # AccessGrid imports if sys.platform == "darwin": import pyGlobus.ioc # Access Grid libraries import agversion agversion.select(3) from AccessGrid import icons from AccessGrid import Platform from AccessGrid import Log from AccessGrid.SharedAppClient import SharedAppClient from AccessGrid.Platform.Config import UserConfig from AccessGrid.ClientProfile import ClientProfile from AccessGrid import DataStoreClient, DataStoreClientUI from AccessGrid.DataStoreClient import GetVenueDataStore from AccessGrid import Toolkit from AccessGrid.Platform.ProcessManager import ProcessManager from AccessGrid.Toolkit import WXGUIApplication # Twisted reactor imports try: from twisted.internet import threadedselectreactor threadedselectreactor.install() except: pass from twisted.internet import reactor class UserCancelled(Exception): pass class Node: def __init__(self, text, _type, metadata=None, haveChildren=False, parent=None): """Node as data for tree item.""" self.parent = parent # parent collection self.text = text # path of collection/data self._type = _type # type of node: collection/data/user self.metadata = metadata # metadata (should mirror PersonalData/ShareData) self.children = [] # children collection/data self.haveChildren = haveChildren class TransferValidator(wx.PyValidator): def __init__(self, data, key): wx.PyValidator.__init__(self) self.data = data self.key = key def Clone(self): """ Note that every validator must implement the Clone() method. """ return TransferValidator(self.data, self.key) def Validate(self, win): return True def TransferToWindow(self): textCtrl = self.GetWindow() textCtrl.SetValue(self.data.get(self.key, "")) return True def TransferFromWindow(self): textCtrl = self.GetWindow() self.data[self.key] = textCtrl.GetValue() return True """ Common Dialogs """ class ConnectionDialog(wx.Dialog): def __init__(self, data): """ Connection dialog """ wx.Dialog.__init__(self, None, -1, "Account to Manage/Share Data", size=(320, 340)) self.Centre() # Create the text controls titleText = wx.StaticText(self, -1, "Account Details?") titleText.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) titleLine = wx.StaticLine(self, -1) buttonLine = wx.StaticLine(self, -1) resc_l = wx.StaticText(self, -1, "Resource:") host_l = wx.StaticText(self, -1, "Host:") port_l = wx.StaticText(self, -1, "Port:") auth_l = wx.StaticText(self, -1, "Auth:") dom_l = wx.StaticText(self, -1, "Domain:") user_l = wx.StaticText(self, -1, "User:") pwd_l = wx.StaticText(self, -1, "Password:") coll_l = wx.StaticText(self, -1, "Root Path:") resc_t = wx.TextCtrl(self, validator=TransferValidator(data, "resc")) host_t = wx.TextCtrl(self, validator=TransferValidator(data, "host")) port_t = wx.TextCtrl(self, validator=TransferValidator(data, "port")) auth_t = wx.TextCtrl(self, validator=TransferValidator(data, "auth")) dom_t = wx.TextCtrl(self, validator=TransferValidator(data, "dom")) user_t = wx.TextCtrl(self, validator=TransferValidator(data, "user")) pwd_t = wx.TextCtrl(self, validator=TransferValidator(data, "pwd"), style=wx.TE_PASSWORD) coll_t = wx.TextCtrl(self, validator=TransferValidator(data, "coll")) # Use standard button IDs okay = wx.Button(self, wx.ID_OK) cancel = wx.Button(self, wx.ID_CANCEL) # Layout with sizers sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(titleText, 0, wx.EXPAND|wx.ALL, 10) sizer.Add(titleLine, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) fgs = wx.FlexGridSizer(8, 2, 5, 5) fgs.Add(resc_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(resc_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(host_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(host_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(port_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(port_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(auth_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(auth_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(dom_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(dom_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(user_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(user_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(pwd_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(pwd_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(coll_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(coll_t, 0, wx.EXPAND) fgs.AddGrowableCol(1) sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5) sizer.Add(buttonLine, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) btns = wx.StdDialogButtonSizer() btns.AddButton(okay) btns.AddButton(cancel) btns.Realize() sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5) self.SetSizer(sizer) class M_CollPropertiesDialog(wx.Dialog): def __init__(self, data): """ Personal collection properties dialog """ wx.Dialog.__init__(self, None, -1, "Collection properties", size=(400, 200)) self.Centre() # Create the text controls titleText = wx.StaticText(self, -1, "Collection properties") titleText.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) titleLine = wx.StaticLine(self, -1) buttonLine = wx.StaticLine(self, -1) name_l = wx.StaticText(self, -1, "Name:") name_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) owner_l = wx.StaticText(self, -1, "Owner:") owner_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) cont_l = wx.StaticText(self, -1, "Contents:") cont_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) loc_l = wx.StaticText(self, -1, "Location:") loc_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) ts_l = wx.StaticText(self, -1, "Modified:") ts_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) if len(data["name"]) > 30: name = data["name"][:20] + "..." + data["name"][len(data["name"])-7:] else: name = data["name"] name_t = wx.StaticText(self, -1, name) owner_t = wx.StaticText(self, -1, data["owner"]) if int(data["count"]) > 1: contents = data["count"] + " items, totalling " + data["size"] else: contents = "1 item, totalling " + data["size"] cont_t = wx.StaticText(self, -1, contents) loc_t = wx.StaticText(self, -1, data["loc"]) ts_t = wx.StaticText(self, -1, data["ts"]) # Use standard button IDs okay = wx.Button(self, wx.ID_OK) # Layout with sizers sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(titleText, 0, wx.EXPAND|wx.ALL, 10) sizer.Add(titleLine, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) fgs = wx.FlexGridSizer(5, 2, 5, 10) fgs.Add(name_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(name_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(owner_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(owner_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(cont_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(cont_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(loc_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(loc_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(ts_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(ts_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.AddGrowableCol(1) sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5) sizer.Add(buttonLine, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) btns = wx.StdDialogButtonSizer() btns.AddButton(okay) btns.Realize() sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5) self.SetSizer(sizer) class M_DataPropertiesDialog(wx.Dialog): def __init__(self, data): """ Personal data properties dialog TODO: display/edit metadata here add type, replica index, resource to properties dialog """ wx.Dialog.__init__(self, None, -1, "Data properties", size=(400, 200)) self.Centre() # Create the text controls titleText = wx.StaticText(self, -1, "Data properties") titleText.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) titleLine = wx.StaticLine(self, -1) buttonLine = wx.StaticLine(self, -1) name_l = wx.StaticText(self, -1, "Name:") name_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) owner_l = wx.StaticText(self, -1, "Owner:") owner_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) size_l = wx.StaticText(self, -1, "Size:") size_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) loc_l = wx.StaticText(self, -1, "Location:") loc_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) ts_l = wx.StaticText(self, -1, "Modified:") ts_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) if len(data["name"]) > 30: name = data["name"][:20] + "..." + data["name"][len(data["name"])-7:] else: name = data["name"] name_t = wx.StaticText(self, -1, name) owner_t = wx.StaticText(self, -1, data["owner"]) size_t = wx.StaticText(self, -1, data["size"]) loc_t = wx.StaticText(self, -1, data["loc"]) ts_t = wx.StaticText(self, -1, data["ts"]) # Use standard button IDs okay = wx.Button(self, wx.ID_OK) # Layout with sizers sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(titleText, 0, wx.EXPAND|wx.ALL, 10) sizer.Add(titleLine, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) fgs = wx.FlexGridSizer(5, 2, 5, 10) fgs.Add(name_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(name_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(owner_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(owner_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(size_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(size_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(loc_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(loc_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(ts_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(ts_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.AddGrowableCol(1) sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5) sizer.Add(buttonLine, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) btns = wx.StdDialogButtonSizer() btns.AddButton(okay) btns.Realize() sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5) self.SetSizer(sizer) class S_CollPropertiesDialog(wx.Dialog): def __init__(self, data): """ Shared collection properties dialog """ wx.Dialog.__init__(self, None, -1, "Collection properties", size=(400, 240)) self.Centre() # Create the text controls titleText = wx.StaticText(self, -1, "Collection properties") titleText.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) titleLine = wx.StaticLine(self, -1) buttonLine = wx.StaticLine(self, -1) name_l = wx.StaticText(self, -1, "Name:") name_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) owner_l = wx.StaticText(self, -1, "Shared by:") owner_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) cont_l = wx.StaticText(self, -1, "Contents:") cont_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) loc_l = wx.StaticText(self, -1, "Location:") loc_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) ticket_l = wx.StaticText(self, -1, "SRB ticket:") ticket_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) share_l = wx.StaticText(self, -1, "Share started:") share_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) expire_l = wx.StaticText(self, -1, "Share expiring:") expire_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) if len(data["name"]) > 30: name = data["name"][:20] + "..." + data["name"][len(data["name"])-7:] else: name = data["name"] name_t = wx.StaticText(self, -1, name) owner_t = wx.StaticText(self, -1, data["owner"]) if int(data["count"]) > 1: contents = data["count"] + " items, totalling " + data["size"] else: contents = "1 item, totalling " + data["size"] cont_t = wx.StaticText(self, -1, contents) loc_t = wx.StaticText(self, -1, data["loc"]) ticket_t = wx.StaticText(self, -1, data["ticket"]) share_t = wx.StaticText(self, -1, data["issue"]) expire_t = wx.StaticText(self, -1, data["expire"]) # Use standard button IDs okay = wx.Button(self, wx.ID_OK) # Layout with sizers sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(titleText, 0, wx.EXPAND|wx.ALL, 10) sizer.Add(titleLine, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) fgs = wx.FlexGridSizer(7, 2, 5, 10) fgs.Add(name_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(name_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(owner_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(owner_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(cont_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(cont_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(loc_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(loc_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(ticket_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(ticket_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(share_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(share_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(expire_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(expire_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.AddGrowableCol(1) sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5) sizer.Add(buttonLine, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) btns = wx.StdDialogButtonSizer() btns.AddButton(okay) btns.Realize() sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5) self.SetSizer(sizer) class S_DataPropertiesDialog(wx.Dialog): def __init__(self, data): """ Shared data properties dialog TODO: display/edit metadata here add type, replica index, resource to properties dialog """ wx.Dialog.__init__(self, None, -1, "Data properties", size=(400, 240)) self.Centre() # Create the text controls titleText = wx.StaticText(self, -1, "Data properties") titleText.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) titleLine = wx.StaticLine(self, -1) buttonLine = wx.StaticLine(self, -1) name_l = wx.StaticText(self, -1, "Name:") name_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) owner_l = wx.StaticText(self, -1, "Shared by:") owner_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) size_l = wx.StaticText(self, -1, "Size:") size_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) loc_l = wx.StaticText(self, -1, "Location:") loc_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) ticket_l = wx.StaticText(self, -1, "SRB Ticket:") ticket_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) share_l = wx.StaticText(self, -1, "Share started:") share_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) expire_l = wx.StaticText(self, -1, "Share expiring:") expire_l.SetFont(wx.Font(wx.DEFAULT, wx.NORMAL, wx.NORMAL, wx.BOLD)) if len(data["name"]) > 30: name = data["name"][:20] + "..." + data["name"][len(data["name"])-7:] else: name = data["name"] name_t = wx.StaticText(self, -1, name) owner_t = wx.StaticText(self, -1, data["owner"]) size_t = wx.StaticText(self, -1, data["size"]) loc_t = wx.StaticText(self, -1, data["loc"]) ticket_t = wx.StaticText(self, -1, data["ticket"]) share_t = wx.StaticText(self, -1, data["issue"]) expire_t = wx.StaticText(self, -1, data["expire"]) # Use standard button IDs okay = wx.Button(self, wx.ID_OK) # Layout with sizers sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(titleText, 0, wx.EXPAND|wx.ALL, 10) sizer.Add(titleLine, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) fgs = wx.FlexGridSizer(7, 2, 5, 10) fgs.Add(name_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(name_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(owner_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(owner_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(size_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(size_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(loc_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(loc_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(ticket_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(ticket_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(share_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(share_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.Add(expire_l, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) fgs.Add(expire_t, 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) fgs.AddGrowableCol(1) sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5) sizer.Add(buttonLine, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5) btns = wx.StdDialogButtonSizer() btns.AddButton(okay) btns.Realize() sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5) self.SetSizer(sizer) # here is the Access Grid Toolkit-based code... class StatusBar(wx.StatusBar): def __init__(self, parent): wx.StatusBar.__init__(self, parent, -1) self.sizeChanged = False self.parent = parent self.Bind(wx.EVT_SIZE, self.OnSize) self.progress = wx.Gauge(self, wx.NewId(), 100, style=wx.GA_HORIZONTAL|wx.GA_SMOOTH) self.progress.SetValue(True) self.cancelButton = wx.Button(self, wx.NewId(), "Cancel") wx.EVT_BUTTON(self, self.cancelButton.GetId(), self.OnCancel) self.__hideProgressUI() self.Reset() self.secondFieldWidth = -4 self.fields = 2 self.SetFieldsCount(self.fields) self.SetStatusWidths([-5, self.secondFieldWidth]) self.Reposition() def __hideProgressUI(self): self.hidden = 1 self.progress.Hide() self.cancelButton.Hide() def __showProgressUI(self): self.hidden = 0 self.progress.Show() self.cancelButton.Show() def SetMax(self, value): self.max = value def Reset(self): self.__cancelFlag = 0 self.transferDone = 0 self.fields = 3 self.SetFieldsCount(self.fields) self.SetStatusWidths([-5, -3, -1]) # set the initial position of the progress UI. self.Reposition() def SetMessage(self, text): self.SetStatusText(text, 0) def IsCancelled(self): if self.__cancelFlag: self.__hideProgressUI() return self.__cancelFlag def SetProgress(self, text, value, doneFlag): if self.hidden: self.__showProgressUI() if doneFlag: self.transferDone = doneFlag self.progress.SetValue(100) self.__hideProgressUI() self.SetMessage("Transfer complete") self.fields = 2 self.SetFieldsCount(self.fields) self.SetStatusWidths([-5, self.secondFieldWidth]) self.Reposition() return self.SetMessage(text) # Scale value to range 1-100 if self.max == 0: value = 100 else: value = int(100*value) self.progress.SetValue(value) def OnCancel(self, event): """ The cancel button was clicked. If we are still transferring, this is a cancel. If we are done transferring, this is an OK. """ if not self.transferDone: self.__cancelFlag = 1 self.__hideProgressUI() self.SetMessage("") self.fields = 2 self.SetFieldsCount(self.fields) self.SetStatusWidths([-5, self.secondFieldWidth]) self.Refresh() def OnSize(self, evt): """ Handles normal size events. """ self.Reposition() def Reposition(self): """ Make sure objects are positioned correct in the statusbar. """ if self.fields == 2: self.__hideProgressUI() return # Gauge rect = self.GetFieldRect(1) self.progress.SetPosition(wx.Point(rect.x, rect.y)) self.progress.SetSize(wx.Size(rect.width, rect.height)) # Cancel button rect = self.GetFieldRect(2) self.cancelButton.SetPosition(wx.Point(rect.x, rect.y)) self.cancelButton.SetSize(wx.Size(rect.width, rect.height)) class SRBFrame(wx.Frame): FILE_EXIT = wx.NewId() FILE_CONN = wx.NewId() FILE_DISCONN = wx.NewId() def __init__(self, parent, app, title, pos, size): """ Creates the browser UI and bind callbacks for UI events """ self.root = Node(text="root", _type="coll", metadata=None, parent=None) self.s_treeData = Node(text="root", _type="coll", metadata=None, parent=None) self.isConnected = False self.m_selected = False self.s_selected = False self.m_total = 0 self.s_total = 0 """ Storage formats TODO: what about on UserCancel - uploading: remove uploaded dirs/files - downloading: remove local dirs/files - transferring: remove local + uploaded dirs/files split personal and shared spaces as panels in GUI? auto-parse extension and assign icons? move whole thing to 'Shared Data' bit of VenueClient <- IN THE FUTURE!!! PersonalData format: [(name, owner, size, time-stamp, replication-index, resource, type, currentPath),,] m_datalist = PersonalData format ShareData format: [(owner, name, size, issue-time, revoke-time, ticket, currentpath, type, host, port),,] s_datalist = ShareData format """ self.PersonalData = [] self.ShareData = [] self.m_datalist = [] self.s_datalist = [] self.shared_names = [] wx.Frame.__init__(self, parent, -1, title, pos, size, wx.DEFAULT_FRAME_STYLE, title) self.parent = parent self.app = app # Load images imageSize = 22 il = wx.ImageList(imageSize, imageSize) self.fldridx = il.Add(wx.Bitmap("Folder.png", wx.BITMAP_TYPE_PNG)) self.fldropenidx = il.Add(wx.Bitmap("Folder-open.png", wx.BITMAP_TYPE_PNG)) self.fileidx = il.Add(wx.Bitmap("GenericData.png", wx.BITMAP_TYPE_PNG)) self.AGId = il.Add(icons.getDefaultNodeBitmap()) self.participantId = il.Add(icons.getDefaultParticipantBitmap()) self.menubar = wx.MenuBar() fileMenu = wx.Menu() fileMenu.Append(self.FILE_CONN, "&Connect") fileMenu.Append(self.FILE_DISCONN, "&Disconnect") fileMenu.Append(self.FILE_EXIT, "&Exit") self.menubar.Append(fileMenu, "&SRB") self.SetMenuBar(self.menubar) self.menubar.Enable(self.FILE_DISCONN, False) # status bar self.statusbar = StatusBar(self) self.SetStatusBar(self.statusbar) self.toolbar = self.CreateToolBar(wx.NO_BORDER) toolsize = (44,44) # - create the connect toolbar button bmp = wx.Image("Connect.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap() self.connButton = wx.BitmapButton(self.toolbar, -1, bmp, size=toolsize) self.connButton.SetToolTip(wx.ToolTip("SRB Connect/Disconnect")) self.toolbar.AddControl(self.connButton) self.toolbar.AddSeparator() # - create the navigation buttons GO_UP = self.toolbar.AddSimpleTool(1, wx.Image("Go-up.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap(), "Up", "") REFRESH = self.toolbar.AddSimpleTool(2, wx.Image("View-refresh.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap(), "Refresh", "") HOME = self.toolbar.AddSimpleTool(3, wx.Image("Go-home.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap(), "Home", "") self.toolbar.AddSeparator() # - create the I/O buttons NEW = self.toolbar.AddSimpleTool(4, wx.Image("Folder-new.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap(), "New", "") DOWNLOAD = self.toolbar.AddSimpleTool(5, wx.Image("Data-save.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap(), "Download", "") DELETE = self.toolbar.AddSimpleTool(6, wx.Image("Data-trash.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap(), "Delete", "") self.toolbar.Realize() self.toolbar.EnableTool(1, False) self.toolbar.EnableTool(2, False) self.toolbar.EnableTool(3, False) self.toolbar.EnableTool(4, False) self.toolbar.EnableTool(5, False) self.toolbar.EnableTool(6, False) m_Lbl = wx.StaticText(self, -1, "Personal SRB Space") m_Lbl.SetFont(wx.Font(16, wx.NORMAL, wx.NORMAL, wx.BOLD)) s_Lbl = wx.StaticText(self, -1, "Shared SRB Space") s_Lbl.SetFont(wx.Font(16, wx.NORMAL, wx.NORMAL, wx.BOLD)) self.m_tree = wx.TreeCtrl(self, style=wx.TR_HAS_BUTTONS|wx.TR_LINES_AT_ROOT|wx.TR_SINGLE) self.m_tree.AssignImageList(il) self.s_tree = wx.TreeCtrl(self, style=wx.TR_HAS_BUTTONS|wx.TR_LINES_AT_ROOT|wx.TR_SINGLE) self.s_tree.AssignImageList(il) self.m_list = wx.ListCtrl(self, -1, style=wx.LC_LIST|wx.LC_EDIT_LABELS, size=(150, 300)) self.m_list.AssignImageList(il, wx.IMAGE_LIST_SMALL) self.s_list = wx.ListCtrl(self, -1, style=wx.LC_LIST, size=(150, 300)) self.s_list.AssignImageList(il, wx.IMAGE_LIST_SMALL) self.m_list.Enable(False) # add buttons self.new_coll_Btn = wx.Button(self, -1, "&New Collection") self.upload_coll_Btn = wx.Button(self, -1, "Upload &Collection") self.upload_data_Btn = wx.Button(self, -1, "&Upload Data") self.m_downloadBtn = wx.Button(self, -1, "D&ownload") self.s_downloadBtn = wx.Button(self, -1, "Do&wnload from Shared SRB") self.deleteBtn = wx.Button(self, -1, "D&elete") self.shareBtn = wx.Button(self, -1, "S&hare") self.leftBtn = wx.Button(self, -1, "<--o") self.rightBtn = wx.Button(self, -1, "o-->") self.removeBtn = wx.Button(self, -1, "&Remove from Shared SRB") self.new_coll_Btn.Enable(False) self.upload_coll_Btn.Enable(False) self.upload_data_Btn.Enable(False) self.m_downloadBtn.Enable(False) self.deleteBtn.Enable(False) self.leftBtn.Enable(False) self.rightBtn.Enable(False) self.shareBtn.Enable(False) self.removeBtn.Enable(False) # share button sizer share_btn_sizer = wx.BoxSizer(wx.VERTICAL) share_btn_sizer.Add(self.rightBtn, 1, wx.EXPAND) share_btn_sizer.Add(self.leftBtn, 1, wx.EXPAND) # my srb sizer m_sizer = wx.BoxSizer(wx.VERTICAL) m_sizer.Add(m_Lbl, 0, wx.ALIGN_CENTER, 5) m_view_sizer = wx.BoxSizer(wx.HORIZONTAL) m_tree_border = wx.BoxSizer(wx.VERTICAL) m_tree_border.Add(self.m_tree, 1, wx.EXPAND|wx.ALL, 3) m_list_border = wx.BoxSizer(wx.VERTICAL) m_list_border.Add(self.m_list, 1, wx.EXPAND|wx.ALL, 3) m_view_sizer.Add(m_tree_border, 2, wx.EXPAND) m_view_sizer.Add(m_list_border, 3, wx.EXPAND) m_btn_1_sizer = wx.BoxSizer(wx.HORIZONTAL) m_btn_2_sizer = wx.BoxSizer(wx.HORIZONTAL) m_btn_1_sizer.Add(self.new_coll_Btn, 1, wx.EXPAND) m_btn_1_sizer.Add(self.upload_coll_Btn, 1, wx.EXPAND) m_btn_1_sizer.Add(self.upload_data_Btn, 1, wx.EXPAND) m_btn_2_sizer.Add(self.m_downloadBtn, 1, wx.EXPAND) m_btn_2_sizer.Add(self.deleteBtn, 1, wx.EXPAND) m_btn_2_sizer.Add(self.shareBtn, 1, wx.EXPAND) m_sizer.Add(m_view_sizer, 1, wx.EXPAND) m_sizer.Add(m_btn_1_sizer, 0, wx.EXPAND) m_sizer.Add(m_btn_2_sizer, 0, wx.EXPAND) # shared srb sizer s_sizer = wx.BoxSizer(wx.VERTICAL) s_sizer.Add(s_Lbl, 0, wx.ALIGN_CENTER, 5) s_view_sizer = wx.BoxSizer(wx.HORIZONTAL) s_tree_border = wx.BoxSizer(wx.VERTICAL) s_tree_border.Add(self.s_tree, 1, wx.EXPAND|wx.ALL, 3) s_list_border = wx.BoxSizer(wx.VERTICAL) s_list_border.Add(self.s_list, 1, wx.EXPAND|wx.ALL, 3) s_view_sizer.Add(s_tree_border, 2, wx.EXPAND) s_view_sizer.Add(s_list_border, 3, wx.EXPAND) s_btn_sizer = wx.BoxSizer(wx.HORIZONTAL) s_btn_sizer.Add(self.s_downloadBtn, 1, wx.EXPAND) s_btn_sizer.Add(self.removeBtn, 1, wx.EXPAND) s_sizer.Add(s_view_sizer, 1, wx.EXPAND) s_sizer.Add(s_btn_sizer, 0, wx.EXPAND) # window sizer sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(m_sizer, 1, wx.EXPAND) sizer.Add(share_btn_sizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND) sizer.Add(s_sizer, 1, wx.EXPAND) self.SetSizerAndFit(sizer) self.Centre() # bindings for personal tree self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.M_ItemExpandingCB, self.m_tree) self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.M_ItemCollapsedCB, self.m_tree) self.Bind(wx.EVT_TREE_SEL_CHANGED, self.M_SelChangedCB, self.m_tree) # bindings for shared tree self.Bind(wx.EVT_TREE_SEL_CHANGED, self.S_SelChangedCB, self.s_tree) # bindings for personal listctrl self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.M_ItemSelectedCB, self.m_list) self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.M_ItemDeselectedCB, self.m_list) self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.M_ItemActivatedCB, self.m_list) self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.M_RightClickCB, self.m_list) self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.M_CheckIfSharedCB, self.m_list) self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.M_RenameCB, self.m_list) # bindings for shared listctrl # TODO: need drag + drop from shared listctrl -> personal listctrl self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.S_ItemSelectedCB, self.s_list) self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.S_ItemDeselectedCB, self.s_list) self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.S_ItemActivatedCB, self.s_list) self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.S_RightClickCB, self.s_list) #self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.S_TransferCB, self.s_list) # bindings for toolbar self.Bind(wx.EVT_MENU, self.GoUpCB, GO_UP) self.Bind(wx.EVT_MENU, self.RefreshCB, REFRESH) self.Bind(wx.EVT_MENU, self.GoHomeCB, HOME) self.Bind(wx.EVT_MENU, self.M_NewCollCB, NEW) self.Bind(wx.EVT_MENU, self.DownloadCB, DOWNLOAD) self.Bind(wx.EVT_MENU, self.TrashCB, DELETE) # bindings for buttons + menu items wx.EVT_BUTTON(self, self.new_coll_Btn.GetId(), self.M_NewCollCB) wx.EVT_BUTTON(self, self.upload_coll_Btn.GetId(), self.M_UploadDirCB) wx.EVT_BUTTON(self, self.upload_data_Btn.GetId(), self.M_UploadFilesCB) wx.EVT_BUTTON(self, self.deleteBtn.GetId(), self.M_DeleteCB) wx.EVT_BUTTON(self, self.m_downloadBtn.GetId(), self.M_DownloadCB) wx.EVT_BUTTON(self, self.s_downloadBtn.GetId(), self.S_DownloadCB) wx.EVT_BUTTON(self, self.rightBtn.GetId(), self.ShareCB) wx.EVT_BUTTON(self, self.leftBtn.GetId(), self.S_TransferCB) wx.EVT_BUTTON(self, self.shareBtn.GetId(), self.ShareCB) wx.EVT_BUTTON(self, self.removeBtn.GetId(), self.RemoveCB) wx.EVT_MENU(self, self.FILE_CONN, self.ConnectCB) wx.EVT_MENU(self, self.FILE_DISCONN, self.DisconnectCB) wx.EVT_MENU(self, self.FILE_EXIT, self.CloseCB) wx.EVT_CLOSE(self, self.CloseCB) wx.EVT_BUTTON(self, self.connButton.GetId(), self.ConnectionCB) """ Methods for intializing and populating personal/shared TreeCtrls """ def Init_M_Tree(self, treeData): """ Initializing the personal tree """ self.m_tree.DeleteAllItems() m_root = self.m_tree.AddRoot(self.rootPath.split("/")[-1]) self.m_tree.SetItemPyData(m_root, treeData) self.m_tree.SetItemHasChildren(m_root, True) self.m_tree.Expand(m_root) def Add_M_TreeNodes(self, parentItem): """ Adding node data into tree """ items = self.m_tree.GetItemPyData(parentItem) for nodeItem in items.children: itemIdx = nodeItem.text.split("/")[-1] newItem = self.m_tree.AppendItem(parentItem, itemIdx) self.m_tree.SetItemImage(newItem, self.fldridx, wx.TreeItemIcon_Normal) self.m_tree.SetItemImage(newItem, self.fldropenidx, wx.TreeItemIcon_Expanded) if nodeItem.haveChildren: self.m_tree.SetItemHasChildren(newItem, True) self.m_tree.SetPyData(newItem, nodeItem) def Init_S_Tree(self, treeData): """ Initializing the shared tree """ self.s_tree.DeleteAllItems() s_root = self.s_tree.AddRoot("All Shared Data") self.s_tree.SetItemImage(s_root, self.AGId, wx.TreeItemIcon_Normal) self.Add_S_TreeNodes(s_root, treeData) self.s_tree.Expand(s_root) self_node = self.s_tree.GetSelection() self.s_tree.UnselectItem(self_node) self.s_tree.SelectItem(self_node) def Add_S_TreeNodes(self, parentItem, items): """ Adding node data into tree """ for nodeItem in items.children: if nodeItem._type != "data": itemIdx = nodeItem.text.split("/")[-1] newItem = self.s_tree.AppendItem(parentItem, itemIdx) if nodeItem._type == "user": self.s_tree.SetItemImage(newItem, self.participantId, wx.TreeItemIcon_Normal) elif nodeItem._type == "coll": self.s_tree.SetItemImage(newItem, self.fldridx, wx.TreeItemIcon_Normal) self.s_tree.SetItemImage(newItem, self.fldropenidx, wx.TreeItemIcon_Expanded) self.s_tree.SetPyData(newItem, nodeItem) self.Add_S_TreeNodes(newItem, nodeItem) """ Methods for updating the statusbar """ def UpdateStatusCancelled(self): return self.statusbar.IsCancelled() def UpdateStatus(self, op, filename, size, sent, xfer_done): # op: operation type # "Uploading", "Downloading" text = op + " " + filename wx.CallAfter(self.statusbar.SetMax, size) wx.CallAfter(self.statusbar.SetProgress, text, sent, xfer_done) """ Methods for updating the personal ListCtrl and TreeCtrl """ def RefreshList(self): self.PersonalData = self.app.On_M_RefreshList(self.currentPath) self.Set_M_ListCB(self.PersonalData) def RefreshTree(self): self.m_treeData = self.app.getColls(self.root, "coll", self.rootPath) self.Init_M_Tree(self.m_treeData) """ Toolbar callbacks """ def ConnectionCB(self, event): if self.isConnected: self.DisconnectCB(event) else: self.ConnectCB(event) def GoUpCB(self, event): if self.currentPath != self.rootPath: try: # try to go up through parent node parent_node = self.m_tree.GetItemParent(self.selected_node) self.m_tree.UnselectItem(self.selected_node) self.m_tree.SelectItem(parent_node) except: # otherwise through parent path parent, current = os.path.split(self.currentPath) self.currentPath = parent self.RefreshList() self.m_selected = False self.m_datalist = [] def RefreshCB(self, event): self.RefreshList() def GoHomeCB(self, event): if self.currentPath != self.rootPath: try: # try to go to root node root_node = self.m_tree.GetRootItem() self.m_tree.UnselectItem(self.selected_node) self.m_tree.SelectItem(root_node) except: # otherwise change to rootPath self.PersonalData = self.app.On_M_RefreshList(self.rootPath) self.Set_M_ListCB(self.PersonalData) def DownloadCB(self, event): if self.m_selected: self.M_DownloadCB(event) elif self.s_selected: self.S_DownloadCB(event) def TrashCB(self, event): if self.m_selected: self.M_DeleteCB(event) elif self.s_selected: self.RemoveCB(event) """ Menu and GUI button callbacks """ def CloseCB(self, event): """ Callback for closing SRB browser """ answer = wx.MessageBox("Do you really want to quit?", "Exit confirmation", wx.YES_NO, self) if answer == wx.YES: if self.isConnected == True: self.app.OnDisconnect() self.app.OnExit() else: self.app.OnExit() log.info("Close down") else: pass def enableGUI(self, switch): """ Callback for enabling/disabling GUI elements """ self.new_coll_Btn.Enable(switch) self.upload_coll_Btn.Enable(switch) self.upload_data_Btn.Enable(switch) self.m_downloadBtn.Enable(switch) self.deleteBtn.Enable(switch) self.leftBtn.Enable(switch) self.rightBtn.Enable(switch) self.shareBtn.Enable(switch) self.removeBtn.Enable(switch) self.m_tree.Enable(switch) self.m_list.Enable(switch) self.toolbar.EnableTool(2, switch) self.toolbar.EnableTool(4, switch) self.toolbar.EnableTool(5, switch) self.toolbar.EnableTool(6, switch) self.menubar.Enable(self.FILE_DISCONN, switch) self.menubar.Enable(self.FILE_CONN, not(switch)) self.menubar.Enable(self.FILE_EXIT, not(switch)) def ConnectCB(self, event): """ Callback for connecting to SRB """ defaultSettings = {"resc":"demoResc", "host":"laing.vislab.uq.edu.au", "port":"5544", "auth":"ENCRYPT1", "dom":"demo", "user":"terry", "pwd":"terry", "coll":"/A/home/terry.demo"} dlgConnect = ConnectionDialog(defaultSettings) if dlgConnect.ShowModal() == wx.ID_OK: self.currentPath, conn = self.app.OnInitSRB(defaultSettings) if conn > 0: self.isConnected = True self.rootPath = self.currentPath self.enableGUI(True) self.RefreshTree() self.selected_node = self.m_tree.GetSelection() self.RefreshList() self.connButton.SetBitmapLabel(wx.Image("Disconnect.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()) dlgConnect.Destroy() def DisconnectCB(self, event): """ Callback for disconnecting from SRB """ dlgDisconnect = wx.MessageDialog(None, "Disconnect from SRB Server?", "SRB Connect Tool", wx.YES_NO) if dlgDisconnect.ShowModal() == wx.ID_YES: self.app.OnDisconnect() self.isConnected = False self.m_list.DeleteAllItems() self.m_tree.DeleteAllItems() self.enableGUI(False) self.connButton.SetBitmapLabel(wx.Image("Connect.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()) dlgDisconnect.Destroy() def M_NewCollCB(self, event): """ Callback for creating collection in personal space TODO: add dialog to input metadata here """ dlgNew = wx.TextEntryDialog(None, "New collection name?", "New Collection", style=wx.OK|wx.CANCEL) if dlgNew.ShowModal() == wx.ID_OK and dlgNew.GetValue() != "": self.app.On_M_NewColl(dialog.GetValue(), self.currentPath) dlgNew.Destroy() self.RefreshTree() self.RefreshList() def M_DeleteCB(self, event): """ Callback for deleting data/collection """ if self.m_selected == True: self.shared_names = [] self.shared_listChildren(self.shared_names, self.s_treeData) shared = sets.Set(self.shared_names) for data in self.m_datalist: selected_name = data[7] + "/" + data[0] if selected_name in shared: dlg = wx.MessageDialog(None, "Cannot delete shared SRB item '" + data[0] + "'.", "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() else: if data[6] == "coll": # Deleting collection dlgDel = wx.MessageDialog(None, "Really delete collection: '" + str(data[0]) + "'?", "agSRB Browser", wx.YES_NO) if dlgDel.ShowModal() == wx.ID_YES: collName = data[0].split("/")[0] self.app.On_M_DeleteColl(collName, self.currentPath) dlgDel.Destroy() else: # Deleting data dlgDel = wx.MessageDialog(None, "Really delete data: '" + str(data[0]) + "'?", "agSRB Browser", wx.YES_NO) if dlgDel.ShowModal() == wx.ID_YES: self.app.On_M_DeleteData(str(data[0]), self.currentPath) dlgDel.Destroy() self.RefreshList() self.RefreshTree() self.m_selected = False self.m_datalist = [] def ShareCB(self, event): """ Callback for sharing data to shared space TODO: share metadata """ if self.m_selected == True: self.shared_names = [] self.shared_listChildren(self.shared_names, self.s_treeData) shared = sets.Set(self.shared_names) for data in self.m_datalist: selected_name = data[7] + "/" + data[0] if selected_name in shared: dlg = wx.MessageDialog(None, "The item '" + data[0] + "' is already shared.", "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() else: if data[6] == "coll": # Sharing collection new_coll = (self.app.publicName, data[0], "", "", "", "", self.currentPath, "coll", "", "") self.app.OnNewShare(new_coll) else: status, data_ticket = self.app.OnIssueTicket(data[0], self.currentPath, "", "", "") if int(status) < 0: error_msg = srb.get_error(int(status)) dlg = wx.MessageDialog(None, "Cannot issue ticket for '" + data[0] + "' \nSRB Error: "+ error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() else: # Sharing data new_data = (self.app.publicName, data[0], data[2], str(time.asctime()), "...", data_ticket, self.currentPath, "data", self.app._host, self.app._port) self.app.OnNewShare(new_data) wx.CallAfter(self.Init_S_Tree, self.s_treeData) self.m_selected = False self.m_datalist = [] def RemoveCB(self, event): """ Callback for removing data from shared space # need to update! """ if self.s_selected == True: for data in self.s_datalist: if str(data[0]) == self.app.publicName: self.app.OnRemoveShare(data) self.app.OnRemoveTicket(str(data[1]), str(data[5])) wx.CallAfter(self.Init_S_Tree, self.s_treeData) self.s_selected = False self.s_datalist = [] if len(self.ShareData) == 0: self.statusbar.SetStatusText("0 items", 1) def S_TransferCB(self, event): """ Callback for transfering data from shared to personal space """ transferName = [] temp_coll = [] temp_data = [] if self.s_selected == True: for data in self.s_datalist: if data[7] == "coll": temp_coll.append(data) else: temp_data.append(data) # Download collection to temp location for data in temp_coll: if Platform.isWindows(): saveLocation = tempfile.tempdir + "\\srb\\" + data[1].split("/")[-1] else: saveLocation = tempfile.tempdir + "/srb/" + data[1].split("/")[-1] self.statusbar.Reset() self.app.On_S_DownloadColl(data[8], data[9], data[1].split("/")[-1], data[6], saveLocation, data[5], False) transferName.append((saveLocation, "coll")) # Download data to temp location for data in temp_data: if Platform.isWindows(): saveLocation = tempfile.tempdir + "\\srb\\" else: saveLocation = tempfile.tempdir + "/srb/" self.statusbar.Reset() self.app.On_S_DownloadData(data[8], data[9], data[1], data[2], data[6], saveLocation, data[5]) transferName.append((saveLocation, "data")) # Upload collection/data to personal SRB for name in transferName: if name[1] == "coll": self.statusbar.Reset() self.app.On_M_UploadDir(str(name[0]), self.currentPath) else: self.statusbar.Reset() self.app.On_M_UploadFile(str(name[0]) + data[1], self.currentPath) # TOFIX: need to get proper path self.RefreshTree() self.RefreshList() # Remove directory/files in temp location for name in transferName: if name[1] == "data": os.remove(name[0]) else: if Platform.isWindows(): walkPath = tempfile.tempdir + "\\srb\\" else: walkPath = tempfile.tempdir + "/srb/" for root, dirs, files in os.walk(walkPath, topdown=False): for file in files: os.remove(os.path.join(root, file)) for dr in dirs: os.rmdir(os.path.join(root, dr)) self.s_selected = False self.s_datalist = [] def M_UploadDirCB(self, event): """ Callback for uploading collection to shared space TODO: add dialog to input metadata here """ dlgUploadDir = wx.DirDialog(None, "Choose a directory to upload...", defaultPath=os.getcwd(), style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE) if dlgUploadDir.ShowModal() == wx.ID_OK: uploadPath = dlgUploadDir.GetPath() self.statusbar.Reset() self.app.On_M_UploadDir(uploadPath, self.currentPath) dlgUploadDir.Destroy() self.RefreshTree() self.RefreshList() def M_AGUploadCB(self, event): """ Callback for uploading data to AG DataStore for sharing to non-SRB users """ # Download data to temp location for data in self.m_datalist: if Platform.isWindows(): saveLocation = tempfile.tempdir + "\\srb\\" + data[0] else: saveLocation = tempfile.tempdir + "/srb/" + data[0] self.statusbar.Reset() self.app.On_M_DownloadData(data[0], self.currentPath, saveLocation) # Upload data to AG DataStore dsc = GetVenueDataStore(self.app.venueUrl, self.app.connectionId) dsc.Upload(saveLocation) # Remove file from temp location os.remove(saveLocation) def M_UploadFilesCB(self, event): """ Callback for uploading data to personal space TODO: add dialog to input metadata here """ dlgUpload = wx.FileDialog(self, "Choose file(s) to upload...", tempfile.tempdir, style=wx.OPEN|wx.MULTIPLE) if dlgUpload.ShowModal() == wx.ID_OK: uploadList = dlgUpload.GetPaths() self.statusbar.Reset() for file in uploadList: self.app.On_M_UploadFile(str(file), self.currentPath) dlgUpload.Destroy() def S_DownloadCB(self, event): """ Callback for downloading data/collection from shared space """ temp_coll = [] temp_data = [] if self.s_selected == True: for data in self.s_datalist: if data[7] == "coll": temp_coll.append(data) else: temp_data.append(data) # Downloading a collection for data in temp_coll: if os.path.isdir(data[1].split("/")[-1]): # Existing directory dlg = wx.SingleChoiceDialog(None, "A folder with the same name already exists.\nWhat would you like to do?", "Download Options", ["Skip", "Save with different name", "Replace"]) if dlg.ShowModal() == wx.ID_OK: response = dlg.GetStringSelection() if response == "Replace": self.app.On_S_DownloadColl(data[8], data[9], data[1].split("/")[-1], data[6], os.getcwd() + "/" + data[1].split("/")[-1], data[5], True) if response == "Save with different name": dlgSave = wx.FileDialog(self, "Type new name to save as...", os.getcwd(), data[1].split("/")[-1], style=wx.SAVE|wx.OVERWRITE_PROMPT) if dlgSave.ShowModal() == wx.ID_OK: aveLocation = dlgSave.GetPath() self.app.On_S_DownloadColl(data[8], data[9], data[1].split("/")[-1], data[6], saveLocation, data[5], False) dlgSave.Destroy() dlg.Destroy() else: dlgSave = wx.FileDialog(self, "Choose location to save...", os.getcwd(), data[1].split("/")[-1], style=wx.SAVE|wx.OVERWRITE_PROMPT) if dlgSave.ShowModal() == wx.ID_OK: saveLocation = dlgSave.GetPath() self.app.On_S_DownloadColl(data[8], data[9], data[1].split("/")[-1], data[6], saveLocation, data[5], False) dlgSave.Destroy() # Downloading data for data in temp_data: dlgSave = wx.FileDialog(self, "Choose location to save...", os.getcwd(), data[1], style=wx.SAVE|wx.OVERWRITE_PROMPT) if dlgSave.ShowModal() == wx.ID_OK: saveLocation = dlgSave.GetPath() self.statusbar.Reset() self.app.On_S_DownloadData(data[8], data[9], data[1], data[2], data[6], saveLocation, data[5]) dlgSave.Destroy() self.s_selected = False self.s_datalist = [] def M_DownloadCB(self, event): """ Callback for downloading data/collection from personal space """ temp_coll = [] temp_data = [] if self.m_selected == True: for data in self.m_datalist: if data[6] == "coll": temp_coll.append(data) else: temp_data.append(data) # Downloading a collection for data in temp_coll: if os.path.isdir(data[0]): # Existing directory dlg = wx.SingleChoiceDialog(None, "A folder with the same name already exists.\nWhat would you like to do?", "Download Options", ["Skip", "Save with different name", "Replace"]) if dlg.ShowModal() == wx.ID_OK: response = dlg.GetStringSelection() if response == "Replace": self.app.On_M_DownloadColl(data[0], self.currentPath, os.getcwd() + "/" + data[0], True) if response == "Save with different name": dlgSave = wx.FileDialog(self, "Type new name to save as...", tempfile.tempdir, data[0], style=wx.SAVE|wx.OVERWRITE_PROMPT) if dlgSave.ShowModal() == wx.ID_OK: saveLocation = dlgSave.GetPath() self.app.On_M_DownloadColl(data[0], self.currentPath, saveLocation, False) dlgSave.Destroy() dlg.Destroy() else: dlgSave = wx.FileDialog(self, "Choose location to save...", os.getcwd(), data[0], style=wx.SAVE|wx.OVERWRITE_PROMPT) if dlgSave.ShowModal() == wx.ID_OK: saveLocation = dlgSave.GetPath() self.app.On_M_DownloadColl(data[0], self.currentPath, saveLocation, False) dlgSave.Destroy() # Downloading data for data in temp_data: dlgSave = wx.FileDialog(self, "Choose location to save...", os.getcwd(), data[0], style=wx.SAVE|wx.OVERWRITE_PROMPT) if dlgSave.ShowModal() == wx.ID_OK: saveLocation = dlgSave.GetPath() self.statusbar.Reset() self.app.On_M_DownloadData(data[0], self.currentPath, saveLocation) dlgSave.Destroy() self.m_selected = False self.m_datalist = [] """ Methods for setting icon display in ListCtrl """ def Set_M_ListCB(self, personal_data): self.m_list.DeleteAllItems() for item in personal_data: if item[6] == "coll": index = self.m_list.InsertImageStringItem(sys.maxint, item[0], self.fldridx) else: index = self.m_list.InsertImageStringItem(sys.maxint, item[0], self.fileidx) def Set_S_ListCB(self, share_list): self.s_list.DeleteAllItems() share_list.sort() for item in share_list: if item[7] == "coll": index = self.s_list.InsertImageStringItem(sys.maxint, item[1].split("/")[-1], self.fldridx) self.s_list.SetItemData(index, share_list.index(item)) else: index = self.s_list.InsertImageStringItem(sys.maxint, item[1].split("/")[-1], self.fileidx) self.s_list.SetItemData(index, share_list.index(item)) """ Events from TreeCtrl mouse events. """ def M_ItemExpandingCB(self, event): self.Add_M_TreeNodes(event.GetItem()) def M_ItemCollapsedCB(self, event): self.m_tree.DeleteChildren(event.GetItem()) def M_SelChangedCB(self, event): self.selected_node = self.m_tree.GetSelection() location = self.m_tree.GetPyData(event.GetItem()) self.currentPath = location.text.strip() if self.currentPath == "root": self.currentPath = self.rootPath if self.currentPath != self.rootPath: self.toolbar.EnableTool(1, True) self.toolbar.EnableTool(3, True) else: self.toolbar.EnableTool(1, False) self.toolbar.EnableTool(3, False) self.RefreshList() self.m_selected = False self.m_datalist = [] self.m_total = 0 if len(self.PersonalData) > 0: if len(self.PersonalData) == 1: self.statusbar.SetStatusText("1 item", 0) else: self.statusbar.SetStatusText(str(len(self.PersonalData)) + " items", 0) else: self.statusbar.SetStatusText("0 items", 0) def S_SelChangedCB(self, event): self.ShareData = [] s_user = self.s_tree.GetItemText(event.GetItem()) selected = self.s_tree.GetPyData(event.GetItem()) if s_user == "All Shared Data": for user in self.s_treeData.children: for nodeItem in user.children: self.ShareData.append(nodeItem.metadata) else: if selected._type == "data": self.ShareData.append(selected.metadata) else: for nodeItem in selected.children: self.ShareData.append(nodeItem.metadata) self.Set_S_ListCB(self.ShareData) self.s_selected = False self.s_datalist = [] self.s_total = 0 if len(self.ShareData) > 1 or len(self.ShareData) == 0: self.statusbar.SetStatusText(str(len(self.ShareData)) + " items", 1) else: self.statusbar.SetStatusText("1 item", 1) """ Events from ListCtrl mouse events. """ def M_ItemSelectedCB(self, event): item = event.GetItem() selected = int(event.GetIndex()) self.m_datalist.append(self.PersonalData[selected]) if len(self.m_datalist) > 1: for data in self.m_datalist: self.m_total = self.m_total + int(data[2]) self.statusbar.SetStatusText(str(len(self.m_datalist)) + " items selected (" + self.toUnits(self.m_total) + ")", 0) else: self.statusbar.SetStatusText("'" + str(self.PersonalData[selected][0]) + "' selected (" + self.toUnits(self.PersonalData[selected][2]) + "), owner: " + str(self.PersonalData[selected][1]), 0) self.m_selected = True self.s_selected = False def M_ItemDeselectedCB(self, event): self.m_selected = False self.m_datalist = [] self.m_total = 0 if len(self.PersonalData) > 1: self.statusbar.SetStatusText(str(len(self.PersonalData) - 1) + " items", 0) elif len(self.PersonalData) == 1: self.statusbar.SetStatusText("0 items", 0) def M_CheckIfSharedCB(self, event): """ Checks if the item to be renamed is already shared """ item = event.GetItem() selected = int(event.GetIndex()) selectedPath = self.currentPath + "/" + self.PersonalData[selected][0] self.shared_names = [] self.shared_listChildren(self.shared_names, self.s_treeData) print self.shared_names shared = sets.Set(self.shared_names) if selectedPath in shared: event.Veto() dlg = wx.MessageDialog(None, "Cannot rename shared SRB item '" + self.PersonalData[selected][0] + "'.", "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() def M_RenameCB(self, event): if event.IsEditCancelled(): return item = event.GetItem() selected = int(event.GetIndex()) newName = str(item.GetText()) wx.CallAfter(self.app.On_M_Rename, self.PersonalData[selected][0], newName, self.currentPath, self.PersonalData[selected][6]) self.RefreshTree() self.RefreshList() def M_ItemActivatedCB(self, event): item = event.GetItem() selected = int(event.GetIndex()) if self.PersonalData[selected][6] == "coll": child = str(item.GetText()) if self.currentPath != "/": self.currentPath = self.currentPath + "/" + child else: self.currentPath = self.currentPath + child self.RefreshList() self.m_selected = False self.m_datalist = [] self.toolbar.EnableTool(1, True) self.toolbar.EnableTool(3, True) else: """ TODO: filetypes: images->gqview/firefox, PDF->kpdf, TEXT->gedit, kml->google-earth how to link? or use internal viewer for some types? """ self.m_selected = False self.m_datalist = [] def M_RightClickCB(self, event): """ Generates right-click menu for personal data """ selected = -1 selected = int(event.GetIndex()) if selected > -1: self.m_datalist = [] self.m_datalist.append(self.PersonalData[selected]) self.m_selected = True m_menu = wx.Menu() if self.m_datalist[0][6] != "coll": id = wx.NewId() m_menu.Append(id, "Upload to AG Data") wx.EVT_MENU(m_menu, id, self.M_AGUploadCB) id = wx.NewId() m_menu.Append(id, "Download") wx.EVT_MENU(m_menu, id, self.M_DownloadCB) id = wx.NewId() m_menu.Append(id, "Delete") wx.EVT_MENU(m_menu, id, self.M_DeleteCB) m_menu.AppendSeparator() id = wx.NewId() m_menu.Append(id, "Properties") wx.EVT_MENU(m_menu, id, self.M_PropertiesCB) self.m_list.PopupMenu(m_menu, event.GetPoint()) else: m_menu = wx.Menu() id = wx.NewId() m_menu.Append(id, "Create Collection") wx.EVT_MENU(m_menu, id, self.M_NewCollCB) m_menu.AppendSeparator() id = wx.NewId() m_menu.Append(id, "Upload Directory") wx.EVT_MENU(m_menu, id, self.M_UploadDirCB) id = wx.NewId() m_menu.Append(id, "Upload Files") wx.EVT_MENU(m_menu, id, self.M_UploadFilesCB) self.m_list.PopupMenu(m_menu, event.GetPoint()) def S_ItemSelectedCB(self, event): selected = self.s_list.GetItemData(event.GetIndex()) self.s_datalist.append(self.ShareData[selected]) if len(self.s_datalist) > 1: for data in self.s_datalist: self.s_total = self.s_total + int(data[2]) self.statusbar.SetStatusText(str(len(self.s_datalist)) + " items selected (" + self.toUnits(self.s_total) + ")", 1) else: self.statusbar.SetStatusText("'" + str(self.ShareData[selected][1].split("/")[-1]) + "' selected (" + self.toUnits(self.ShareData[selected][2]) + ") , shared by: " + str(self.ShareData[selected][0]), 1) self.s_selected = True self.m_selected = False def S_ItemDeselectedCB(self, event): self.s_selected = False self.s_datalist = [] self.s_total = 0 if len(self.ShareData) > 1 or len(self.ShareData) == 0: self.statusbar.SetStatusText(str(len(self.ShareData)) + " items", 1) else: self.statusbar.SetStatusText("1 item", 1) def S_ItemActivatedCB(self, event): item = event.GetItem() selected = int(event.GetIndex()) """ TODO: filetypes: images->gqview/firefox, PDF->kpdf, TEXT->gedit, kml->google-earth how to link? or use internal viewer for some types? """ self.s_selected = False self.s_datalist = [] def S_RightClickCB(self, event): """ Generates right-click menu for shared data """ selected = int(event.GetIndex()) if selected > -1: self.s_datalist = [] self.s_datalist.append(self.ShareData[selected]) self.s_selected = True s_menu = wx.Menu() id = wx.NewId() s_menu.Append(id, "Download") wx.EVT_MENU(s_menu, id, self.S_DownloadCB) if self.isConnected == True: id = wx.NewId() s_menu.Append(id, "Transfer to Personal SRB") wx.EVT_MENU(s_menu, id, self.S_TransferCB) s_menu.AppendSeparator() id = wx.NewId() s_menu.Append(id, "Properties") wx.EVT_MENU(s_menu, id, self.S_PropertiesCB) self.s_list.PopupMenu(s_menu, event.GetPoint()) def M_PropertiesCB(self, event): """ Callback for displaying Personal data properties dialog """ if self.m_datalist[0][6] == "coll": count, total = self.app.get_M_Total(self.m_datalist[0][0], self.currentPath) dataProperties = {"name":self.m_datalist[0][0], "owner":self.m_datalist[0][1], "size":self.toUnits(total), "count":str(count), "ts":self.m_datalist[0][3], "loc":self.m_datalist[0][7]} dlgProperties = M_CollPropertiesDialog(dataProperties) dlgProperties.ShowModal() dlgProperties.Destroy() self.app.m_selected_list = [] self.app.m_total = 0 else: dataProperties = {"name":self.m_datalist[0][0], "owner":self.m_datalist[0][1], "size":self.toUnits(self.m_datalist[0][2]), "ts":self.m_datalist[0][3], "loc":self.m_datalist[0][7]} dlgProperties = M_DataPropertiesDialog(dataProperties) dlgProperties.ShowModal() dlgProperties.Destroy() self.m_selected = False self.m_datalist = [] def S_PropertiesCB(self, event): """ Callback for displaying Shared data properties dialog """ if self.s_datalist[0][7] == "coll": count, total = self.app.get_S_Total(self.s_datalist[0][1]) dataProperties = {"owner":self.s_datalist[0][0], "name":self.s_datalist[0][1].split("/")[-1], "size":self.toUnits(total), "count":str(count), "ticket":self.s_datalist[0][5], "issue":self.s_datalist[0][3], "expire":self.s_datalist[0][4], "loc":self.s_datalist[0][8]} dlgProperties = S_CollPropertiesDialog(dataProperties) dlgProperties.ShowModal() dlgProperties.Destroy() self.app.s_selected_list = [] self.app.s_total = 0 else: dataProperties = {"owner":self.s_datalist[0][0], "name":self.s_datalist[0][1], "size":self.toUnits(self.s_datalist[0][2]), "ticket":self.s_datalist[0][5], "issue":self.s_datalist[0][3], "expire":self.s_datalist[0][4], "loc":self.s_datalist[0][8]} dlgProperties = S_DataPropertiesDialog(dataProperties) dlgProperties.ShowModal() dlgProperties.Destroy() self.s_selected = False self.s_datalist = [] def S_DragDropCB(self, event): pass def toUnits(self, filesize): """ Method for converting data size into human readable format. """ floatSize = float(filesize) if filesize < 1023: return str(round(filesize, 2)) + " bytes" floatSize = floatSize / 1024; if floatSize < 1023: return str(round(floatSize, 2)) + " KB" floatSize = floatSize / 1024; if floatSize < 1023: return str(round(floatSize, 2)) + " MB" floatSize = floatSize / 1024; if floatSize < 1023: return str(round(floatSize, 2)) + " GB" floatSize = floatSize / 1024; return str(round(floatSize, 2)) + " TB" def shared_listChildren(self, wxParent, treeStruct): """ Fills the 'shared_names' list object with the children of the specified node. """ for nodeItem in treeStruct.children: wxItem = self.shared_names.append(nodeItem.text) self.shared_listChildren(wxItem, nodeItem) class agSRB(wx.App): appName = "ag-SRB Browser" appDescription = "AG Shared SRB Browser" appMimetype = "application/x-ag-srb" def __init__(self, appUrl, venueUrl=None, name='agSRB', connectionId=None): """ Creates the shared application client, used for application service interaction, and opens a SRB browser for UI display. """ wx.App.__init__(self, False) self.reactorFinished = [] reactor.interleave(wx.CallAfter) # Create shared application client self.sharedAppClient = SharedAppClient(name) self.log = self.sharedAppClient.InitLogging() self.venueUrl = venueUrl self.connectionId = connectionId # Get client profile try: clientProfileFile = os.path.join(UserConfig.instance().GetConfigDir(), "profile") clientProfile = ClientProfile(clientProfileFile) except: self.log.info("SharedAppClient.Connect: Could not load client profile, set clientProfile = None") clientProfile = None # Join the application session. self.sharedAppClient.Join(appUrl, clientProfile) self.publicId = self.sharedAppClient.GetPublicId() self.publicName = str(clientProfile.name) self.log.info(self.publicName) # Set venue url. if not self.venueUrl: self.venueUrl = self.sharedAppClient.GetVenueURL() # Register event callbacks self.sharedAppClient.RegisterEventCallback("modifiedData", self.OnRemoteEvent) # Create SRB browser window width, height = 800, 600 self.frame = SRBFrame(None, self, "agSRB Browser", wx.DefaultPosition, wx.DefaultSize) self.m_downlist = [] self.s_downlist = [] self.m_selected_list = [] self.m_total = 0 self.s_selected_list = [] self.s_total = 0 # Load the current shared data, if exists node_object = self.sharedAppClient.GetData("sharenode") if node_object: self.frame.s_treeData = cPickle.loads(str(node_object)) self.frame.Init_S_Tree(self.frame.s_treeData) self.frame.ShareData = [] for user in self.frame.s_treeData.children: for nodeItem in user.children: self.frame.ShareData.append(nodeItem.metadata) self.frame.Set_S_ListCB(self.frame.ShareData) self.frame.Show(True) self.SetTopWindow(self.frame) def OnInit(self): return 1 def OnInitSRB(self, serverSettings): """ Initialize SRB-based stuff. """ self.venueURL = "default" self.resource = serverSettings["resc"] self._host = serverSettings["host"] self._port = serverSettings["port"] _auth = serverSettings["auth"] _srbDomain = serverSettings["dom"] _user = serverSettings["user"] _passwd = serverSettings["pwd"] self.currentPath = serverSettings["coll"] self.connID = srb.connect(self._host, self._port, _srbDomain, _auth, _user, _passwd, "") if self.connID > 0: log.info("Connected to SRB Server (id=%d)" %self.connID) log.info(str(self.resource)) log.info(str(self._host)) log.info(str(self._port)) log.info(str(_auth)) log.info(str(_srbDomain)) log.info(str(_user)) log.info(str(self.currentPath)) return self.currentPath, self.connID else: log.info("SRB server Connection Error") def OnDisconnect(self): """ Disconnect from SRB """ if self.connID != None: srb.disconnect(self.connID) log.info("Disconnected from SRB server (id=%d)" %self.connID) def ReactorFinished(self): self.reactorFinished.append(True) def OnExit(self): """ Shut down. """ reactor.stop() try: # print "Shutting down shared app client" self.sharedAppClient.Shutdown() except: traceback.print_exc() # Allow twisted reactor to finish. while len(self.reactorFinished) == 0: reactor.iterate() os._exit(1) def get_M_Total(self, collName, currentPath): """ Method for calculating no. of items and total size in a personal collection. """ selectedPath = currentPath + "/" + collName selectedDir = Node(text="selected_dir", _type="coll", parent=None) selectedData = self.getObjs(selectedDir, "coll", [], selectedPath) self.m_selected_listChildren(self.m_selected_list, selectedData) count = str(len(self.m_selected_list)) if len(self.m_selected_list) > 1: for data in self.m_selected_list: self.m_total = self.m_total + int(data[2]) return count, self.m_total def get_S_Total(self, collName): """ Method for calculating no. of items and total size in a shared collection. """ self.selectedData = Node self.findNode(collName, self.frame.s_treeData) self.s_selected_listChildren(self.s_selected_list, self.selectedData) count = str(len(self.s_selected_list)) if len(self.s_selected_list) > 1: for data in self.s_selected_list: self.s_total = self.s_total + int(data[2]) return count, self.s_total self.s_downlist = [] def On_M_Rename(self, inName, newName, currentPath, _type): """ Method for renaming data/collection """ log.debug("Renaming: " + inName + " to: " + newName) if _type == "coll": ren_status = srb.coll_rename(self.connID, currentPath + "/" + inName, currentPath + "/" + newName) else: ren_status = srb.obj_rename(self.connID, currentPath + "/" + inName, currentPath + "/" + newName) if ren_status < 0: error_msg = srb.get_error(int(ren_status)) dlg = wx.MessageDialog(None, "Cannot rename '" + inName + "'.\nSRB Error: " + error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() def On_M_RefreshList(self, currentPath): """ Method for populating personal ListCtrl with SRB data TODO: what about read/write permissions (or set in SRB groups?) """ srblist = [] # fills in sub-collections # how to get owner of collections <- when sharing, send across AG user name? nums = srb.get_subcolls(self.connID, 0, currentPath) for num in range(nums): s_name = srb.get_subcoll_name(self.connID, num) # need to truncate collPath, collName = os.path.split(s_name) srblist.append((collName, "", 0, "", "", "", "coll", currentPath)) # fills in data objects nums = srb.get_objs_in_coll(self.connID, 0, 16, currentPath) for num in range(nums): s_name = srb.get_obj_metadata(self.connID, 0, num) s_parent = srb.get_obj_metadata(self.connID, 1, num) s_size = srb.get_obj_metadata(self.connID, 2, num) s_type = srb.get_obj_metadata(self.connID, 3, num) s_owner = srb.get_obj_metadata(self.connID, 4, num) s_ts = srb.get_obj_metadata(self.connID, 5, num) s_repidx = srb.get_obj_metadata(self.connID, 6, num) s_resc = srb.get_obj_metadata(self.connID, 7, num) srblist.append((s_name, s_owner, s_size, s_ts, s_repidx, s_resc, s_type, currentPath)) return srblist def On_M_NewColl(self, collName, currentPath): """ New collection TODO: set_user_metadata(int conn_id, char *dataName, char *collName, char *field, char *value) """ log.debug("Creating Collection: " + collName + " in: " + currentPath) collId = srb.mk_collection(self.connID, 0, currentPath, collName) if collId < 0: error_msg = srb.get_error(int(collId)) dlg = wx.MessageDialog(None, "Cannot create '" + collName + "'.\nSRB Error: " + error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def On_M_DeleteColl(self, collName, currentPath): """ Delete collection """ log.debug("Deleting Collection: " + collName + " from: " + currentPath) collId = srb.rm_collection(self.connID, 0, 1, currentPath + "/" + collName) if collId < 0: error_msg = srb.get_error(int(collId)) dlg = wx.MessageDialog(None, "Cannot delete '" + collName + "'.\nSRB Error: " + error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def On_M_DeleteData(self, dataName, currentPath): """ Delete data """ log.debug("Deleting Filename: " + dataName + " from Collection: " + currentPath) dataId = srb.obj_delete(self.connID, dataName, 0, currentPath) if dataId < 0: error_msg = srb.get_error(int(dataId)) dlg = wx.MessageDialog(None, "Cannot delete '" + dataName + "'.\nSRB Error: " + error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() def OnIssueTicket(self, dataName, currentPath, is_coll, beginTime, endTime): """ Issue ticket for a data object. """ log.debug("Issuing ticket for: " + dataName) path, name = os.path.split(dataName) if is_coll == "": status, ticket = srb.issue_ticket(self.connID, name, currentPath, is_coll, beginTime, endTime, -1, "ticketuser@sdsc") else: status, ticket = srb.issue_ticket(self.connID, "", currentPath + "/" + name, is_coll, beginTime, endTime, -1, "ticketuser@sdsc") return status, ticket def OnRemoveTicket(self, dataName, ticket): """ Remove ticket access for a data object. """ log.debug("Removing ticket for: " + dataName) rem_status = srb.remove_ticket(self.connID, ticket) if rem_status < 0: error_msg = srb.get_error(int(rem_status)) dlg = wx.MessageDialog(None, "Cannot remove ticket.\nSRB Error: " + error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return def On_M_UploadDir(self, dirName, currentPath): """ Uploads a directory to personal space. """ temp_dirs = [] temp_files = [] log.debug("Uploading "+ dirName +" to Collection: "+ currentPath) # Use os.walk to get directory + file listing for root, dirs, files in os.walk(dirName): for name in dirs: temp_dirs.append(os.path.join(root, name)) for name in files: temp_files.append(os.path.join(root, name)) # Create base collection path, name = os.path.split(dirName) self.On_M_NewColl(name, currentPath) coll_path = currentPath + "/" + name # Create collection structure for coll in temp_dirs: uploadPath, name = os.path.split(coll_path + coll[len(dirName):]) self.On_M_NewColl(name, uploadPath) # Upload files for file in temp_files: uploadPath, name = os.path.split(coll_path + file[len(dirName):]) self.On_M_UploadFile(file, uploadPath) # here is the Access Grid Toolkit-based code... (substantially modified) def On_M_UploadFile(self, fileName, currentPath): # Plumbing for getting progress callbacks to the dialog. def progressCB(dataName, size, progress, done): if not self.frame.UpdateStatusCancelled(): self.frame.UpdateStatus("Uploading", dataName, size, progress, done) return self.frame.UpdateStatusCancelled() log.debug("Uploading: " + fileName + " to Collection: " + currentPath) path, name = os.path.split(fileName) log.debug("Opening File: " + str(name)) dataId = self.__openData(name, currentPath) if dataId < 0: error_msg = srb.get_error(int(dataId)) dlg = wx.MessageDialog(None, "Cannot upload '" + name + "'.\nSRB Error: " + error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return method = self.UploadLocalFile ul_args = (dataId, fileName, currentPath, progressCB) # Create the thread and pass arguments to run the upload. log.debug("Have args, creating thread") upload_thread = threading.Thread(target = method, args = ul_args) upload_thread.setDaemon(1) upload_thread.start() log.debug("Started thread") # here is the pySRB-based code... (substantially modified) def UploadLocalFile(self, dataId, fileName, currentPath, progressCB=None): """ Upload data to personal share. """ try: self.MAX_WRITE_BUFF_SIZE = 4 * 1024 * 1024 path, name = os.path.split(fileName) dataFile = open(fileName, "r") #open the local file dataLen = os.path.getsize(fileName) #get the local file size startTime = time.time() writeSize = 0 #how much got written , check buffer = dataFile.read(self.MAX_WRITE_BUFF_SIZE) #read in first buffer while len(buffer) > 0 : #while there is data in the buffer writeSize += srb.obj_write(self.connID, dataId, buffer, len(buffer)) #write data, add to counter buffer = dataFile.read(self.MAX_WRITE_BUFF_SIZE)#read next buffer if progressCB: try: cancelled = progressCB(name, dataLen, float(writeSize)/float(dataLen), 0) if cancelled: raise UserCancelled("User cancelled upload") except UserCancelled: raise except: log.exception("Exception in progress callback") if writeSize != dataLen: #if i did not write enough data if writeSize < 0: #check to see for srb errors msg = "SRB Error: " + str(writeSize) if writeSize == -1309 or writeSize == -3927: #these are odd errors, but are caused by existing collections, so give user a hint msg += " TIP: Check to see if data exists already" else: msg = "Data not Written, Wrote: " + str(writeSize) + " Expected: " + str(dataLen) #show any other errors raise msg #raise exception dataFile.close() #close local file self.__closeData(dataId) #close srb data stopTime = time.time() #timer for speed / debug timeTaken = stopTime - startTime #calc time taken speed = (writeSize/1024/1024) / timeTaken log.debug("SRB Upload Speed: " + str(speed) + " M/sec") if progressCB: progressCB(name, dataLen, dataLen, 1) except UserCancelled: log.info("User cancelled upload of %s", fileName) def On_M_DownloadColl(self, collName, currentPath, saveLocation, overwrite): """ Downloads a collection from personal space """ # Retrieves listing of sub-collections + data (if any) log.debug("Downloading: " + collName + " to: " + saveLocation) selectedPath = currentPath + "/" + collName selectedDir = Node(text="selected_dir", _type="coll", parent=None) selectedData = self.getObjs(selectedDir, "coll", [], selectedPath) self.m_download_listChildren(self.m_downlist, selectedData) log.debug("No. of items: " + str(len(self.m_downlist))) # Create base directory orig_len = len(currentPath) temp = [] temp2 = [] if overwrite == False: log.debug("Creating: " + saveLocation) os.makedirs(saveLocation) # Create directory structure for obj in self.m_downlist: if obj[6] == "coll": temp.append(obj[0]) temp.append(obj[7]) for obj in self.m_downlist: if obj[6] == "coll": base, path = os.path.split(os.path.commonprefix(temp)) new_dir = saveLocation + obj[0][len(base)+len(path)+1:] log.debug("New directory: " + new_dir) if overwrite == False: os.makedirs(new_dir) # Download data for obj in self.m_downlist: if obj[6] == "coll": temp2.append(obj[0]) temp2.append(obj[7]) for obj in self.m_downlist: if obj[6] != "coll": base = os.path.commonprefix(temp2) data_path = obj[7] + "/" + obj[0] save_path = saveLocation + data_path[len(base):] self.On_M_DownloadData(obj[0], obj[7], save_path) self.m_downlist = [] # here is the Access Grid Toolkit-based code... (substantially modified) def On_M_DownloadData(self, dataName, currentPath, saveLocation): # Plumbing for getting progress callbacks to the dialog def progressCB(dataName, size, progress, done): if not self.frame.UpdateStatusCancelled(): self.frame.UpdateStatus("Downloading", dataName, size, progress, done) return self.frame.UpdateStatusCancelled() log.debug("Downloading: " + dataName + " to: " + saveLocation) path, name = os.path.split(dataName) dataId = srb.obj_open(self.connID, currentPath, name, 2) log.debug("Data opened ID: " + str(dataId) + " " + name) if dataId < 0: error_msg = srb.get_error(int(dataId)) dlg = wx.MessageDialog(None, "Cannot open '" + name + "'.\nSRB Error: " + error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return method = self.DownloadPersonalData dl_args = (dataId, dataName, currentPath, saveLocation, progressCB) # Create the thread and pass arguments to run the download. log.debug("Have args, creating thread") download_thread = threading.Thread(target = method, args = dl_args) download_thread.setDaemon(1) download_thread.start() log.debug("Started thread") # here is the pySRB-based code... (substantially modified) def DownloadPersonalData(self, dataId, dataName, currentPath, saveLocation, progressCB=None): """ Download data from personal space """ try: MAX_READ_BUFF_SIZE = 4 * 1024 * 1024 path, name = os.path.split(dataName) dataFile = open(saveLocation, "w") # open the local file nums = srb.get_objs_in_coll(self.connID, 0, 16, currentPath) #TEMP: replace '16' with l_FLAG for num in range(nums): obj = srb.get_obj_metadata(self.connID, 0, num) if dataName == obj: dataLen = srb.get_obj_metadata(self.connID, 2, num) # get the SRB data size dataSize = int(dataLen) startTime = time.time() readSize = 0 # how much got read , check buffer = srb.obj_read(self.connID, dataId, MAX_READ_BUFF_SIZE) # read in first buffer while len(buffer) > 0 : # while there is data in the buffer dataFile.write(buffer) # write data, add to counter readSize += len(buffer) buffer = srb.obj_read(self.connID, dataId, MAX_READ_BUFF_SIZE) # read next buffer if progressCB: try: cancelled = progressCB(name, dataSize, float(readSize)/float(dataSize), 0) if cancelled: raise UserCancelled("User cancelled download") except UserCancelled: raise except: log.exception("Exception in progress callback") if readSize != dataSize: # if i did not read enough data if readSize < 0: # check to see for srb errors error_msg = srb.get_error(int(dataId)) msg = "SRB Error: " + error_msg else: msg = "Data not Read, Read: " + str(readSize) + " Expected: " + str(dataSize) # show any other errors raise msg # raise exception dataFile.close() # close local file self.__closeData(dataId) # close srb file if progressCB: progressCB(name, dataSize, dataSize, 1) stopTime = time.time() # timer for speed / debug timeTaken = stopTime - startTime # calc time taken speed = (readSize/1024/1024) / timeTaken log.debug("SRB Download Speed: " + str(speed) + " M/sec") except UserCancelled: log.info("User cancelled download of %s", dataName) def On_S_DownloadColl(self, host, port, collName, currentPath, saveLocation, ticket, overwrite): """ Download collection for ticket users """ # Retrieves listing of shared sub-collections + data (if any) self.selectedData = Node log.debug("Downloading: " + collName + " to: " + saveLocation) selectedPath = currentPath + "/" + collName self.findNode(selectedPath, self.frame.s_treeData) self.s_download_listChildren(self.s_downlist, self.selectedData) log.debug("No. of items: " + str(len(self.s_downlist))) # Create base directory orig_len = len(currentPath) temp = [] temp2 = [] if overwrite == False: log.debug("Creating: " + saveLocation) os.makedirs(saveLocation) for obj in self.s_downlist: if obj[7] == "coll": temp.append(obj[1]) temp.append(obj[6]) # Create directory structure for obj in self.s_downlist: if obj[7] == "coll": base, path = os.path.split(os.path.commonprefix(temp)) new_dir = saveLocation + obj[1][len(base)+len(path)+1:] log.debug("New directory: " + new_dir) if overwrite == False: os.makedirs(new_dir) for obj in self.s_downlist: if obj[7] == "coll": temp2.append(obj[1]) temp2.append(obj[6]) # Download data for obj in self.s_downlist: if obj[7] != "coll": base = os.path.commonprefix(temp2) data_path = obj[6] + "/" + obj[1] save_path = saveLocation + data_path[len(base):] self.On_S_DownloadData(obj[8], obj[9], obj[1], obj[2], obj[6], save_path, obj[5]) self.s_downlist = [] # here is the Access Grid Toolkit-based code... (substantially modified) def On_S_DownloadData(self, host, port, dataName, dataSize, currentPath, saveLocation, ticket): # Plumbing for getting progress callbacks to the dialog def progressCB(dataName, size, progress, done): if not self.frame.UpdateStatusCancelled(): self.frame.UpdateStatus("Downloading", dataName, size, progress, done) return self.frame.UpdateStatusCancelled() log.debug("Downloading with ticket: " + ticket) path, name = os.path.split(dataName) tiConnId = srb.ti_connect(host, port) if tiConnId > 0: log.info("Connected (id=%d)" %tiConnId) log.info(str(host)) log.info(str(port)) else: log.info("SRB server Connection Error") dataId = srb.obj_ti_open(tiConnId, name, currentPath, str(ticket)) log.debug("Data opened ID: " + str(dataId)) if dataId < 0: error_msg = srb.get_error(int(dataId)) dlg = wx.MessageDialog(None, "Cannot open '" + name + "'.\nSRB Error: " + error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return method = self.DownloadSharedData dl_args = (tiConnId, dataId, dataName, dataSize, currentPath, saveLocation, ticket, progressCB) # Create the thread and pass arguments to run the download. log.debug("Have args, creating thread") download_thread = threading.Thread(target = method, args = dl_args) download_thread.setDaemon(1) download_thread.start() log.debug("Started thread") # here is the pySRB-based code... (substantially modified) def DownloadSharedData(self, tiConnID, dataId, dataName, dataSize, currentPath, saveLocation, ticket, progressCB=None): """ Download data for ticket users """ try: MAX_READ_BUFF_SIZE = 4 * 1024 * 1024 path, name = os.path.split(dataName) dataFile = open(saveLocation, "w") # open the local file startTime = time.time() readSize = 0 # how much got read , check buffer = srb.obj_read(tiConnID, dataId, MAX_READ_BUFF_SIZE) # read in first buffer while len(buffer) > 0 : # while there is data in the buffer dataFile.write(buffer) # write data, add to counter readSize += len(buffer) buffer = srb.obj_read(tiConnID, dataId, MAX_READ_BUFF_SIZE) # read next buffer if progressCB: try: cancelled = progressCB(name, dataSize, float(readSize)/float(dataSize), 0) if cancelled: raise UserCancelled("User cancelled download") except UserCancelled: raise except: log.exception("Exception in progress callback") if readSize != int(dataSize): # if i did not read enough data if readSize < 0: # check to see for srb errors error_msg = srb.get_error(int(dataId)) msg = "SRB Error: " + error_msg else: msg = "Data not Read, Read: " + str(readSize) + " Expected: " + str(dataSize) # show any other errors raise msg # raise exception dataFile.close() # close local file self.__ti_closeData(tiConnID, dataId) # close srb file if progressCB: progressCB(name, dataSize, dataSize, 1) stopTime = time.time() # timer for speed / debug timeTaken = stopTime - startTime # calc time taken speed = (readSize/1024/1024) / timeTaken log.debug("SRB Download Speed: " + str(speed) + " M/sec") except UserCancelled: log.info("User cancelled download of %s", dataName) # here is the pySRB-based code... def __dataExists(self, currentPath, dataName): nums = srb.get_objs_in_coll(self.connID, 0, 0, currentPath) for num in range(nums): obj = srb.get_obj_metadata(self.connID, 0, num) if dataName == obj: log.debug("Data " + dataName + " exists") return True return False # here is the pySRB-based code... def __openData(self, dataName, currentPath): _create = True if self.__dataExists(currentPath, dataName): # add dialog here to ask if want to replace log.debug("Opening existing data") dataId = srb.obj_open(self.connID, currentPath, dataName, 2) log.debug("Data opened ID: " + str(dataId) + " " + dataName) else: if _create: #if not there and create log.debug("Creating new data") dataId = self.__createData(dataName, currentPath) else: #data not found log.critical("Could not find the data specified : " + dataName) log.critical("Hint: Set create flag if you want a new database") raise "Hint: Set create flag if you want a new database" if dataId < 0: error_msg = srb.get_error(int(dataId)) raise "SRB Error: " + error_msg return dataId # here is the pySRB-based code... def __createData(self, dataName, currentPath, createSize=0): log.debug("Creating data: " + dataName) (shortname, extension) = os.path.splitext(dataName) id = srb.obj_create(self.connID, 0, dataName, string.upper(extension.split(".")[1]), self.resource, currentPath , "", createSize) return id # here is the pySRB-based code... def __closeData(self, dataID): return srb.obj_close(self.connID, dataID) def __ti_closeData(self, tiConnID, dataID): return srb.obj_close(tiConnID, dataID) """ Methods that operate on the node structure. """ def getColls(self, prevNode, _type, currentPath): """ Retrieves node-based tree data for treectrl collection navigation """ subcolls = [] text = currentPath _type = _type # Checks to see if any sub-collections exist nums = srb.get_subcolls(self.connID, 0, currentPath) if nums > 0: node = Node(text=text, _type=_type, haveChildren=True) else: node = Node(text=text, _type=_type) node.parent = prevNode prevNode.children.append(node) # Get number of sub-collections in current node for num in range(nums): s_name = srb.get_subcoll_name(self.connID, num) subcolls.append(s_name) # Recursively add nodes to tree-data for folder in subcolls: self.getColls(node, "coll", folder) return node def getObjs(self, prevNode, _type, metadata, currentPath): """ Retrieves node-based tree data for personal data share I/O operations """ objs = [] text = currentPath _type = _type metadata = metadata nums = srb.get_subcolls(self.connID, 0, currentPath) + srb.get_objs_in_coll(self.connID, 0, 16, currentPath) if nums > 0: node = Node(text=text, _type=_type, metadata=metadata, haveChildren=True) else: node = Node(text=text, _type=_type, metadata=metadata) node.parent = prevNode prevNode.children.append(node) # Get number of sub-collections in current node nums = srb.get_subcolls(self.connID, 0, currentPath) for num in range(nums): s_name = srb.get_subcoll_name(self.connID, num) objs.append((s_name, "", 0, "", "", "", "coll", currentPath)) # Get number of objects in current node nums = srb.get_objs_in_coll(self.connID, 0, 16, currentPath) for num in range(nums): s_name = srb.get_obj_metadata(self.connID, 0, num) s_parent = srb.get_obj_metadata(self.connID, 1, num) s_size = srb.get_obj_metadata(self.connID, 2, num) s_type = srb.get_obj_metadata(self.connID, 3, num) s_owner = srb.get_obj_metadata(self.connID, 4, num) s_ts = srb.get_obj_metadata(self.connID, 5, num) s_repidx = srb.get_obj_metadata(self.connID, 6, num) s_resc = srb.get_obj_metadata(self.connID, 7, num) objs.append((s_name, s_owner, s_size, s_ts, s_repidx, s_resc, s_type, currentPath)) # Recursively add nodes to tree-data for obj in objs: self.getObjs(node, obj[6], obj, obj[0]) return node def getTickets(self, prevNode, _type, metadata, currentPath): """ Recursively issues tickets and generate node-structure for sharing purposes """ objs = [] text = currentPath _type = _type metadata = metadata nums = srb.get_subcolls(self.connID, 0, currentPath) + srb.get_objs_in_coll(self.connID, 0, 16, currentPath) if nums > 0: node = Node(text=text, _type=_type, metadata=metadata, haveChildren=True) else: node = Node(text=text, _type=_type, metadata=metadata) node.parent = prevNode prevNode.children.append(node) # Get sub-collections in current node and issues a ticket for each nums = srb.get_subcolls(self.connID, 0, currentPath) for num in range(nums): s_name = srb.get_subcoll_name(self.connID, num) status, coll_ticket = self.OnIssueTicket(s_name, currentPath, "D", "", "") if int(status) < 0: error_msg = srb.get_error(int(status)) dlg = wx.MessageDialog(None, "Cannot issue ticket for '" + s_name + "' \nSRB Error: "+ error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() else: objs.append((self.publicName, s_name, 0, str(time.asctime()), "...", coll_ticket, currentPath, "coll", self._host, self._port)) # Get number of objects in current node and issues a ticket for each nums = srb.get_objs_in_coll(self.connID, 0, 16, currentPath) for num in range(nums): s_name = srb.get_obj_metadata(self.connID, 0, num) s_size = srb.get_obj_metadata(self.connID, 2, num) s_type = srb.get_obj_metadata(self.connID, 3, num) status, data_ticket = self.OnIssueTicket(s_name, currentPath, "", "", "") if int(status) < 0: error_msg = srb.get_error(int(status)) dlg = wx.MessageDialog(None, "Cannot issue ticket for '" + s_name + "' \nSRB Error: "+ error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() else: objs.append((self.publicName, s_name, s_size, str(time.asctime()), "...", data_ticket, currentPath, "data", self._host, self._port)) for obj in objs: self.getTickets(node, obj[7], obj, obj[1]) return node def findNode(self, dataName, treeStruct): """ Finds a child node with a specified name in the tree-structure """ for nodeItem in treeStruct.children: if nodeItem.text == dataName: self.selectedData = nodeItem self.findNode(dataName, nodeItem) def removeNode(self, dataName, treeStruct): """ Removes a child node with a specified name from the tree-structure. """ for nodeItem in treeStruct.children: if nodeItem.text == dataName: treeStruct.children.remove(nodeItem) self.removeNode(dataName, nodeItem) def m_selected_listChildren(self, wxParent, nodeParent): """ Fills the 'm_selected_list' list object with the children of the specified node. """ for nodeItem in nodeParent.children: wxItem = self.m_selected_list.append(nodeItem.metadata) self.m_selected_listChildren(wxItem, nodeItem) def s_selected_listChildren(self, wxParent, nodeParent): """ Fills the 's_selected_list' list object with the children of the specified node. """ for nodeItem in nodeParent.children: wxItem = self.s_selected_list.append(nodeItem.metadata) self.s_selected_listChildren(wxItem, nodeItem) def m_download_listChildren(self, wxParent, nodeParent): """ Fills the 'm_downlist' list object with the children of the specified node. """ for nodeItem in nodeParent.children: wxItem = self.m_downlist.append(nodeItem.metadata) self.m_download_listChildren(wxItem, nodeItem) def s_download_listChildren(self, wxParent, nodeParent): """ Fills the 's_downlist' list object with the children of the specified node. """ for nodeItem in nodeParent.children: wxItem = self.s_downlist.append(nodeItem.metadata) self.s_download_listChildren(wxItem, nodeItem) """ Events generated by this user for the AG event channel. """ def OnNewShare(self, add_data): node_object = self.sharedAppClient.GetData("sharenode") if node_object: share_nodes = cPickle.loads(str(node_object)) else: share_nodes = Node(text="root", _type="coll", metadata=None, parent=None) currentUser = Node(text=add_data[0], _type="user", metadata="user") currentUser.parent = share_nodes share_nodes.children.append(currentUser) for user in share_nodes.children: if user.text == add_data[0]: # Existing sharing user if add_data[7] == "coll": # recursively sharing of a collection selectedPath = add_data[6] + "/" + add_data[1] status, coll_ticket = self.OnIssueTicket(add_data[1], add_data[6], "D", "", "") if int(status) < 0: error_msg = srb.get_error(int(status)) dlg = wx.MessageDialog(None, "Cannot issue ticket for '" + add_data[1] + "' \nSRB Error: "+ error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() else: metadata = ((self.publicName, selectedPath, 0, str(time.asctime()), "...", coll_ticket, add_data[6], "coll", self._host, self._port)) selectedDir = Node(text="selected_dir", _type="coll", parent=None) add_node = self.getTickets(selectedDir, "coll", metadata, selectedPath) else: # sharing of data dataPath = add_data[6] + "/" + add_data[1] add_node = Node(text=dataPath, _type=add_data[7], metadata=add_data) add_node.parent = user user.children.append(add_node) else: # New sharing user newUser = Node(text=add_data[0], _type="user", metadata="user") newUser.parent = share_nodes share_nodes.children.append(newUser) if add_data[7] == "coll": # recursively sharing of a collection selectedPath = add_data[6] + "/" + add_data[1] status, coll_ticket = self.OnIssueTicket(add_data[1], add_data[6], "D", "", "") if int(status) < 0: error_msg = srb.get_error(int(status)) dlg = wx.MessageDialog(None, "Cannot issue ticket for '" + add_data[1] + "' \nSRB Error: "+ error_msg, "agSRB Browser", wx.OK|wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() else: metadata = ((self.publicName, selectedPath, 0, str(time.asctime()), "...", coll_ticket, add_data[6], "coll", self._host, self._port)) selectedDir = Node(text="selected_dir", _type="coll", parent=None) add_node = self.getTickets(selectedDir, "coll", metadata, selectedPath) else: # sharing of data dataPath = add_data[6] + "/" + add_data[1] add_node = Node(text=dataPath, _type=add_data[7], metadata=add_data) add_node.parent = newUser newUser.children.append(add_node) self.frame.s_treeData = share_nodes share_node_object = cPickle.dumps(share_nodes) self.sharedAppClient.SendEvent("modifiedData", share_node_object) self.sharedAppClient.SetData("sharenode", share_node_object) def OnRemoveShare(self, remove_data): node_object = self.sharedAppClient.GetData("sharenode") if node_object: remove_nodes = cPickle.loads(str(node_object)) else: remove_nodes = Node(text="root", _type="coll", metadata=None, parent=None) if remove_data[7] == "coll": dataName = remove_data[1] else: dataName = remove_data[6] + "/" + remove_data[1] self.removeNode(dataName, remove_nodes) self.frame.s_treeData = remove_nodes remove_node_object = cPickle.dumps(remove_nodes) self.sharedAppClient.SendEvent("modifiedData", remove_node_object) self.sharedAppClient.SetData("sharenode", remove_node_object) """ Event coming in from the AG event channel. """ def OnRemoteEvent(self, event): """ A peer has shared/removed coll/data Ignore data from this process """ senderId = event.GetSenderId() self.log.debug("RemoteEventCallback") if senderId == self.sharedAppClient.GetPublicId(): self.log.debug("RemoteEventCallback Ignoring from myself") else: log.debug("Getting data...") node_object = self.sharedAppClient.GetData("sharenode") self.frame.s_treeData = cPickle.loads(str(node_object)) log.debug("Got data...") wx.CallAfter(self.frame.Init_S_Tree, self.frame.s_treeData) self.log.debug("done with RemoteEventCallback") #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # Utility functions # #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def Usage(): """ Standard usage information for users. """ print "%s:" % sys.argv[0] print " -v|--venueURL : " print " -a|--applicationURL : " print " -c|--connectionId : " print " -d|--data : " print " -h|--help : print usage" print " -i|--information : " print " --debug : print debugging output" #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # MAIN block # #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if __name__ == "__main__": # Initialization of variables venueURL = None appURL = None venueDataUrl = None connectionId = None name = "agSRB" debug = 0 app = WXGUIApplication() init_args = [] if "--debug" in sys.argv or "-d" in sys.argv: init_args.append("--debug") app.Initialize(name,args=init_args) wx.InitAllImageHandlers() # Here we parse command line options try: opts, args = getopt.getopt(sys.argv[1:], "d:v:a:l:c:ih", ["venueURL=", "applicationURL=", "information=", "connectionId=", "data=", "debug", "help"]) except getopt.GetoptError: Usage() sys.exit(2) for o, a in opts: if o in ("-v", "--venueURL"): venueURL = a elif o in ("-a", "--applicationURL"): appURL = a elif o in ("-c", "--connectionId"): connectionId = a elif o in ("-i", "--information"): print "App Name: %s" % agSRB.appName print "App Description: %s" % agSRB.appDescription print "App Mimetype: %s" % agSRB.appMimetype sys.exit(0) elif o in ("-d", "--data"): venueDataUrl = a elif o in ("--debug",): debug = 1 elif o in ("-h", "--help"): Usage() sys.exit(0) # If we're not passed some url that we can use, bail showing usage if appURL == None and venueURL == None: Usage() sys.exit(0) # If we got a venueURL and not an applicationURL # This is only in the example code. When Applications are integrated # with the Venue Client, the application will only be started with # some applicatoin URL (it will never know about the Venue URL) if appURL == None and venueURL != None: venueProxy = Client.SecureHandle(venueURL).get_proxy() appURL = venueProxy.CreateApplication(agSRB.appName, agSRB.appDescription, agSRB.appMimetype) log.debug("Application URL: %s", appURL) # This is all that really matters! log = app.GetLog() sb = agSRB(appURL, venueURL, name, connectionId=connectionId) reactor.addSystemEventTrigger('after', 'shutdown', sb.ReactorFinished) sb.MainLoop()