[cig-commits] r11886 - in cs/portal/trunk/seismo/SeismoWebPortal: . designs/plone static/pics/th templates/SeismoWebPortal

leif at geodynamics.org leif at geodynamics.org
Thu May 1 13:19:03 PDT 2008


Author: leif
Date: 2008-05-01 13:19:02 -0700 (Thu, 01 May 2008)
New Revision: 11886

Added:
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/TableView.html
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/TableView.py
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/workspace_form.html
Removed:
   cs/portal/trunk/seismo/SeismoWebPortal/static/pics/th/receivers_at_depth.gif
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/eventworkspace_form.html
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemodel_form.html
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_detail.html
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/welcome.html
Modified:
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/__init__.py
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.css
   cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.py
   cs/portal/trunk/seismo/SeismoWebPortal/forms.py
   cs/portal/trunk/seismo/SeismoWebPortal/gui.py
   cs/portal/trunk/seismo/SeismoWebPortal/management.py
   cs/portal/trunk/seismo/SeismoWebPortal/models.py
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/mineosparameters_form.html
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/par_file.txt
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/parameters.pml
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/seismogram_table.html
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_form.html
   cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/stationlist_upload.html
   cs/portal/trunk/seismo/SeismoWebPortal/views.py
Log:
Merged Specfem 'model' with 'parameters'.  Trimmed number of generated
objects.  Weak attempt at TableView abstraction.  Expermentation with
'actionMenu'.  Cleaning.


Added: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/TableView.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/TableView.html	                        (rev 0)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/TableView.html	2008-05-01 20:19:02 UTC (rev 11886)
@@ -0,0 +1,45 @@
+
+<table rules=cols class=cool>
+    #if $tableView.caption
+        <caption>$tableView.caption</caption>
+    #end if
+
+    <thead>
+    #if $len($tableView.columnGroups) > 1
+        <tr>
+            #for $columnGroup in $tableView.columnGroups
+                #set $colspan = len($columnGroup.columns)
+                <th valign=bottom colspan=$colspan>
+                    #if $columnGroup.title
+                        $columnGroup.title
+                    #end if
+                </th>
+            #end for
+        </tr>
+    #end if
+    <tr>
+        #for $columnGroup in $tableView.columnGroups
+            #for $column in $columnGroup.columns
+                <th valign=bottom>$column.title</th>
+            #end for
+        #end for
+    </tr>
+    </thead>
+
+    <tbody>
+    #for $i, $row in $enumerate($tableView.rows)
+        #set $klass = $i % 2 and 'even' or 'odd'
+        <tr class="$klass">
+            #for $data in $row
+                #if $isinstance($data, bool)
+                    #set $yesNo = $data and 'yes' or 'no'
+                    <td><img src="$root/pics/icon-${yesNo}.gif" alt="$data"></td>
+                #else
+                    <td>$data</td>
+                #endif
+            #end for
+        </tr>
+    #end for
+    </tbody>
+
+</table>

Added: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/TableView.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/TableView.py	                        (rev 0)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/TableView.py	2008-05-01 20:19:02 UTC (rev 11886)
@@ -0,0 +1,192 @@
+#!/usr/bin/env python
+
+
+
+
+##################################################
+## DEPENDENCIES
+import sys
+import os
+import os.path
+from os.path import getmtime, exists
+import time
+import types
+import __builtin__
+from Cheetah.Version import MinCompatibleVersion as RequiredCheetahVersion
+from Cheetah.Version import MinCompatibleVersionTuple as RequiredCheetahVersionTuple
+from Cheetah.Template import Template
+from Cheetah.DummyTransaction import DummyTransaction
+from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList
+from Cheetah.CacheRegion import CacheRegion
+import Cheetah.Filters as Filters
+import Cheetah.ErrorCatchers as ErrorCatchers
+
+##################################################
+## MODULE CONSTANTS
+try:
+    True, False
+except NameError:
+    True, False = (1==1), (1==0)
+VFFSL=valueFromFrameOrSearchList
+VFSL=valueFromSearchList
+VFN=valueForName
+currentTime=time.time
+__CHEETAH_version__ = '2.0rc7'
+__CHEETAH_versionTuple__ = (2, 0, 0, 'candidate', 7)
+__CHEETAH_genTime__ = 1209516150.7002649
+__CHEETAH_genTimestamp__ = 'Tue Apr 29 17:42:30 2008'
+__CHEETAH_src__ = 'TableView.html'
+__CHEETAH_srcLastModified__ = 'Tue Apr 29 17:42:28 2008'
+__CHEETAH_docstring__ = 'Autogenerated by CHEETAH: The Python-Powered Template Engine'
+
+if __CHEETAH_versionTuple__ < RequiredCheetahVersionTuple:
+    raise AssertionError(
+      'This template was compiled with Cheetah version'
+      ' %s. Templates compiled before version %s must be recompiled.'%(
+         __CHEETAH_version__, RequiredCheetahVersion))
+
+##################################################
+## CLASSES
+
+class TableView(Template):
+
+    ##################################################
+    ## CHEETAH GENERATED METHODS
+
+
+    def __init__(self, *args, **KWs):
+
+        Template.__init__(self, *args, **KWs)
+        if not self._CHEETAH__instanceInitialized:
+            cheetahKWArgs = {}
+            allowedKWs = 'searchList namespaces filter filtersLib errorCatcher'.split()
+            for k,v in KWs.items():
+                if k in allowedKWs: cheetahKWArgs[k] = v
+            self._initCheetahInstance(**cheetahKWArgs)
+        
+
+    def respond(self, trans=None):
+
+
+
+        ## CHEETAH: main method generated for this template
+        if (not trans and not self._CHEETAH__isBuffering and not callable(self.transaction)):
+            trans = self.transaction # is None unless self.awake() was called
+        if not trans:
+            trans = DummyTransaction()
+            _dummyTrans = True
+        else: _dummyTrans = False
+        write = trans.response().write
+        SL = self._CHEETAH__searchList
+        _filter = self._CHEETAH__currentFilter
+        
+        ########################################
+        ## START - generated method body
+        
+        write('\n<table rules=cols class=cool>\n')
+        if VFFSL(SL,"tableView.caption",True): # generated from line 3, col 5
+            write('        <caption>')
+            _v = VFFSL(SL,"tableView.caption",True) # '$tableView.caption' on line 4, col 18
+            if _v is not None: write(_filter(_v, rawExpr='$tableView.caption')) # from line 4, col 18.
+            write('</caption>\n')
+        write('\n    <thead>\n')
+        if VFFSL(SL,"len",False)(VFFSL(SL,"tableView.columnGroups",True)) > 1: # generated from line 8, col 5
+            write('        <tr>\n')
+            for columnGroup in VFFSL(SL,"tableView.columnGroups",True): # generated from line 10, col 13
+                colspan = len(VFFSL(SL,"columnGroup.columns",True))
+                write('                <th valign=bottom colspan=')
+                _v = VFFSL(SL,"colspan",True) # '$colspan' on line 12, col 43
+                if _v is not None: write(_filter(_v, rawExpr='$colspan')) # from line 12, col 43.
+                write('>\n')
+                if VFFSL(SL,"columnGroup.title",True): # generated from line 13, col 21
+                    write('                        ')
+                    _v = VFFSL(SL,"columnGroup.title",True) # '$columnGroup.title' on line 14, col 25
+                    if _v is not None: write(_filter(_v, rawExpr='$columnGroup.title')) # from line 14, col 25.
+                    write('\n')
+                write('                </th>\n')
+            write('        </tr>\n')
+        write('    <tr>\n')
+        for columnGroup in VFFSL(SL,"tableView.columnGroups",True): # generated from line 21, col 9
+            for column in VFFSL(SL,"columnGroup.columns",True): # generated from line 22, col 13
+                write('                <th valign=bottom>')
+                _v = VFFSL(SL,"column.title",True) # '$column.title' on line 23, col 35
+                if _v is not None: write(_filter(_v, rawExpr='$column.title')) # from line 23, col 35.
+                write('</th>\n')
+        write('''    </tr>
+    </thead>
+
+    <tbody>
+''')
+        for i, row in VFFSL(SL,"enumerate",False)(VFFSL(SL,"tableView.rows",True)): # generated from line 30, col 5
+            klass = VFFSL(SL,"i",True) % 2 and 'even' or 'odd'
+            write('        <tr class="')
+            _v = VFFSL(SL,"klass",True) # '$klass' on line 32, col 20
+            if _v is not None: write(_filter(_v, rawExpr='$klass')) # from line 32, col 20.
+            write('">\n')
+            for data in VFFSL(SL,"row",True): # generated from line 33, col 13
+                if VFFSL(SL,"isinstance",False)(VFFSL(SL,"data",True), bool): # generated from line 34, col 17
+                    yesNo = VFFSL(SL,"data",True) and 'yes' or 'no'
+                    write('                    <td><img src="')
+                    _v = VFFSL(SL,"root",True) # '$root' on line 36, col 35
+                    if _v is not None: write(_filter(_v, rawExpr='$root')) # from line 36, col 35.
+                    write('/pics/icon-')
+                    _v = VFFSL(SL,"yesNo",True) # '${yesNo}' on line 36, col 51
+                    if _v is not None: write(_filter(_v, rawExpr='${yesNo}')) # from line 36, col 51.
+                    write('.gif" alt="')
+                    _v = VFFSL(SL,"data",True) # '$data' on line 36, col 70
+                    if _v is not None: write(_filter(_v, rawExpr='$data')) # from line 36, col 70.
+                    write('"></td>\n')
+                else: # generated from line 37, col 17
+                    write('                    <td>')
+                    _v = VFFSL(SL,"data",True) # '$data' on line 38, col 25
+                    if _v is not None: write(_filter(_v, rawExpr='$data')) # from line 38, col 25.
+                    write('</td>\n')
+            write('        </tr>\n')
+        write('''    </tbody>
+
+</table>
+''')
+        
+        ########################################
+        ## END - generated method body
+        
+        return _dummyTrans and trans.response().getvalue() or ""
+        
+    ##################################################
+    ## CHEETAH GENERATED ATTRIBUTES
+
+
+    _CHEETAH__instanceInitialized = False
+
+    _CHEETAH_version = __CHEETAH_version__
+
+    _CHEETAH_versionTuple = __CHEETAH_versionTuple__
+
+    _CHEETAH_genTime = __CHEETAH_genTime__
+
+    _CHEETAH_genTimestamp = __CHEETAH_genTimestamp__
+
+    _CHEETAH_src = __CHEETAH_src__
+
+    _CHEETAH_srcLastModified = __CHEETAH_srcLastModified__
+
+    _mainCheetahMethod_for_TableView= 'respond'
+
+## END CLASS DEFINITION
+
+if not hasattr(TableView, '_initCheetahAttributes'):
+    templateAPIClass = getattr(TableView, '_CHEETAH_templateClass', Template)
+    templateAPIClass._addCheetahPlumbingCodeToClass(TableView)
+
+
+# CHEETAH was developed by Tavis Rudd and Mike Orr
+# with code, advice and input from many other volunteers.
+# For more information visit http://www.CheetahTemplate.org/
+
+##################################################
+## if run from command line:
+if __name__ == '__main__':
+    from Cheetah.TemplateCmdLineIface import CmdLineIface
+    CmdLineIface(templateObj=TableView()).run()
+
+

Modified: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/__init__.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/__init__.py	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/__init__.py	2008-05-01 20:19:02 UTC (rev 11886)
@@ -2,6 +2,7 @@
 
 from Desktop import Desktop
 from Document import Document
+from TableView import TableView
 from style import style
 from script import script
 
@@ -69,12 +70,22 @@
     def newDesktopTemplate(self):
         return Desktop()
 
+    def newTableViewTemplate(self):
+        return TableView()
+
     def renderBreadcrumbs(self, breadcrumbs):
         breadcrumbSeparator = ' <span class="breadcrumbSeparator">&rarr;</span> '
         lastFormat = '<span dir="ltr"><span>%s</span></span>'
         links = ['<a href="%s">%s</a>' % (url, title) for url, title in breadcrumbs[:-1]]
         return breadcrumbSeparator.join(links + [lastFormat % breadcrumbs[-1][1]])
 
+    def renderTableView(self, tableView):
+        template = self.newTableViewTemplate()
+        template.design = self
+        template.root = config.root
+        template.tableView = tableView
+        return template
+
     def serveFile(self, path):
         from django.http import HttpResponse, Http404
         if not path: raise Http404

Modified: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.css
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.css	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.css	2008-05-01 20:19:02 UTC (rev 11886)
@@ -638,7 +638,7 @@
 
 /* stations */
 
-#stations {
+#stationsIllustration {
     width: 576px;
 }
 
@@ -673,6 +673,37 @@
 
 
 
+/* plone overrides */
+
+.cool a {
+    color: black;
+    text-decoration: none;
+    border-bottom: none !important; /* override documentContent */
+}
+
+.cool .actionMenu.activated .actionMenuHeader,
+.cool .actionMenu.activated .actionMenuHeader a {
+    background-color: black;
+    color: white;
+}
+
+.cool .actionMenu .actionMenuContent {
+    position: absolute;
+    right: auto;
+}
+
+.cool .actionMenu .actionMenuContent ul {
+    background-color: white;
+    border: 1px solid black;
+}
+
+.cool .actionMenu .actionMenuContent li a:hover {
+    background-color: black;
+    color: white;
+}
+
+
+
 /* plone extensions */
 
 @media screen {

Modified: cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.py	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/designs/plone/style.py	2008-05-01 20:19:02 UTC (rev 11886)
@@ -33,10 +33,10 @@
 currentTime=time.time
 __CHEETAH_version__ = '2.0rc7'
 __CHEETAH_versionTuple__ = (2, 0, 0, 'candidate', 7)
-__CHEETAH_genTime__ = 1208912201.098881
-__CHEETAH_genTimestamp__ = 'Tue Apr 22 17:56:41 2008'
+__CHEETAH_genTime__ = 1209524864.618077
+__CHEETAH_genTimestamp__ = 'Tue Apr 29 20:07:44 2008'
 __CHEETAH_src__ = 'style.css'
-__CHEETAH_srcLastModified__ = 'Tue Apr 22 17:56:36 2008'
+__CHEETAH_srcLastModified__ = 'Tue Apr 29 20:07:42 2008'
 __CHEETAH_docstring__ = 'Autogenerated by CHEETAH: The Python-Powered Template Engine'
 
 if __CHEETAH_versionTuple__ < RequiredCheetahVersionTuple:
@@ -729,7 +729,7 @@
 
 /* stations */
 
-#stations {
+#stationsIllustration {
     width: 576px;
 }
 
@@ -764,6 +764,37 @@
 
 
 
+/* plone overrides */
+
+.cool a {
+    color: black;
+    text-decoration: none;
+    border-bottom: none !important; /* override documentContent */
+}
+
+.cool .actionMenu.activated .actionMenuHeader,
+.cool .actionMenu.activated .actionMenuHeader a {
+    background-color: black;
+    color: white;
+}
+
+.cool .actionMenu .actionMenuContent {
+    position: absolute;
+    right: auto;
+}
+
+.cool .actionMenu .actionMenuContent ul {
+    background-color: white;
+    border: 1px solid black;
+}
+
+.cool .actionMenu .actionMenuContent li a:hover {
+    background-color: black;
+    color: white;
+}
+
+
+
 /* plone extensions */
 
 @media screen {

Modified: cs/portal/trunk/seismo/SeismoWebPortal/forms.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/forms.py	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/forms.py	2008-05-01 20:19:02 UTC (rev 11886)
@@ -5,12 +5,10 @@
 from django.core import validators
 from django.template import loader
 from django.template import Context
-from models import Location, CMTSolution
-from models import Specfem3DGlobeMesh, Event, DataSource, Region, Source
-from models import UserInfo, Invite, Folder
-from models import EventWorkspace, StationList
+
 import cmt
 import config
+import models
 
 
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -22,7 +20,14 @@
     
     def __init__(self, nchunks):
         self.nchunks = nchunks
+        
         # replace generic fields with custom fields
+
+        del self['location']
+        self.fields.extend([forms.FloatField('center_latitude', max_digits=19, decimal_places=10, is_required=True),
+                            forms.FloatField('center_longitude', max_digits=19, decimal_places=10, is_required=True),
+                            ])
+        
         if nchunks < 3:
             return
         del self['nproc_xi']
@@ -44,6 +49,8 @@
             forms.SelectField(field_name='nex_c', choices=NEX_C_CHOICES, is_required=True),
             ])
 
+        return
+
     def save(self, new_data):
         if self.nchunks < 3:
             return
@@ -59,30 +66,30 @@
         return
 
 
-class MeshAddManipulator(Specfem3DGlobeMesh.AddManipulator, MeshManipulator):
+class MeshAddManipulator(models.Specfem3DGlobeMesh.AddManipulator, MeshManipulator):
     def __init__(self, nchunks):
-        Specfem3DGlobeMesh.AddManipulator.__init__(self, follow = { 'fsNode': False })
+        models.Specfem3DGlobeMesh.AddManipulator.__init__(self, follow = { 'fsNode': False })
         MeshManipulator.__init__(self, nchunks)
 
     def save(self, new_data):
         MeshManipulator.save(self, new_data)
-        return Specfem3DGlobeMesh.AddManipulator.save(self, new_data)
+        return models.Specfem3DGlobeMesh.AddManipulator.save(self, new_data)
 
 
-class MeshChangeManipulator(Specfem3DGlobeMesh.ChangeManipulator, MeshManipulator):
+class MeshChangeManipulator(models.Specfem3DGlobeMesh.ChangeManipulator, MeshManipulator):
     def __init__(self, nchunks, object_id):
-        Specfem3DGlobeMesh.ChangeManipulator.__init__(self, object_id, follow = { 'fsNode': False })
+        models.Specfem3DGlobeMesh.ChangeManipulator.__init__(self, object_id, follow = { 'fsNode': False })
         MeshManipulator.__init__(self, nchunks)
 
     def flatten_data(self):
-        new_data = Specfem3DGlobeMesh.ChangeManipulator.flatten_data(self)
+        new_data = models.Specfem3DGlobeMesh.ChangeManipulator.flatten_data(self)
         new_data['nproc'] = self.original_object.nproc_xi
         new_data['nex_c'] = self.original_object.nex_xi_c
         return new_data
 
     def save(self, new_data):
         MeshManipulator.save(self, new_data)
-        return Specfem3DGlobeMesh.ChangeManipulator.save(self, new_data)
+        return models.Specfem3DGlobeMesh.ChangeManipulator.save(self, new_data)
 
 
 class RunStatusManipulator(forms.Manipulator):
@@ -120,8 +127,8 @@
 
 def isValidInvitationCode(field_data, all_data):
     try:
-        invite = Invite.objects.get(code = field_data)
-    except Invite.DoesNotExist:
+        invite = models.Invite.objects.get(code = field_data)
+    except models.Invite.DoesNotExist:
         raise validators.ValidationError, "Invalid invitation code."
     if invite.hasExpired():
         raise validators.ValidationError, "This invitation code has expired."
@@ -168,7 +175,7 @@
         invite = None
         inviteCode = new_data['invite']
         if inviteCode:
-            invite = Invite.objects.get(code = inviteCode)
+            invite = models.Invite.objects.get(code = inviteCode)
             approved = True
         
         user, created = User.objects.get_or_create(
@@ -183,7 +190,7 @@
         # Now we're committed to creating the user account.
         user.set_password(new_data['password1'])
         user.save()
-        UserInfo.objects.create(
+        models.UserInfo.objects.create(
             user        = user,
             institution = new_data['institution'],
             address1    = new_data['address1'],
@@ -192,8 +199,8 @@
             phone       = new_data['phone'],
             invite      = invite,
             approved    = approved,
-            home        = Folder.newFolder("Home", user),
-            trash       = Folder.newFolder("Trash", user),
+            home        = models.Folder.newFolder("Home", user),
+            trash       = models.Folder.newFolder("Trash", user),
             )
         
         # Log-in the new user.
@@ -238,7 +245,7 @@
         new_data.update(self.user.__dict__)
         try:
             userInfo = self.user.userinfo
-        except UserInfo.DoesNotExist:
+        except models.UserInfo.DoesNotExist:
             pass
         else:
             new_data.update(userInfo.__dict__)
@@ -249,11 +256,11 @@
         user = self.user
         try:
             userInfo = user.userinfo
-        except UserInfo.DoesNotExist:
-            userInfo = UserInfo()
+        except models.UserInfo.DoesNotExist:
+            userInfo = models.UserInfo()
             userInfo.user = user
-            userInfo.home = Folder.newFolder("Home", user)
-            userInfo.trash = Folder.newFolder("Trash", user)
+            userInfo.home = models.Folder.newFolder("Home", user)
+            userInfo.trash = models.Folder.newFolder("Trash", user)
         # Save the new user.
         user.first_name      = new_data['first_name']
         user.last_name       = new_data['last_name']
@@ -317,7 +324,7 @@
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-class SingleSourceEventAddManipulator(CMTSolution.AddManipulator):
+class SingleSourceEventAddManipulator(models.CMTSolution.AddManipulator):
 
     def __init__(self):
         super(SingleSourceEventAddManipulator, self).__init__()
@@ -332,10 +339,10 @@
     def save(self, new_data, user):
         
         # Get/create the location.
-        location = Location.getLocation(new_data['latitude'], new_data['longitude'])
+        location = models.Location.getLocation(new_data['latitude'], new_data['longitude'])
 
         # Get/create the CMTSolution.
-        cmtSolution = CMTSolution.getCMTSolution(
+        cmtSolution = models.CMTSolution.getCMTSolution(
             halfDuration = new_data['halfDuration'],
             location = location,
             depth = new_data['depth'],
@@ -354,10 +361,10 @@
         source = self.createSource(new_data, event, cmtSolution)
 
         # Create a new workspace for this event.
-        workspace = EventWorkspace(event = event,
-                                   stations = StationList.favorite(),
-                                   zero_half_duration = True,
-                                   )
+        workspace = models.Workspace(event = event,
+                                     stations = models.StationList.favorite(),
+                                     zero_half_duration = True,
+                                     )
         workspace.save()
         
         return workspace
@@ -366,11 +373,11 @@
         from datetime import datetime
 
         # Create the DataSource and Region.
-        dataSource, created = DataSource.objects.get_or_create(name = "ZZZ")
-        region, created = Region.objects.get_or_create(name = "ZZZZ")
+        dataSource, created = models.DataSource.objects.get_or_create(name = "ZZZ")
+        region, created = models.Region.objects.get_or_create(name = "ZZZZ")
 
         # Create the new Event.
-        event = Event.objects.create(
+        event = models.Event.objects.create(
             dataSource = dataSource,
             when = datetime.min,
             microsecond = 0,
@@ -384,7 +391,7 @@
         return event
 
     def createSource(self, new_data, event, cmtSolution):
-        source = Source.objects.create(
+        source = models.Source.objects.create(
             event = event,
             eventName = "000000Z",
             timeShift = 0.0,
@@ -418,14 +425,14 @@
         from datetime import datetime
 
         # Create the DataSource and Region.
-        dataSource, created = DataSource.objects.get_or_create(name = new_data['dataSource'])
-        region, created = Region.objects.get_or_create(name = new_data['region'])
+        dataSource, created = models.DataSource.objects.get_or_create(name = new_data['dataSource'])
+        region, created = models.Region.objects.get_or_create(name = new_data['region'])
 
         # get/create the location
-        sourceLocation = Location.getLocation(new_data['sourceLatitude'], new_data['sourceLongitude'])
+        sourceLocation = models.Location.getLocation(new_data['sourceLatitude'], new_data['sourceLongitude'])
 
         # Create the new Event.
-        event = Event.objects.create(
+        event = models.Event.objects.create(
             dataSource = dataSource,
             when = datetime.combine(new_data['when_date'], new_data['when_time']),
             microsecond = new_data['microsecond'],
@@ -439,7 +446,7 @@
         return event
 
     def createSource(self, new_data, event, cmtSolution):
-        source = Source.objects.create(
+        source = models.Source.objects.create(
             event = event,
             eventName = new_data['eventName'],
             timeShift = new_data['timeShift'],
@@ -454,7 +461,7 @@
         return workspace
 
 
-class SingleSourceEventChangeManipulator(CMTSolution.ChangeManipulator):
+class SingleSourceEventChangeManipulator(models.CMTSolution.ChangeManipulator):
 
     def __init__(self, workspace):
         self.workspace = workspace
@@ -480,7 +487,7 @@
         source = self.workspace.event.singleSource
 
         # Get/create the location.
-        location = Location.getLocation(new_data['latitude'], new_data['longitude'])
+        location = models.Location.getLocation(new_data['latitude'], new_data['longitude'])
         del new_data['latitude']
         del new_data['longitude']
        
@@ -489,7 +496,7 @@
         for k in new_data.iterkeys():
             kwds[k] = new_data[k] # convert from MultiValueDict to dict
         kwds['location'] = location
-        source.cmtSolution = CMTSolution.getCMTSolution(**kwds)
+        source.cmtSolution = models.CMTSolution.getCMTSolution(**kwds)
 
         # Save the Source.
         source.save()
@@ -536,14 +543,14 @@
                                  cmtSolution.minute,
                                  cmtSolution.second)
         
-        dataSource, created = DataSource.objects.get_or_create(name = cmtSolution.dataSource)
-        region, created = Region.objects.get_or_create(name = cmtSolution.regionName)
+        dataSource, created = models.DataSource.objects.get_or_create(name = cmtSolution.dataSource)
+        region, created = models.Region.objects.get_or_create(name = cmtSolution.regionName)
 
-        event = Event.objects.create(
+        event = models.Event.objects.create(
             dataSource        = dataSource,
             when              = when,
             microsecond       = cmtSolution.microsecond,
-            sourceLocation    = Location.getLocation(cmtSolution.sourceLatitude, cmtSolution.sourceLongitude),
+            sourceLocation    = models.Location.getLocation(cmtSolution.sourceLatitude, cmtSolution.sourceLongitude),
             sourceDepth       = cmtSolution.sourceDepth,
             sourceMB          = cmtSolution.sourceMB,
             sourceMs          = cmtSolution.sourceMs,
@@ -552,16 +559,16 @@
 
         # Add each source.
         for cmtSolution in cmtSolutionList:
-            Source.saveSource(event, cmtSolution)
+            models.Source.saveSource(event, cmtSolution)
 
         # Create a new workspace for this event.
         zero_half_duration = False
         if event.singleSource:
             zero_half_duration = True
-        workspace = EventWorkspace(event = event,
-                                   stations = StationList.favorite(),
-                                   zero_half_duration = zero_half_duration,
-                                   )
+        workspace = models.Workspace(event = event,
+                                     stations = models.StationList.favorite(),
+                                     zero_half_duration = zero_half_duration,
+                                     )
         workspace.save()
         
         workspace.fsNode.name = filename

Modified: cs/portal/trunk/seismo/SeismoWebPortal/gui.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/gui.py	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/gui.py	2008-05-01 20:19:02 UTC (rev 11886)
@@ -92,6 +92,37 @@
         self.html = html
 
 
+class TableView(object):
+    class ColumnGroup(object):
+        def __init__(self, title=None):
+            self.title = title
+            self.columns = []
+        def addColumn(self, *args, **kwds):
+            column = self.Column(*args, **kwds)
+            self.columns.append(column)
+            return column
+        class Column(object):
+            def __init__(self, attr, title=None):
+                self.attr = attr
+                if title is None:
+                    title = attr
+                self.title = title
+    def __init__(self, caption=None):
+        self.caption = caption
+        self.columnGroups = []
+        self.rows = []
+    def addColumnGroup(self, *args, **kwds):
+        columnGroup = self.ColumnGroup(*args, **kwds)
+        self.columnGroups.append(columnGroup)
+        return columnGroup
+    def addRow(self, obj):
+        row = []
+        for cg in self.columnGroups:
+            for col in cg.columns:
+                row.append(getattr(obj, col.attr))
+        self.rows.append(row)
+
+
 #------------------------------------------------------------------------
 # virtual file system
 
@@ -255,6 +286,10 @@
         return bc
 
 
+#------------------------------------------------------------------------
+# app windows
+
+
 class FileBrowser(TreeBrowser):
     def iconURL(self):
         return "%s/icons/folder.gif" % config.root
@@ -265,11 +300,8 @@
         return "%s/icons/eventfinder.gif" % config.root
 
 
-class EventViewer(TreeBrowser):
+class Workspace(TreeBrowser):
     
     def __init__(self, url, title, memento, root, rootURL, workspace):
         Navigator.__init__(self, url, title, memento, root, rootURL)
         self.workspace = workspace
-
-    def icon(self):
-        return self.workspace.icon()

Modified: cs/portal/trunk/seismo/SeismoWebPortal/management.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/management.py	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/management.py	2008-05-01 20:19:02 UTC (rev 11886)
@@ -5,22 +5,6 @@
 import os
 
 
-def createSpecfem3DGlobeModels():
-    for type, name in models.MODEL_TYPES:
-        model = models.Specfem3DGlobeModel.objects.create(
-            type = type,
-            oceans = True,
-            gravity = True,
-            attenuation = True,
-            topography = True,
-            rotation = True,
-            ellipticity = True)
-        model.fsNode.name = name
-        model.fsNode.save()
-        print "Adding Specfem3DGlobeModel '%s'" % model
-    return
-
-
 def createSpecfem3DGlobeMeshes():
     
     m = {}
@@ -44,8 +28,11 @@
         # ...rejecting those below Nex = 160 (27s)...
         if nex < 160:
             break
-        # ...selecting those variants that use the fewest processors...
-        nex_c, nproc = m[nex][0]
+        # ...rejecting those above Nex = 240 (18s)...
+        if nex > 240:
+            continue
+        # ...selecting those variants that use the greatest # of processors...
+        nex_c, nproc = m[nex][-1]
         mesh = models.Specfem3DGlobeMesh(
             nchunks = 6,
             nproc_xi = nproc,
@@ -90,34 +77,6 @@
     return
 
 
-def createSpecfem3DGlobeParameters():
-    # Create a parameter set for desired mesh/model combinations.
-    if False:
-        meshList = models.Specfem3DGlobeMesh.objects.all()
-    else:
-        defaultMesh = models.Specfem3DGlobeMesh.objects.get(
-            nchunks = 6,
-            nproc_xi = 5,
-            nproc_eta = 5,
-            nex_xi_c = 6,
-            nex_eta_c = 6,
-            fsNode__owner__isnull = True,
-            )
-        assert defaultMesh.nex_xi() == defaultMesh.nex_xi() == 240
-        meshList = [defaultMesh]
-    for model in models.Specfem3DGlobeModel.objects.all():
-        for mesh in meshList:
-            parameters = models.Specfem3DGlobeParameters.objects.create(
-                model = model,
-                mesh = mesh,
-                receivers_can_be_buried = True,
-                )
-            parameters.fsNode.name = str(model) + ' ' + str(mesh)
-            parameters.fsNode.save()
-            print "Adding Specfem3DGlobeParameters '%s'" % parameters
-    return
-
-
 def createMineosModeCatalogs():
     catalog = models.MineosModeCatalog.objects.create()
     catalog.fsNode.name = "Built-in"
@@ -230,15 +189,8 @@
 def createDefaults(app, created_models):
     
     # Specfem 3D Globe
-    flag = models.Specfem3DGlobeParameters in created_models
-    if models.Specfem3DGlobeModel in created_models:
-        createSpecfem3DGlobeModels()
-        flag = True
     if models.Specfem3DGlobeMesh in created_models:
         createSpecfem3DGlobeMeshes()
-        flag = True
-    if flag:
-        createSpecfem3DGlobeParameters()
 
     # Mineos
     flag = models.MineosParameters in created_models

Modified: cs/portal/trunk/seismo/SeismoWebPortal/models.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/models.py	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/models.py	2008-05-01 20:19:02 UTC (rev 11886)
@@ -320,6 +320,11 @@
     def icon(self):
         return '<img src="%s/icons/stationlist.gif">' % config.root
 
+    def name(self): return self.fsNode.name
+
+    def size(self):
+        return self.station_set.count()
+
     def save(self):
         super(StationList, self).save()
         if self.fsNode is None:
@@ -338,7 +343,14 @@
         # NYI
         return StationList.objects.all()[0]
 
+    @classmethod
+    def buildTableView(cls, tv):
+        cg = tv.addColumnGroup()
+        cg.addColumn('name')
+        cg.addColumn('size', "number of stations")
+        return
 
+
 class Station(models.Model):
     
     stationList = models.ForeignKey(StationList)
@@ -474,55 +486,35 @@
         m = cls.Dummy(**snapshot)
         return m
 
-    class Admin:
-        pass
+    @classmethod
+    def buildTableView(cls, tv):
+        cg = tv.addColumnGroup()
+        cg.addColumn('nchunks')
+        
+        cg = tv.addColumnGroup("nproc")
+        cg.addColumn('nproc_eta', "&eta;")
+        cg.addColumn('nproc_xi', "&xi;")
+        
+        cg = tv.addColumnGroup("nex")
+        cg.addColumn('nex_eta', "&eta;")
+        cg.addColumn('nex_xi', "&xi;")
 
+        cg = tv.addColumnGroup("angular width")
+        cg.addColumn('angular_width_eta', "&eta;")
+        cg.addColumn('angular_width_xi', "&xi;")
 
+        cg = tv.addColumnGroup("center")
+        cg.addColumn('center_latitude', "latitude")
+        cg.addColumn('center_longitude', "longitude")
+        
+        cg = tv.addColumnGroup()
+        cg.addColumn('gamma_rotation_azimuth', "gamma<br>rotation<br>azimuth")
 
-class Specfem3DGlobeModel(models.Model):
-    
-    fsNode = models.ForeignKey(FSNode, null=True)
-    
-    type = models.IntegerField(choices=MODEL_TYPES, core=True, default=False)
-    oceans = models.BooleanField(core=True, default=False)
-    gravity = models.BooleanField(core=True, default=False)
-    attenuation = models.BooleanField(core=True, default=False)
-    topography = models.BooleanField(core=True, default=False)
-    rotation = models.BooleanField(core=True, default=False)
-    ellipticity = models.BooleanField(core=True, default=False)
-
-    def __str__(self): return self.fsNode.name
-
-    def icon(self):
-        return '<img src="%s/icons/model.gif">' % config.root
-
-    def save(self):
-        super(Specfem3DGlobeModel, self).save()
-        if self.fsNode is None:
-            self.fsNode = FSNode.newNode(self)
-            super(Specfem3DGlobeModel, self).save()
-        else:
-            self.fsNode.save() # touch
+        cg = tv.addColumnGroup()
+        cg.addColumn('shortestPeriod', "shortest<br>period<br>(s)")
+        
         return
 
-    def get_type_id(self):
-        return model_types[self.type-1][0]
-
-    def snapshot(self):
-        return dict(
-            type = self.type,
-            oceans = self.oceans,
-            gravity = self.gravity,
-            attenuation = self.attenuation,
-            topography = self.topography,
-            rotation = self.rotation,
-            ellipticity = self.ellipticity,
-            )
-
-    @classmethod
-    def objectFromSnapshot(cls, snapshot):
-        return cls(**snapshot)
-
     class Admin:
         pass
 
@@ -533,9 +525,16 @@
     fsNode = models.ForeignKey(FSNode, null=True)
     
     mesh = models.ForeignKey(Specfem3DGlobeMesh, limit_choices_to = userFSChoices)
-    model = models.ForeignKey(Specfem3DGlobeModel, limit_choices_to = userFSChoices)
-    receivers_can_be_buried = models.BooleanField(core=True)
 
+    model = models.IntegerField(choices=MODEL_TYPES, core=True, default=2)
+    oceans = models.BooleanField(core=True, default=True)
+    gravity = models.BooleanField(core=True, default=True)
+    attenuation = models.BooleanField(core=True, default=True)
+    topography = models.BooleanField(core=True, default=True)
+    rotation = models.BooleanField(core=True, default=True)
+    ellipticity = models.BooleanField(core=True, default=True)
+
+
     def __str__(self): return self.fsNode.name
     
     def icon(self):
@@ -557,6 +556,9 @@
         # allow this.
         return not self.mesh.nchunks == 6
 
+    def get_model_id(self):
+        return model_types[self.model-1][0]
+
     def save(self):
         super(Specfem3DGlobeParameters, self).save()
         if self.fsNode is None:
@@ -569,8 +571,13 @@
     def snapshot(self, workspace):
         return dict(
             mesh = self.mesh.snapshot(),
-            model = self.model.snapshot(),
-            receivers_can_be_buried = self.receivers_can_be_buried,
+            model = self.model,
+            oceans = self.oceans,
+            gravity = self.gravity,
+            attenuation = self.attenuation,
+            topography = self.topography,
+            rotation = self.rotation,
+            ellipticity = self.ellipticity,
             zero_half_duration = workspace.zero_half_duration,
             )
 
@@ -581,12 +588,8 @@
     @classmethod
     def objectFromSnapshot(cls, snapshot):
         mesh = Specfem3DGlobeMesh.objectFromSnapshot(snapshot.pop('mesh'))
-        model = Specfem3DGlobeModel.objectFromSnapshot(snapshot.pop('model'))
-        zero_half_duration = snapshot.pop('zero_half_duration')
         p = cls.Dummy(**snapshot)
         p.mesh = mesh
-        p.model = model
-        p.zero_half_duration = zero_half_duration
         return p
     
     code = 1  # cf. CODE_CHOICES
@@ -598,7 +601,25 @@
     class Admin:
         pass
 
+    @classmethod
+    def buildTableView(cls, tv):
+        cg = tv.addColumnGroup()
+        cg.addColumn('mesh')
+        cg = tv.addColumnGroup("model")
+        cg.addColumn('get_model_display', "name")
+        for attr in [
+            'oceans',
+            'gravity',
+            'attenuation',
+            'topography',
+            'rotation',
+            'ellipticity',
+            ]:
+            title = '<img src="%s/pics/th/%s.gif" align=bottom>' % (config.root, attr)
+            cg.addColumn(attr, title)
+        return
 
+
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 # Mineos
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -665,7 +686,32 @@
     def objectFromSnapshot(cls, snapshot):
         return cls(**snapshot)
 
+    @classmethod
+    def buildTableView(cls, tv):
+        cg = tv.addColumnGroup()
+        cg.addColumn('eps')
+        
+        cg = tv.addColumnGroup()
+        cg.addColumn('wgrav')
+        
+        cg = tv.addColumnGroup('<center>angular<br>orders</center>')
+        cg.addColumn('lmin', "<small>min</small>")
+        cg.addColumn('lmax', "<small>max</small>")
+        
+        cg = tv.addColumnGroup('<center>frequency<br>range<br><small>(mHz)</small></center>')
+        cg.addColumn('wmin', "<small>min</small>")
+        cg.addColumn('wmax', "<small>max</small>")
+        
+        cg = tv.addColumnGroup('<center>dispersion<br>branch<br>numbers</center>')
+        cg.addColumn('nmin', "<small>min</small>")
+        cg.addColumn('nmax', "<small>max</small>")
+        
+        cg = tv.addColumnGroup()
+        cg.addColumn('max_depth', "maximum<br>depth")
+        
+        return
 
+
 class MineosModel(models.Model):
 
     fsNode = models.ForeignKey(FSNode, null=True)
@@ -675,6 +721,8 @@
 
     def __str__(self): return self.fsNode.name
 
+    def name(self): return self.fsNode.name
+
     def icon(self):
         return '<img src="%s/icons/model.gif">' % config.root
 
@@ -692,6 +740,12 @@
         return self.id
 
     @classmethod
+    def buildTableView(cls, tv):
+        cg = tv.addColumnGroup()
+        cg.addColumn('name')
+        return
+
+    @classmethod
     def objectFromSnapshot(cls, snapshot):
         return cls.objects.get(id = snapshot)
 
@@ -769,10 +823,36 @@
     def canHandleEvent(cls, event):
         return event.singleSource is not None
 
+    @classmethod
+    def buildTableView(cls, tv):
+        cg = tv.addColumnGroup()
+        cg.addColumn('catalog')
+        cg = tv.addColumnGroup()
+        cg.addColumn('model')
+        cg = tv.addColumnGroup("oscillations")
+        for attr in [
+            'radial',
+            'toroidal',
+            'spheroidal',
+            'ictoroidal',
+            ]:
+            gif = attr == 'ictoroidal' and 'i_c_toroidal' or attr
+            title = '<img src="%s/pics/th/%s.gif" align=bottom>' % (config.root, gif)
+            cg.addColumn(attr, title)
+        
+        cg = tv.addColumnGroup('<center>select<br>frequency<br>range<br><small>(mHz)</small></center>')
+        cg.addColumn('fmin', "<small>min</small>")
+        cg.addColumn('fmax', "<small>max</small>")
+        
+        cg = tv.addColumnGroup()
+        title = '<img src="%s/pics/th/sampling_period.gif" align=bottom>' % config.root
+        cg.addColumn('step', title)
+        
+        return
 
 
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-# Event Workspaces
+# Workspaces
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
@@ -785,11 +865,10 @@
     raise validators.ValidationError("Please enter a positive number between %.1f and %.1f." % (lower, upper))
 
 
-class EventWorkspace(models.Model):
+class Workspace(models.Model):
 
     fsNode = models.ForeignKey(FSNode, null=True)
 
-    event = models.ForeignKey(Event)
     stations = models.ForeignKey(StationList, limit_choices_to = userFSChoices)
     record_length = models.FloatField(max_digits=19, decimal_places=10, core=True, default=20.0,
                                       validator_list=[isValidRecordLength])
@@ -802,18 +881,27 @@
     def __str__(self): return self.fsNode.name
 
     def icon(self):
-        return self.event.icon()
+        return '<img class="icon" src="%s/icons/document.gif">' % config.root
 
     def save(self):
-        super(EventWorkspace, self).save()
+        super(Workspace, self).save()
         if self.fsNode is None:
             self.fsNode = FSNode.newNode(self)
-            super(EventWorkspace, self).save()
+            super(Workspace, self).save()
         else:
             self.fsNode.save() # touch
         return
 
 
+class WorkspaceItem(models.Model):
+
+    workspace = models.ForeignKey(Workspace, db_index=True)
+    
+    kind = models.ForeignKey(ContentType, db_index=True)
+    objId = models.PositiveIntegerField(db_index=True)
+    obj = models.GenericForeignKey(ct_field='kind', fk_field='objId')
+
+
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 # Requests
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Deleted: cs/portal/trunk/seismo/SeismoWebPortal/static/pics/th/receivers_at_depth.gif
===================================================================
(Binary files differ)

Deleted: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/eventworkspace_form.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/eventworkspace_form.html	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/eventworkspace_form.html	2008-05-01 20:19:02 UTC (rev 11886)
@@ -1,46 +0,0 @@
-
-<h2>settings</h2>
-
-<form method="post" action="{{ action }}">
-
-    {% if form.has_errors %}
-    <p><span class=error>Please correct the following error{{ form.error_dict|pluralize }}.</span>
-    {% endif %}
-
-    <div class=tab30ex>
-
-    <div>
-        <label for="id_stations" class=before>station list</label>
-        {{ form.stations }}
-        {% if form.stations.errors %}<span class=error>{{ form.stations.errors|join:", " }}</span>{% endif %}
-    </div>
-
-    <div>
-        <label for="id_record_length" class=before>record length</label>
-        {{ form.record_length }} minutes
-        {% if form.record_length.errors %}<span class=error>{{ form.record_length.errors|join:", " }}</span>{% endif %}
-        {% if help_visible %}<span class=help>Choose the desired record length of the synthetic seismograms (in minutes). This controls the length of the numerical simulation, i.e., twice the record length requires twice as much CPU time. This must be 100 minutes or less for simulations run via the web.</span>{% endif %}
-    </div>
-
-    <p></p>
-
-    <fieldset><legend>3D Synthetics</legend>
-
-    <div class=checkbox>
-        {{ form.zero_half_duration }}
-        <label for="id_zero_half_duration" class=after>zero half duration</label>
-        {% if form.zero_half_duration.errors %}<span class=error>{{ form.zero_half_duration.errors|join:", " }}</span>{% endif %}
-    </div>
-
-    {% if help_visible %}
-    <dl class=help><dt>zero half duration</dt><dd>For point-source simulations, we recommend setting the source half-duration parameter <code>half duration</code> equal to zero, which corresponds to simulating a step source-time function, i.e., a moment-rate function that is a delta function. If <code>half duration</code> is not set to zero, the code will use a Gaussian (i.e., a signal with a shape similar to a 'smoothed triangle', as explained in Komatitsch and Tromp [2002a]) source-time function with half-width <code>half duration</code>. We prefer to run the solver with <code>half duration</code> set to zero and convolve the resulting synthetic seismograms in post-processing after the run, because this way it is easy to use a variety of source-time functions. Komatitsch and Tromp [2002a] determined that the noise generated in the simulation by using a step source time function may be safely filtered out afterward based upon a convolution with the desired source time function and/or low-pass filtering. Use the script <code>process_syn.pl</code> for this purpose by specifying the <code>-h</code> option (the <code>process_syn.pl</code> script can be found in the <a href="{{root}}/samples/UTILS.tar.gz">utilities package</a>). Alternatively, use signal-processing software packages such as <a href="http://www.llnl.gov/sac/">SAC</a>.<p>For finite fault simulations, it is usually not advisable to use a zero half duration and convolve afterwards, since the half duration is generally fixed by the finite fault model.</dd></dl>
-    {% endif %}
-
-    </fieldset>
-
-    <div><input class=submit type="submit" name="save" value="Save" />
-    </div>
-
-    </div> <!-- tab30ex -->
-
-</form>

Modified: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/mineosparameters_form.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/mineosparameters_form.html	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/mineosparameters_form.html	2008-05-01 20:19:02 UTC (rev 11886)
@@ -1,7 +1,7 @@
 
 <h2>{% if object %}edit {{ object }}{% else %}new mineos parameter set{% endif %}</h2>
 
-<form method="post" action="{{ action }}">
+<form method="post" action=".">
 
     {% if form.has_errors %}
     <p><span class=error>Please correct the following error{{ form.error_dict|pluralize }}.</span>

Modified: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/par_file.txt
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/par_file.txt	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/par_file.txt	2008-05-01 20:19:02 UTC (rev 11886)
@@ -31,15 +31,15 @@
 # fully 3D models:
 # transversely_isotropic_prem_plus_3D_crust_2.0, 3D_anisotropic, 3D_attenuation,
 # s20rts, s362ani, s362iso, s362wmani, s362ani_prem, s29ea
-MODEL                           = {{ simulation.model.get_type_id }}
+MODEL                           = {{ simulation.get_model_id }}
 
 # parameters describing the Earth model
-OCEANS                          = .{{ simulation.model.oceans }}.
-ELLIPTICITY                     = .{{ simulation.model.ellipticity }}.
-TOPOGRAPHY                      = .{{ simulation.model.topography }}.
-GRAVITY                         = .{{ simulation.model.gravity }}.
-ROTATION                        = .{{ simulation.model.rotation }}.
-ATTENUATION                     = .{{ simulation.model.attenuation }}.
+OCEANS                          = .{{ simulation.oceans }}.
+ELLIPTICITY                     = .{{ simulation.ellipticity }}.
+TOPOGRAPHY                      = .{{ simulation.topography }}.
+GRAVITY                         = .{{ simulation.gravity }}.
+ROTATION                        = .{{ simulation.rotation }}.
+ATTENUATION                     = .{{ simulation.attenuation }}.
 
 # absorbing boundary conditions for a regional simulation
 ABSORBING_CONDITIONS            = .{{ simulation.auto_absorbing_conditions }}.
@@ -105,7 +105,7 @@
 USE_BINARY_FOR_LARGE_FILE       = .false.
 
 # flag to impose receivers at the surface or allow them to be buried
-RECEIVERS_CAN_BE_BURIED         = .{{ simulation.receivers_can_be_buried }}.
+RECEIVERS_CAN_BE_BURIED         = .true.
 
 # print source time function
 PRINT_SOURCE_TIME_FUNCTION      = .false.

Modified: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/parameters.pml
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/parameters.pml	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/parameters.pml	2008-05-01 20:19:02 UTC (rev 11886)
@@ -5,12 +5,11 @@
 <inventory>
 
     <component name="Specfem3DGlobe">
-        <facility name="model">{{ simulation.model.get_type_id }}</facility>
+        <facility name="model">{{ simulation.get_model_id }}</facility>
 
         <component name="solver">
             <property name="record-length">{{ simulation.record_length }}*minute</property>
             <property name="absorbing-conditions">{{ simulation.auto_absorbing_conditions }}</property>
-            <property name="receivers-can-be-buried">{{ simulation.receivers_can_be_buried }}</property>
         </component>
 
 
@@ -29,12 +28,12 @@
 
 
         <component name="model">
-            <property name="oceans">{{ simulation.model.oceans }}</property>
-            <property name="ellipticity">{{ simulation.model.ellipticity }}</property>
-            <property name="topography">{{ simulation.model.topography }}</property>
-            <property name="gravity">{{ simulation.model.gravity }}</property>
-            <property name="rotation">{{ simulation.model.rotation }}</property>
-            <property name="attenuation">{{ simulation.model.attenuation }}</property>
+            <property name="oceans">{{ simulation.oceans }}</property>
+            <property name="ellipticity">{{ simulation.ellipticity }}</property>
+            <property name="topography">{{ simulation.topography }}</property>
+            <property name="gravity">{{ simulation.gravity }}</property>
+            <property name="rotation">{{ simulation.rotation }}</property>
+            <property name="attenuation">{{ simulation.attenuation }}</property>
         </component>
 
     </component>

Modified: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/seismogram_table.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/seismogram_table.html	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/seismogram_table.html	2008-05-01 20:19:02 UTC (rev 11886)
@@ -73,7 +73,6 @@
     <tr>
         <th colspan=7 valign=bottom>model</th>
         <th valign=bottom rowspan=3>mesh</th>
-        <th valign=bottom rowspan=3><img src="{{root}}/pics/th/receivers_at_depth.gif" width=12 height=92 align=bottom></th>
         <th rowspan=3 valign=bottom>data</th>
     </tr>
     <tr>
@@ -92,15 +91,14 @@
     <tbody>
     {% for ps in specfem3dglobe_parameter_set_list %}
     <tr class="{% cycle odd,even %}" id="ps{{ps.id}}">
-        <td>{{ ps.model.get_type_display }}</td>
-        <td><img src="{{ root }}/pics/icon-{% if ps.model.oceans %}yes{% else %}no{% endif %}.gif" alt="{{ ps.model.oceans }}"></td>
-        <td><img src="{{ root }}/pics/icon-{% if ps.model.gravity %}yes{% else %}no{% endif %}.gif" alt="{{ ps.model.gravity }}"></td>
-        <td><img src="{{ root }}/pics/icon-{% if ps.model.attenuation %}yes{% else %}no{% endif %}.gif" alt="{{ ps.model.attenuation }}"></td>
-        <td><img src="{{ root }}/pics/icon-{% if ps.model.topography %}yes{% else %}no{% endif %}.gif" alt="{{ ps.model.topography }}"></td>
-        <td><img src="{{ root }}/pics/icon-{% if ps.model.rotation %}yes{% else %}no{% endif %}.gif" alt="{{ ps.model.rotation }}"></td>
-        <td><img src="{{ root }}/pics/icon-{% if ps.model.ellipticity %}yes{% else %}no{% endif %}.gif" alt="{{ ps.model.ellipticity }}"></td>
+        <td>{{ ps.get_model_display }}</td>
+        <td><img src="{{ root }}/pics/icon-{% if ps.oceans %}yes{% else %}no{% endif %}.gif" alt="{{ ps.oceans }}"></td>
+        <td><img src="{{ root }}/pics/icon-{% if ps.gravity %}yes{% else %}no{% endif %}.gif" alt="{{ ps.gravity }}"></td>
+        <td><img src="{{ root }}/pics/icon-{% if ps.attenuation %}yes{% else %}no{% endif %}.gif" alt="{{ ps.attenuation }}"></td>
+        <td><img src="{{ root }}/pics/icon-{% if ps.topography %}yes{% else %}no{% endif %}.gif" alt="{{ ps.topography }}"></td>
+        <td><img src="{{ root }}/pics/icon-{% if ps.rotation %}yes{% else %}no{% endif %}.gif" alt="{{ ps.rotation }}"></td>
+        <td><img src="{{ root }}/pics/icon-{% if ps.ellipticity %}yes{% else %}no{% endif %}.gif" alt="{{ ps.ellipticity }}"></td>
         <td>{{ ps.mesh }}</td>
-        <td><img src="{{ root }}/pics/icon-{% if ps.receivers_can_be_buried %}yes{% else %}no{% endif %}.gif" alt="{{ ps.receivers_can_be_buried }}"></td>
         <td>{{ ps.outputStatus }}</td>
     </tr>
     {% endfor %}

Deleted: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemodel_form.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemodel_form.html	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobemodel_form.html	2008-05-01 20:19:02 UTC (rev 11886)
@@ -1,84 +0,0 @@
-
-<h2>{% if object %}edit {{ object }}{% else %}new model{% endif %}</h2>
-
-<form method="post" action="{{ action }}">
-
-    {% if form.has_errors %}
-    <p><span class=error>Please correct the following error{{ form.error_dict|pluralize }}.</span>
-    {% endif %}
-
-    <div class=tab30ex>
-
-    <div>
-        <label for="id_type" class=before>type</label>
-        {{ form.type }}
-        {% if form.type.errors %}<span class=error>{{ form.type.errors|join:", " }}</span>{% endif %}
-
-        {% if help_visible %}
-        <dl class=help>
-
-            <dt>crust2.0+prem</dt><dd>This model has CRUST2.0 [Bassin et al., 2000] on top of a transversely isotropic PREM. We first extrapolate PREM mantle velocity up to the surface, then overwrite the model with CRUST2.0.</dd>
-
-            <dt>s20rts</dt><dd>By default, the code uses 3D mantle model S20RTS [Ritsema et al., 1999] and 3D crustal model Crust2.0 [Bassin et al., 2000]. Note that S20RTS uses transversely isotropic PREM as a background model, and that we use the PREM radial attenuation model when 'attenuation' is selected.</dd>
-
-            <dt>s362ani</dt><dd>A global shear-wave speed model developed by Kustowski et al. [2006]. In this model, radial anisotropy is confined to the uppermost mantle. The model (and the corresponding mesh) incorporate tomography on the 650~km and 410~km discontinuities in the 1D reference model REF.</dd>
-
-            <dt>s362wmani</dt><dd>A version of S362ANI with anisotropy allowed throughout the mantle.</dd>
-            <dt>s362ani+prem</dt><dd>A version of S362ANI calculated using PREM as the 1D reference model.</dd>
-            <dt>s29ea</dt><dd>A global model with higher resolution in the upper mantle beneath Eurasia calculated using REF as the 1D reference model.</dd>
-        </dl>
-        {% endif %}
-
-    </div>
-
-    <div class=checkbox>
-        {{ form.oceans }}
-        <label for="id_oceans" class=after>oceans</label>
-        {% if form.oceans.errors %}<span class=error>{{ form.oceans.errors|join:", " }}</span>{% endif %}
-        {% if help_visible %}<span class=help>If selected, the effect of the oceans on seismic wave propagation will be incorporated based upon the approximate treatment discussed in Komatitsch and Tromp [2002b]. This feature is inexpensive from a numerical perspective, both in terms of memory requirements and CPU time. This approximation is accurate at periods of roughly 20s and longer. At shorter periods the effect of water phases/reverberations is not taken into account, even when the flag is on.</span>{% endif %}
-    </div>
-
-    <div class=checkbox>
-        {{ form.gravity }}
-        <label for="id_gravity" class=after>gravity</label>
-        {% if form.gravity.errors %}<span class=error>{{ form.gravity.errors|join:", " }}</span>{% endif %}
-        {% if help_visible %}<span class=help>If selected, self-gravitation will be incorporated in the Cowling approximation [Komatitsch and Tromp, 2002b, Dahlen and Tromp, 1998]. Turning this feature on is relatively inexpensive, both from the perspective of memory requirements as well as in terms of computational speed.</span>{% endif %}
-    </div>
-
-    <div class=checkbox>
-        {{ form.attenuation }}
-        <label for="id_attenuation" class=after>attenuation</label>
-        {% if form.attenuation.errors %}<span class=error>{{ form.attenuation.errors|join:", " }}</span>{% endif %}
-        {% if help_visible %}<span class=help>If selected, attenuation will be incorporated. Turning this feature on increases the memory requirements significantly (roughly by a factor of 1.5), and is numerically fairly expensive. Of course for realistic simulations this flag should be turned on. See Komatitsch and Tromp [1999, 2002a] for a discussion on the implementation of attenuation based upon standard linear solids.</span>{% endif %}
-    </div>
-
-    <div class=checkbox>
-        {{ form.topography }}
-        <label for="id_topography" class=after>topography</label>
-        {% if form.topography.errors %}<span class=error>{{ form.topography.errors|join:", " }}</span>{% endif %}
-        {% if help_visible %}<span class=help>If selected, topography and bathymetry will be incorporated based upon model ETOPO5 [NOAA, 1988]. This feature adds no cost to the simulation.</span>{% endif %}
-    </div>
-
-    <div class=checkbox>
-        {{ form.rotation }}
-        <label for="id_rotation" class=after>rotation</label>
-        {% if form.rotation.errors %}<span class=error>{{ form.rotation.errors|join:", " }}</span>{% endif %}
-        {% if help_visible %}<span class=help>If selected, the Coriolis effect will be incorporated. Turning this feature on is relatively cheap 
-numerically.</span>{% endif %}
-    </div>
-
-    <div class=checkbox>
-        {{ form.ellipticity }}
-        <label for="id_ellipticity" class=after>ellipticity</label>
-        {% if form.ellipticity.errors %}<span class=error>{{ form.ellipticity.errors|join:", " }}</span>{% endif %}
-        {% if help_visible %}<span class=help>If selected, the mesh will make the Earth model elliptical in shape according to
- Clairaut's equation [Dahlen and Tromp, 1998]. This feature adds no cost to the simulation.</span>{% endif %}
-    </div>
-
-    <div>
-        <input class=submit type="submit" value="Save" />
-    </div>
-
-    </div> <!-- tab30ex -->
-
-</form>

Deleted: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_detail.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_detail.html	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_detail.html	2008-05-01 20:19:02 UTC (rev 11886)
@@ -1,13 +0,0 @@
-
-<h2>{{ object }}</h2>
-
-<div class=properties>
-
-    <dl>
-        <dt>type</dt><dd>{% ifequal object.mesh.nchunks 6 %}global{% else %}regional{% endifequal %}</dd>
-        <dt>mesh</dt><dd><a href="../../meshes/{{ object.mesh.id }}/">{{ object.mesh }}</a></dd>
-        <dt>model</dt><dd><a href="../../models/{{ object.model.id }}/">{{ object.model }}</a></dd>
-        <dt>receivers at depth</dt><dd>{{ object.receivers_can_be_buried }}</dd>
-    </dl>
-
-</div>

Modified: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_form.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_form.html	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/specfem3dglobeparameters_form.html	2008-05-01 20:19:02 UTC (rev 11886)
@@ -19,16 +19,69 @@
         <label for="id_model" class=before>model</label>
         {{ form.model }}
         {% if form.model.errors %}<span class=error>{{ form.model.errors|join:", " }}</span>{% endif %}
+
+        {% if help_visible %}
+        <dl class=help>
+
+            <dt>crust2.0+prem</dt><dd>This model has CRUST2.0 [Bassin et al., 2000] on top of a transversely isotropic PREM. We first extrapolate PREM mantle velocity up to the surface, then overwrite the model with CRUST2.0.</dd>
+
+            <dt>s20rts</dt><dd>By default, the code uses 3D mantle model S20RTS [Ritsema et al., 1999] and 3D crustal model Crust2.0 [Bassin et al., 2000]. Note that S20RTS uses transversely isotropic PREM as a background model, and that we use the PREM radial attenuation model when 'attenuation' is selected.</dd>
+
+            <dt>s362ani</dt><dd>A global shear-wave speed model developed by Kustowski et al. [2006]. In this model, radial anisotropy is confined to the uppermost mantle. The model (and the corresponding mesh) incorporate tomography on the 650~km and 410~km discontinuities in the 1D reference model REF.</dd>
+
+            <dt>s362wmani</dt><dd>A version of S362ANI with anisotropy allowed throughout the mantle.</dd>
+            <dt>s362ani+prem</dt><dd>A version of S362ANI calculated using PREM as the 1D reference model.</dd>
+            <dt>s29ea</dt><dd>A global model with higher resolution in the upper mantle beneath Eurasia calculated using REF as the 1D reference model.</dd>
+        </dl>
+        {% endif %}
+
     </div>
 
+    <div class=checkbox>
+        {{ form.oceans }}
+        <label for="id_oceans" class=after>oceans</label>
+        {% if form.oceans.errors %}<span class=error>{{ form.oceans.errors|join:", " }}</span>{% endif %}
+        {% if help_visible %}<span class=help>If selected, the effect of the oceans on seismic wave propagation will be incorporated based upon the approximate treatment discussed in Komatitsch and Tromp [2002b]. This feature is inexpensive from a numerical perspective, both in terms of memory requirements and CPU time. This approximation is accurate at periods of roughly 20s and longer. At shorter periods the effect of water phases/reverberations is not taken into account, even when the flag is on.</span>{% endif %}
+    </div>
 
     <div class=checkbox>
-        {{ form.receivers_can_be_buried }}
-        <label for="id_receivers_can_be_buried" class=after>receivers at depth</label>
-        {% if form.receivers_can_be_buried.errors %}<span class=error>{{ form.receivers_can_be_buried.errors|join:", " }}</span>{% endif %}
-        {% if help_visible %}<span class=help>This flag accommodates stations with instruments that are buried, i.e., the solver will calculate seismograms at the burial depth specified in the station list.</span>{% endif %}
+        {{ form.gravity }}
+        <label for="id_gravity" class=after>gravity</label>
+        {% if form.gravity.errors %}<span class=error>{{ form.gravity.errors|join:", " }}</span>{% endif %}
+        {% if help_visible %}<span class=help>If selected, self-gravitation will be incorporated in the Cowling approximation [Komatitsch and Tromp, 2002b, Dahlen and Tromp, 1998]. Turning this feature on is relatively inexpensive, both from the perspective of memory requirements as well as in terms of computational speed.</span>{% endif %}
     </div>
 
+    <div class=checkbox>
+        {{ form.attenuation }}
+        <label for="id_attenuation" class=after>attenuation</label>
+        {% if form.attenuation.errors %}<span class=error>{{ form.attenuation.errors|join:", " }}</span>{% endif %}
+        {% if help_visible %}<span class=help>If selected, attenuation will be incorporated. Turning this feature on increases the memory requirements significantly (roughly by a factor of 1.5), and is numerically fairly expensive. Of course for realistic simulations this flag should be turned on. See Komatitsch and Tromp [1999, 2002a] for a discussion on the implementation of attenuation based upon standard linear solids.</span>{% endif %}
+    </div>
+
+    <div class=checkbox>
+        {{ form.topography }}
+        <label for="id_topography" class=after>topography</label>
+        {% if form.topography.errors %}<span class=error>{{ form.topography.errors|join:", " }}</span>{% endif %}
+        {% if help_visible %}<span class=help>If selected, topography and bathymetry will be incorporated based upon model ETOPO5 [NOAA, 1988]. This feature adds no cost to the simulation.</span>{% endif %}
+    </div>
+
+    <div class=checkbox>
+        {{ form.rotation }}
+        <label for="id_rotation" class=after>rotation</label>
+        {% if form.rotation.errors %}<span class=error>{{ form.rotation.errors|join:", " }}</span>{% endif %}
+        {% if help_visible %}<span class=help>If selected, the Coriolis effect will be incorporated. Turning this feature on is relatively cheap 
+numerically.</span>{% endif %}
+    </div>
+
+    <div class=checkbox>
+        {{ form.ellipticity }}
+        <label for="id_ellipticity" class=after>ellipticity</label>
+        {% if form.ellipticity.errors %}<span class=error>{{ form.ellipticity.errors|join:", " }}</span>{% endif %}
+        {% if help_visible %}<span class=help>If selected, the mesh will make the Earth model elliptical in shape according to
+ Clairaut's equation [Dahlen and Tromp, 1998]. This feature adds no cost to the simulation.</span>{% endif %}
+    </div>
+
+
     <div><input class=submit type="submit" name="save" value="Save" />
     </div>
 

Modified: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/stationlist_upload.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/stationlist_upload.html	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/stationlist_upload.html	2008-05-01 20:19:02 UTC (rev 11886)
@@ -36,7 +36,7 @@
 include as many stations as conceivably useful in the
 <code>STATIONS</code> file, which looks like this:</p>
 
-<div class=illustration id=stations>
+<div class=illustration id=stationsIllustration>
     <img src="{{root}}/pics/stations.gif" width=576 height=322>
     <p class=caption>Sample <code>STATIONS</code> file.</p>
 </div>

Deleted: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/welcome.html
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/welcome.html	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/welcome.html	2008-05-01 20:19:02 UTC (rev 11886)
@@ -1,21 +0,0 @@
-
-<h2>Welcome</h2>
-
-<p>Welcome to the CIG Seismology Web Portal.</p>
-
-<table width="100%" cellspacing=40>
-    <tr>
-        <td>
-            <a href="{{root}}/events/"><img src="{{root}}/icons/eventfinder.gif" float=left>
-                                       <span style="font-size: x-large;">Search for an event</span></a><br>
-            in the the Global CMT Catalog
-        </td>
-
-        <td>
-            <a href="{{root}}/config/events/upload/"><img src="{{root}}/icons/eventupload.gif" float=left>
-                                       <span style="font-size: x-large;">Upload an event file</span></a><br>
-            in CMTSOLUTION format
-        </td>
-
-    </tr>
-</table>

Copied: cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/workspace_form.html (from rev 11862, cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/eventworkspace_form.html)
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/workspace_form.html	                        (rev 0)
+++ cs/portal/trunk/seismo/SeismoWebPortal/templates/SeismoWebPortal/workspace_form.html	2008-05-01 20:19:02 UTC (rev 11886)
@@ -0,0 +1,46 @@
+
+<h2>settings</h2>
+
+<form method="post" action="{{ action }}">
+
+    {% if form.has_errors %}
+    <p><span class=error>Please correct the following error{{ form.error_dict|pluralize }}.</span>
+    {% endif %}
+
+    <div class=tab30ex>
+
+    <div>
+        <label for="id_stations" class=before>station list</label>
+        {{ form.stations }}
+        {% if form.stations.errors %}<span class=error>{{ form.stations.errors|join:", " }}</span>{% endif %}
+    </div>
+
+    <div>
+        <label for="id_record_length" class=before>record length</label>
+        {{ form.record_length }} minutes
+        {% if form.record_length.errors %}<span class=error>{{ form.record_length.errors|join:", " }}</span>{% endif %}
+        {% if help_visible %}<span class=help>Choose the desired record length of the synthetic seismograms (in minutes). This controls the length of the numerical simulation, i.e., twice the record length requires twice as much CPU time. This must be 100 minutes or less for simulations run via the web.</span>{% endif %}
+    </div>
+
+    <p></p>
+
+    <fieldset><legend>3D Synthetics</legend>
+
+    <div class=checkbox>
+        {{ form.zero_half_duration }}
+        <label for="id_zero_half_duration" class=after>zero half duration</label>
+        {% if form.zero_half_duration.errors %}<span class=error>{{ form.zero_half_duration.errors|join:", " }}</span>{% endif %}
+    </div>
+
+    {% if help_visible %}
+    <dl class=help><dt>zero half duration</dt><dd>For point-source simulations, we recommend setting the source half-duration parameter <code>half duration</code> equal to zero, which corresponds to simulating a step source-time function, i.e., a moment-rate function that is a delta function. If <code>half duration</code> is not set to zero, the code will use a Gaussian (i.e., a signal with a shape similar to a 'smoothed triangle', as explained in Komatitsch and Tromp [2002a]) source-time function with half-width <code>half duration</code>. We prefer to run the solver with <code>half duration</code> set to zero and convolve the resulting synthetic seismograms in post-processing after the run, because this way it is easy to use a variety of source-time functions. Komatitsch and Tromp [2002a] determined that the noise generated in the simulation by using a step source time function may be safely filtered out afterward based upon a convolution with the desired source time function and/or low-pass filtering. Use the script <code>process_syn.pl</code> for this purpose by specifying the <code>-h</code> option (the <code>process_syn.pl</code> script can be found in the <a href="{{root}}/samples/UTILS.tar.gz">utilities package</a>). Alternatively, use signal-processing software packages such as <a href="http://www.llnl.gov/sac/">SAC</a>.<p>For finite fault simulations, it is usually not advisable to use a zero half duration and convolve afterwards, since the half duration is generally fixed by the finite fault model.</dd></dl>
+    {% endif %}
+
+    </fieldset>
+
+    <div><input class=submit type="submit" name="save" value="Save" />
+    </div>
+
+    </div> <!-- tab30ex -->
+
+</form>

Modified: cs/portal/trunk/seismo/SeismoWebPortal/views.py
===================================================================
--- cs/portal/trunk/seismo/SeismoWebPortal/views.py	2008-05-01 20:06:06 UTC (rev 11885)
+++ cs/portal/trunk/seismo/SeismoWebPortal/views.py	2008-05-01 20:19:02 UTC (rev 11886)
@@ -1,27 +1,16 @@
-# Create your views here.
 
 from django import forms
 from django.conf import settings
-from django.contrib.auth.decorators import user_passes_test
-from django.core import validators
 from django.http import HttpResponse, HttpResponseRedirect, Http404
 from django.shortcuts import render_to_response, get_object_or_404
 from django.template import loader
 from django.template.context import RequestContext
 from create_update import create_object, update_object
-from forms import RegistrationAddManipulator, RegistrationChangeManipulator
-from models import Location
-from models import Event, Source
-from models import Station, StationList, StationNetwork, DataSource, Region
-from models import Specfem3DGlobeModel, Specfem3DGlobeMesh, Specfem3DGlobeParameters
-from models import MineosModeCatalog, MineosModel, MineosParameters
-from models import Run, Job, OutputFile
-from models import FSNode, Folder, userFSChoices
-from models import EventWorkspace, Request
-from models import CMTSolution as CMTSolutionModel
-from cmt import CMTSolution
+
+import cmt
 import config
 import gui
+import models
 
 import os, os.path
 from HTMLParser import HTMLParser
@@ -81,7 +70,9 @@
     if not path:
         if request.user.is_anonymous():
             return httpResponse(about(request, desktop))
-        return HttpResponseRedirect("%s/welcome/" % config.root)
+        #return HttpResponseRedirect("%s/welcome/" % config.root)
+        openPrivateApps(request, desktop)
+        return httpResponse(readme(request, path, desktop))
 
     # Plone expand/collapse icon redirect hack
     last = path[-1]
@@ -122,7 +113,6 @@
     gmt = lambda request, path, desktop: gmtServe(request, pathname, document_root = GMT_ROOT)
 
     index = Index({
-        "welcome": welcome,
         "profile": profile,
         "logout": logout,
         "help": help,
@@ -136,7 +126,7 @@
         return httpResponse(privateView(request, path, desktop))
 
     objId = intOr404(name)
-    fsNode = get_object_or_404(FSNode, id=objId)
+    fsNode = get_object_or_404(models.FSNode, id=objId)
     
     if fsNode.inTrash:
         raise Http404 # NYI
@@ -147,8 +137,8 @@
     
     obj = fsNode.contents
     ModelClass = obj.__class__
-    if ModelClass is EventWorkspace:
-        return httpResponse(eventViewer(obj, config.root + "/%d/" % objId, request, path, desktop))
+    if ModelClass is models.Workspace:
+        return httpResponse(workspace(obj, config.root + "/%d/" % objId, request, path, desktop))
 
     raise Http404
 
@@ -181,20 +171,12 @@
 
 
 def openPrivateApps(request, desktop):
-    openEventViewers(request, desktop)
-    if not desktop.eventViewers:
-        openGreeter(request, desktop)
+    openWorkspaces(request, desktop)
     if request.user.is_staff:
         openAdminApp(request, desktop)
     return
 
 
-def openGreeter(request, desktop):
-    welcome = gui.AppWindow(config.root + "/welcome/", "Welcome", Memento('welcome', request.session))
-    desktop.insertWindow(welcome)
-    desktop.welcome = welcome
-
-
 def httpResponse(response):
     # "Cast" the argument, 'response', to an HttpResponse.
     
@@ -237,11 +219,11 @@
 
 
 def trashFolder(workspace, url, request, path, appWindow, desktop):
-    return folderView(Folder.trashFolder(request.user), "trash", workspace, url + "trash/", request, path, appWindow, desktop)
+    return folderView(models.Folder.trashFolder(request.user), "trash", workspace, url + "trash/", request, path, appWindow, desktop)
 
 
 def sharedFolder(workspace, url, request, path, appWindow, desktop):
-    return folderView(Folder.sharedFolder(), "shared", workspace, url + "shared/", request, path, appWindow, desktop)
+    return folderView(models.Folder.sharedFolder(), "shared", workspace, url + "shared/", request, path, appWindow, desktop)
 
 
 def folderView(folder, slug, workspace, url, request, path, appWindow, desktop, ModelClass=None):
@@ -262,11 +244,11 @@
 
     name = folder.fsNode.name
     if ModelClass is None:
-        items = FSNode.objects.filter(parent=folder.fsNode)
+        items = models.FSNode.objects.filter(parent=folder.fsNode)
     else:
         from django.contrib.contenttypes.models import ContentType
         ctype = ContentType.objects.get_for_model(ModelClass)
-        items = FSNode.objects.filter(parent=folder.fsNode, kind=ctype)
+        items = models.FSNode.objects.filter(parent=folder.fsNode, kind=ctype)
     
     html = StringIO()
     
@@ -290,27 +272,18 @@
     return gui.StaticContent(html.getvalue())
 
 
-def welcome(request, path, desktop):
-    if path: raise Http404
-    if not hasattr(desktop, 'welcome'):
-        openGreeter(request, desktop)
-    appWindow = desktop.welcome
-    desktop.selectWindow(appWindow)
-    child = gui.ChildWindow(config.root + "/welcome/", "View")
-    child.content = gui.StaticContent(loader.render_to_string('SeismoWebPortal/welcome.html', { 'root': config.root }))
-    desktop.activeWindow.selectWindow(child)
-    return desktop
-
-
 def readme(request, path, desktop):
     if path: raise Http404
+    welcome = gui.AppWindow(config.root + "/welcome/", "Welcome", Memento('welcome', request.session))
+    desktop.insertWindow(welcome)
+    desktop.selectWindow(welcome)
     child = gui.ChildWindow(config.root + "/readme/", "View")
     child.content = gui.StaticContent(loader.render_to_string('SeismoWebPortal/readme.html', { 'root': config.root }))
     desktop.activeWindow.selectWindow(child)
     return desktop
 
 
-def configFactory(url, request, path, desktop):
+def configFactory(workspace, url, request, path, desktop):
 
     if not path: raise Http404
     
@@ -324,20 +297,33 @@
                           url + "config/stations/upload/",
                           "SeismoWebPortal/stationlist_upload.html")
 
+    def configMineosModels(url, request, path, desktop):
+        return uploadOnly(request, path, desktop,
+                          UploadMineosModelManipulator(),
+                          url,
+                          url + "config/mineos/models/upload/",
+                          "SeismoWebPortal/mineosmodel_upload.html")
+
+    def addToWorkspace(obj):
+        models.WorkspaceItem.objects.create(
+            workspace = workspace,
+            obj = obj,
+            )
+        return
+
     index = Index({
-        "specfem3dglobe": Index({"models": Specfem3DGlobeModel,
-                                 "meshes": [config_specfem3dglobe_meshes],
-                                 "parameters": [config_specfem3dglobe_parameters] }),
-        "mineos": Index({"modes": MineosModeCatalog,
-                         "parameters": MineosParameters,
-                         "models": [config_mineos_models]}),
+        "specfem3dglobe": Index({"meshes": [config_specfem3dglobe_meshes],
+                                 "parameters": models.Specfem3DGlobeParameters }),
+        "mineos": Index({"modes": models.MineosModeCatalog,
+                         "parameters": models.MineosParameters,
+                         "models": [configMineosModels]}),
         })
     index = index[name]
     subURL = name
     name = path.pop(0)
     handler = index[name]
     if isinstance(handler, list):
-        return handler[0](url, request, path, desktop)
+        return handler[0](url, request, path, desktop, addToWorkspace)
     else:
         ModelClass = handler
     subURL += "/" + name
@@ -346,7 +332,8 @@
         if path: raise Http404
         window = gui.ChildWindow("%sconfig/%s/new/" % (url, subURL), "New")
         window.buttons.append(helpButton(request))
-        return create(request, desktop, window, ModelClass)
+        return create(request, desktop, window, ModelClass,
+                      post_save_hook = addToWorkspace)
 
     raise Http404
 
@@ -365,7 +352,7 @@
     parentURL = url
     url += "%d/" % objId
     
-    fsNode = get_object_or_404(FSNode, id=objId)
+    fsNode = get_object_or_404(models.FSNode, id=objId)
     
     folder = appWindow.root.index[folderName]
     appWindow.root.setAnchor(folderName)
@@ -383,9 +370,8 @@
     ModelClass = obj.__class__
 
     specialClasses = {
-        StationList: configStationList,
-        Specfem3DGlobeMesh: configSpecfem3DGlobeMesh,
-        Specfem3DGlobeParameters: configSpecfem3DGlobeParameters,
+        models.StationList: configStationList,
+        models.Specfem3DGlobeMesh: configSpecfem3DGlobeMesh,
         }
     specialView = specialClasses.get(ModelClass)
     if specialView:
@@ -418,10 +404,10 @@
         return update_object(request,
                              desktop,
                              properties,
-                             FSNode,
+                             models.FSNode,
                              objId,
                              post_save_redirect = url,
-                             follow = FSNode.follow,
+                             follow = models.FSNode.follow,
                              )
     if name == "delete":
         obj = get_object_or_404(ModelClass, id=objId)
@@ -444,20 +430,77 @@
     return desktop
 
 
+class Blob(object):
+    def __init__(self, **kwds):
+        self.__dict__.update(kwds)
+
+
+def configTable(workspace, url, request, path, appWindow, desktop, ModelClass):
+    from django.contrib.contenttypes.models import ContentType
+
+    if path: raise Http404
+    
+    tv = gui.TableView(caption = "Dogs!")
+    cg = tv.addColumnGroup()
+    col = cg.addColumn('firstName', "First Name")
+    col = cg.addColumn('lastName', "Last Name")
+    tv.addRow(Blob(firstName = "Spike", lastName = "Dog"))
+    tv.addRow(Blob(firstName = "Buffy", lastName = "Dog"))
+
+    sel = """
+    <dl class="actionMenu" id="uniqueIdForThisMenu">
+  <dt class="actionMenuHeader">
+    <a href="some_destination">A Title</a>
+  </dt>
+  <dd class="actionMenuContent">
+    <ul>
+        <li><a class="visualIconPadding visualIcon" href="/">red</a></li>
+        <li><a class="visualIconPadding visualIcon"  href="red">red</a></li>
+        <li><a href="redx">redx</a></li>
+    </ul>
+  </dd>
+</dl>
+"""
+    sel2 = """
+    <dl class="actionMenu" id="uniqueIdForThisMenu2">
+  <dt class="actionMenuHeader">
+    <a href="some_destination2">A Title</a>
+  </dt>
+  <dd class="actionMenuContent">
+  <img src="http://crust.geodynamics.org:8000/portals/seismo/maps/stations/1/mercator.jpg">
+  </dd>
+</dl>
+"""
+    tv.addRow(Blob(firstName = sel, lastName = sel2))
+    dogTV = tv
+
+    tv = gui.TableView()
+    ModelClass.buildTableView(tv)
+    ctype = ContentType.objects.get_for_model(ModelClass)
+    for item in workspace.workspaceitem_set.filter(kind=ctype):
+        tv.addRow(item.obj)
+    
+    view = gui.ChildWindow(url, "View")
+    view.content = tv
+    desktop.activeWindow.insertWindow(view)
+    desktop.activeWindow.selectWindow(view)
+    return desktop
+
+
 def homeFolder(workspace, url, request, path, appWindow, desktop):
 
     url += "home/"
     if path:
         name = path.pop(0)
         if name == "config":
-            return configFactory(url, request, path, desktop)
+            return configFactory(workspace, url, request, path, desktop)
         return configObject(name, workspace, url, request, path, appWindow, desktop, "home")
     
     appWindow.root.setAnchor("home")
-    homeFolder = appWindow.root.index["home"]
-    appWindow.path.append(homeFolder)
+    #homeFolder = appWindow.root.index["home"]
+    #appWindow.path.append(homeFolder)
 
-    home = Folder.homeFolder(request.user)
+    home = models.Folder.homeFolder(request.user)
     
     child = gui.ChildWindow(url, "View")
     configURL = url + "config/"
@@ -481,87 +524,20 @@
                  ]
                  )
         ]
-    home = Folder.homeFolder(request.user)
+    home = models.Folder.homeFolder(request.user)
     child.content = folderIconView(home, workspace, url, request, path, appWindow, desktop)
     desktop.activeWindow.selectWindow(child)
     return desktop
     
 
 
-def config_specfem3dglobe_parameters(url, request, path, desktop):
-    name = path.pop(0)
-    if name == "new":
-        window = gui.ChildWindow(url + "config/specfem3dglobe/parameters/new/", "New")
-        window.buttons.append(helpButton(request))
-        return create_object(request,
-                             desktop,
-                             window,
-                             Specfem3DGlobeParameters,
-                             post_save_redirect = url,
-                             follow = { 'fsNode': False },
-                             )
-    raise Http404
-    
-
-def configSpecfem3DGlobeParameters(parameters, url, request, path, desktop):
-    from list_detail import object_detail
-    
-    objId = parameters.id # Ouch!
-    
-    view = gui.ChildWindow(url, "View")
-    edit = gui.ChildWindow(url + "edit/", "Edit")
-    properties = gui.ChildWindow(url + "properties/", "Properties")
-    menuBar = [
-        gui.Menu("actionMenu", "actions", "Actions",
-                 [gui.MenuItem(url + "delete/", "Delete"),
-                  ]
-                 ),
-        ]
-    view.menuBar = edit.menuBar = properties.menuBar = menuBar
-    edit.buttons.append(helpButton(request))
-    desktop.activeWindow.insertWindow(view)
-    desktop.activeWindow.insertWindow(edit)
-    desktop.activeWindow.insertWindow(properties)
-    
-    if not path:
-        return object_detail(request, desktop, view, Specfem3DGlobeParameters.objects.all(), object_id = objId)
-    
-    name = path.pop(0)
-    if path: raise Http404
-
-    if name == "edit":
-        return update_object(request,
-                             desktop,
-                             edit,
-                             Specfem3DGlobeParameters,
-                             object_id = objId,
-                             post_save_redirect = url,
-                             follow = { 'fsNode': False },
-                             )
-    if name == "properties":
-        objId = parameters.fsNode.id # Ouch!
-        return update_object(request,
-                             desktop,
-                             properties,
-                             FSNode,
-                             objId,
-                             post_save_redirect = url,
-                             follow = FSNode.follow,
-                             )
-    if name == "delete":
-        obj = get_object_or_404(Specfem3DGlobeParameters, id=objId)
-        return moveObjectToTrash(obj, request, config.root + "/welcome/")
-
-    raise Http404
-
-
 def par_file(request):
     from django.template import Context
 
     response = HttpResponse(mimetype='text/plain')
     #response['Content-Disposition'] = 'attachment; filename=parameters.xml'
 
-    parameters = Specfem3DGlobeParameters.objectFromSnapshot(request.parameters)
+    parameters = models.Specfem3DGlobeParameters.objectFromSnapshot(request.parameters)
     parameters.record_length = request.record_length
     
     t = loader.get_template('SeismoWebPortal/par_file.txt')
@@ -575,7 +551,7 @@
     response = HttpResponse(mimetype='text/xml')
     #response['Content-Disposition'] = 'attachment; filename=parameters.xml'
 
-    parameters = Specfem3DGlobeParameters.objectFromSnapshot(request.parameters)
+    parameters = models.Specfem3DGlobeParameters.objectFromSnapshot(request.parameters)
     parameters.record_length = request.record_length
 
     t = loader.get_template('SeismoWebPortal/parameters.pml')
@@ -619,7 +595,7 @@
             help_login_hook(request, user)
             # Light security check -- make sure redirect_to isn't garbage.
             if not redirect_to or '://' in redirect_to or ' ' in redirect_to:
-                redirect_to = postLoginURL(user)
+                redirect_to = config.root + "/"
             return HttpResponseRedirect(redirect_to)
     else:
         errors = {}
@@ -677,24 +653,18 @@
     desktop.activeWindow.selectWindow(reset)
     return desktop
 
-def postLoginURL(user):
-    q = EventWorkspace.objects.filter(fsNode__owner = user, fsNode__inTrash = False)
-    if q.count() == 0:
-        return "%s/welcome/" % config.root
-    return "%s/welcome/" % config.root
-
 def event_txt(request):
 
-    parameters = Specfem3DGlobeParameters.objectFromSnapshot(request.parameters)
+    parameters = models.Specfem3DGlobeParameters.objectFromSnapshot(request.parameters)
 
     sourceList = []
     if request.event < 0:
-        event = Event.objects.get(id = -request.event)
+        event = models.Event.objects.get(id = -request.event)
         for source in event.source_set.all():
-            sourceList.append(CMTSolution.createFromDBModel(source))
+            sourceList.append(cmt.CMTSolution.createFromDBModel(source))
     else:
-        cmt = CMTSolutionModel.objects.get(id = request.event)
-        sourceList.append(CMTSolution().initFromCMTSolutionModel(cmt))
+        cmt = models.CMTSolution.objects.get(id = request.event)
+        sourceList.append(cmt.CMTSolution().initFromCMTSolutionModel(cmt))
     
     response = HttpResponse(mimetype='text/plain')
 
@@ -736,7 +706,7 @@
 def stationlist_detail_txt(request, object_id):
     response = HttpResponse(mimetype='text/plain')
 
-    l = get_object_or_404(StationList, id=object_id)
+    l = get_object_or_404(models.StationList, id=object_id)
     stations = l.station_set.all()
 
     write_stations(stations, response)
@@ -755,7 +725,7 @@
     if name == "list.py":
         if path: raise Http404
         return object_list(request,
-                           queryset = Run.objects.all(),
+                           queryset = models.Run.objects.all(),
                            allow_empty = True,
                            template_name = "SeismoWebPortal/run_list.py",
                            mimetype = "text/plain")
@@ -765,7 +735,7 @@
     name = path.pop(0)
     if path: raise Http404
     if name == "status.html":
-        run = get_object_or_404(Run, id=objId)
+        run = get_object_or_404(models.Run, id=objId)
         return HttpResponse(runOutputStatus(run, innerHTML=True), mimetype='text/html')
     if name != "status": raise Http404
     return update_run_status(request, object_id = objId)
@@ -815,7 +785,7 @@
 
     manipulator = RunStatusManipulator()
 
-    run = get_object_or_404(Run, id=object_id)
+    run = get_object_or_404(models.Run, id=object_id)
 
     if request.method == 'POST':
         response = HttpResponse(mimetype='text/plain')
@@ -898,7 +868,7 @@
 
 class JobProgressManipulator(forms.Manipulator):
     def __init__(self, object_id, **kwds):
-        self.job = get_object_or_404(Job, id = object_id)
+        self.job = get_object_or_404(models.Job, id = object_id)
         self.fields = [
             forms.FloatField('progress', max_digits=19, decimal_places=10, is_required=True),
             ]
@@ -913,15 +883,15 @@
 def jobs(request, path, desktop):
     follow = {'progress': False}
     return daemon_post(request, path,
-                       (Job.AddManipulator, "SeismoWebPortal/job_form.html", follow),
-                       update = (Job.ChangeManipulator, "SeismoWebPortal/job_form.html", follow),
+                       (models.Job.AddManipulator, "SeismoWebPortal/job_form.html", follow),
+                       update = (models.Job.ChangeManipulator, "SeismoWebPortal/job_form.html", follow),
                        progress = (JobProgressManipulator, "SeismoWebPortal/job_progress_form.html", None),
                        )
 
 def output(request, path, desktop):
     return daemon_post(request, path,
-                       (OutputFile.AddManipulator, "SeismoWebPortal/outputfile_form.html", None),
-                       update = (OutputFile.ChangeManipulator, "SeismoWebPortal/outputfile_form.html", None),
+                       (models.OutputFile.AddManipulator, "SeismoWebPortal/outputfile_form.html", None),
+                       update = (models.OutputFile.ChangeManipulator, "SeismoWebPortal/outputfile_form.html", None),
                        )
 
 def daemon_post(request, path, (AddManipulator, add_template, add_follow), **kwds):
@@ -966,6 +936,7 @@
 
 
 def registration(request, path, desktop):
+    from forms import RegistrationAddManipulator
 
     _, _, appWindow = openPublicApps(request, desktop)
     desktop.selectWindow(appWindow)
@@ -998,8 +969,7 @@
                 request.session.delete_test_cookie()
                 help_login_hook(request, user)
                 notify_managers_of_new_user(request, user)
-                user.message_set.create(message="Welcome to the CIG Seismology Web Portal!")
-                return HttpResponseRedirect(config.root + "/welcome/")
+                return HttpResponseRedirect(config.root + "/")
     else:
         # Populate new_data with a 'flattened' version of the current data.
         new_data = manipulator.flatten_data()
@@ -1017,6 +987,8 @@
 
 
 def profile(request, path, desktop):
+    from forms import RegistrationChangeManipulator
+    
     appWindow = gui.AppWindow(config.root + "/profile/", "Profile", Memento('profile', request.session))
     desktop.insertWindow(appWindow)
     desktop.selectWindow(appWindow)
@@ -1115,7 +1087,7 @@
     return desktop
 
 
-def config_specfem3dglobe_meshes(url, request, path, desktop):
+def config_specfem3dglobe_meshes(url, request, path, desktop, post_save_hook):
     if not path:
         raise Http404
     
@@ -1127,7 +1099,8 @@
         nchunks = intOr404(name)
         if path: raise Http404
         window = gui.ChildWindow(url + "config/specfem3dglobe/meshes/new/%d/" % nchunks, "New")
-        return manipulate_mesh(request, desktop, window, nchunks = nchunks, action='new')
+        return manipulate_mesh(request, desktop, window, nchunks = nchunks, action='new',
+                               post_save_hook=post_save_hook)
     
     raise Http404
 
@@ -1160,23 +1133,24 @@
         return update_object(request,
                              desktop,
                              properties,
-                             FSNode,
+                             models.FSNode,
                              objId,
                              post_save_redirect = url,
-                             follow = FSNode.follow,
+                             follow = models.FSNode.follow,
                              )
     if name == "delete":
         if path: raise Http404
-        return moveObjectToTrash(mesh, request, config.root + "/welcome/")
+        return moveObjectToTrash(mesh, request, config.root + "/")
 
     raise Http404
 
 
-def manipulate_mesh(request, desktop, window, action=None, nchunks=None, object_id=None):
+def manipulate_mesh(request, desktop, window, action=None, nchunks=None, object_id=None,
+                    post_save_hook=None):
     from forms import MeshAddManipulator, MeshChangeManipulator
     
     if nchunks is None:
-        mesh = get_object_or_404(Specfem3DGlobeMesh, id=object_id)
+        mesh = get_object_or_404(models.Specfem3DGlobeMesh, id=object_id)
         nchunks = mesh.nchunks
     else:
         mesh = None
@@ -1215,9 +1189,10 @@
             errors = manipulator.get_validation_errors(new_data)
             if not errors:
                 manipulator.do_html2python(new_data)
-                manipulator.save(new_data)
-                if not errors:
-                    return HttpResponseRedirect(config.root + '/welcome/')
+                obj = manipulator.save(new_data)
+                if post_save_hook:
+                    post_save_hook(obj)
+                return HttpResponseRedirect(config.root + '/')
     else:
         # Populate new_data with a 'flattened' version of the current data.
         new_data = manipulator.flatten_data()
@@ -1245,7 +1220,7 @@
 
 
 def create(request, desktop, window, ModelClass, **kwds):
-    post_save_redirect = kwds.pop('post_save_redirect', config.root + '/welcome/')
+    post_save_redirect = kwds.pop('post_save_redirect', config.root + '/')
     return create_object(request,
                          desktop,
                          window,
@@ -1257,7 +1232,7 @@
 
 
 def update(request, desktop, window, object_id, ModelClass, **kwds):
-    post_save_redirect = kwds.pop('post_save_redirect', config.root + '/welcome/')
+    post_save_redirect = kwds.pop('post_save_redirect', config.root + '/')
     return update_object(request,
                          desktop,
                          window,
@@ -1274,7 +1249,7 @@
     if not fsNode.inTrash:
         fsNode.inTrash = True
         fsNode.oldParent = fsNode.parent
-        fsNode.parent = Folder.trashFolder(request.user).fsNode
+        fsNode.parent = models.Folder.trashFolder(request.user).fsNode
         fsNode.save()
     return HttpResponseRedirect(postTrashRedirect)
 
@@ -1325,46 +1300,54 @@
     return view, edit
 
 
-def openEventViewers(request, desktop):
-    desktop.eventViewers = {}
-    for workspace in EventWorkspace.objects.filter(fsNode__owner = request.user, fsNode__inTrash = False):
+def openWorkspaces(request, desktop):
+    desktop.workspaces = {}
+    
+    qs = models.Workspace.objects.filter(fsNode__owner = request.user, fsNode__inTrash = False)
+    
+    if qs.count() == 0:
+        models.Workspace.objects.create(
+            stations = models.StationList.favorite(),
+            zero_half_duration = True,
+            )
+        
+    for workspace in qs:
         id = workspace.fsNode.id
         url = "%s/%d/" % (config.root, id)
         navtree = gui.Directory(str(id), str(workspace), [])
-        appWindow = gui.EventViewer(url, workspace.fsNode.name, Memento('eventWorkspace_%d' % id, request.session),
-                                    navtree, url, workspace)
+        appWindow = gui.Workspace(url, workspace.fsNode.name, Memento('workspace_%d' % id, request.session),
+                                  navtree, url, workspace)
         desktop.insertWindow(appWindow)
-        desktop.eventViewers[workspace.id] = appWindow
+        desktop.workspaces[workspace.id] = appWindow
     return
 
 
-def eventViewer(workspace, url, request, path, desktop):
+def workspace(workspace, url, request, path, desktop):
 
-    event = workspace.event
+    if False:
 
-    if event.singleSource:
-        sources = gui.File("sources", "Source Mechanism", url = url + "sources/")
-    else:
-        sources = gui.Directory("sources", "Sources", [], url = url + "sources/")
+        event = workspace.event
 
-    appWindow = desktop.eventViewers[workspace.id]
+        if event.singleSource:
+            sources = gui.File("sources", "Source Mechanism", url = url + "sources/")
+        else:
+            sources = gui.Directory("sources", "Sources", [], url = url + "sources/")
+
+    appWindow = desktop.workspaces[workspace.id]
     appWindow.root.appendNodes([
         gui.Directory("events", "Events", [gui.Search("search", "Search", url = url + "events/"),
                                            ]),
-        ###
-        gui.Directory("home", "Home", []),
-        gui.Directory("shared", "Shared", []),
         ####
         gui.Directory("stations", "Stations", []),
-        gui.Directory("1dmodels", "1D Models", []),
         gui.Directory("1dparameters", "1D Parameters", []),
-        gui.Directory("3dmeshes", "3D Meshes", []),
-        gui.Directory("3dmodels", "3D Models", []),
         gui.Directory("3dparameters", "3D Parameters", []),
+        gui.Directory("1dmodels", "1D Models", []),
+        gui.Directory("1dmodes", "1D Mode Catalogs", []),
+        gui.Directory("3dmeshes", "3D Meshes", []),
         gui.Directory("trash", "Trash", []),
         ####
         gui.File("seismograms", "Seismograms"),
-        sources,
+        #sources,
         gui.Directory("maps", "Maps", [], url = url + "maps/"),
         gui.File("settings", "Settings", url = url + "settings/"),
         ])
@@ -1407,10 +1390,10 @@
         return update_object(request,
                              desktop,
                              settings,
-                             EventWorkspace,
+                             models.Workspace,
                              workspace.id, # Ouch!
                              post_save_redirect = url,
-                             follow = EventWorkspace.follow,
+                             follow = models.Workspace.follow,
                              )
     if name == "properties":
         desktop.activeWindow.insertWindow(view)
@@ -1418,24 +1401,27 @@
         return update_object(request,
                              desktop,
                              properties,
-                             FSNode,
+                             models.FSNode,
                              workspace.fsNode.id, # Ouch!
                              post_save_redirect = url,
-                             follow = FSNode.follow,
+                             follow = models.FSNode.follow,
                              )
  
     index = Index({
-        "stations": StationList,
-        "1dmodels": MineosModel,
-        "1dparameters": MineosParameters,
-        "3dmeshes": Specfem3DGlobeMesh,
-        "3dmodels": Specfem3DGlobeModel,
-        "3dparameters": Specfem3DGlobeParameters,
+        "stations": models.StationList,
+        "1dmodels": models.MineosModel,
+        "1dmodes": models.MineosModeCatalog,
+        "1dparameters": models.MineosParameters,
+        "3dmeshes": models.Specfem3DGlobeMesh,
+        "3dparameters": models.Specfem3DGlobeParameters,
         })
     ModelClass = index.get(name)
     if ModelClass:
-        return folderView(Folder.sharedFolder(), name, workspace, url + name + "/", request, path, appWindow, desktop,
-                          ModelClass)
+        folderIcon = appWindow.root.index[name]
+        appWindow.root.setAnchor(name)
+        appWindow.path.append(folderIcon)
+        return configTable(workspace, url + name + "/", request, path, appWindow, desktop,
+                           ModelClass)
 
     if name == "home":
         return homeFolder(workspace, url, request, path, appWindow, desktop)
@@ -1445,11 +1431,12 @@
     if name == "trash":
         return trashFolder(workspace, url, request, path, appWindow, desktop)
     if name == "delete":
-        return moveObjectToTrash(workspace, request, config.root + "/welcome/")
+        return moveObjectToTrash(workspace, request, config.root + "/")
 
-    response = eventFiles(name, event, request)
-    if response:
-        return response
+    if False:
+        response = eventFiles(name, event, request)
+        if response:
+            return response
 
     raise Http404
 
@@ -1465,7 +1452,7 @@
     desktop.activeWindow.insertWindow(window)
 
     parameterClasses = []
-    for ParameterClass in [MineosParameters, Specfem3DGlobeParameters]:
+    for ParameterClass in [models.MineosParameters, models.Specfem3DGlobeParameters]:
         if not ParameterClass.canHandleEvent(event):
             continue
         parameterClasses.append(ParameterClass)
@@ -1474,12 +1461,12 @@
     requests = {}
     
     for ParameterClass in parameterClasses:
-        parameterSetList[ParameterClass] = [ps for ps in ParameterClass.objects.filter(userFSChoices)]
+        parameterSetList[ParameterClass] = [ps for ps in ParameterClass.objects.filter(models.userFSChoices)]
         requests[ParameterClass.code] = []
         
-    for request in Request.objects.filter(event = requestEventId(event),
-                                          stations = workspace.stations,
-                                          record_length__gte = workspace.record_length):
+    for request in models.Request.objects.filter(event = requestEventId(event),
+                                                 stations = workspace.stations,
+                                                 record_length__gte = workspace.record_length):
         requests[request.code].append(request)
 
     assist = True
@@ -1499,8 +1486,8 @@
     c = Context({'root': config.root,
                  'assist': assist,
                  'workspace': workspace,
-                 'mineos_parameter_set_list': parameterSetList.get(MineosParameters),
-                 'specfem3dglobe_parameter_set_list': parameterSetList.get(Specfem3DGlobeParameters),
+                 'mineos_parameter_set_list': parameterSetList.get(models.MineosParameters),
+                 'specfem3dglobe_parameter_set_list': parameterSetList.get(models.Specfem3DGlobeParameters),
                  })
     window.content = gui.StaticContent(t.render(c))
     desktop.activeWindow.selectWindow(window)
@@ -1527,19 +1514,19 @@
         # input file downloads
         
         objId = intOr404(name)
-        request = get_object_or_404(Request, id=objId)
+        request = get_object_or_404(models.Request, id=objId)
         if not path: raise Http404
         name = path.pop(0)
         if path: raise Http404
 
-        if request.code == Specfem3DGlobeParameters.code:
+        if request.code == models.Specfem3DGlobeParameters.code:
             index = Index({
                 "par_file.txt": par_file,
                 "parameters.pml": parameters_pml,
                 "stations.txt": stations_txt,
                 "event.txt": event_txt,
                 })
-        elif request.code == MineosParameters.code:
+        elif request.code == models.MineosParameters.code:
             index = Index({
                 "parameters.pml": mineos_parameters_pml,
                 "event.txt": mineos_event_txt,
@@ -1557,16 +1544,16 @@
 
     if path or request.method != 'POST': raise Http404
     
-    workspace = EventWorkspace.objects.get(id = int(request.POST['workspace']))
+    workspace = models.Workspace.objects.get(id = int(request.POST['workspace']))
     code = int(request.POST['code'])
-    if code == Specfem3DGlobeParameters.code:
-        parameters = Specfem3DGlobeParameters.objects.get(id = int(request.POST['parameters']))
-    elif code == MineosParameters.code:
-        parameters = MineosParameters.objects.get(id = int(request.POST['parameters']))
+    if code == models.Specfem3DGlobeParameters.code:
+        parameters = models.Specfem3DGlobeParameters.objects.get(id = int(request.POST['parameters']))
+    elif code == models.MineosParameters.code:
+        parameters = models.MineosParameters.objects.get(id = int(request.POST['parameters']))
     else:
         raise Http404
 
-    req = Request()
+    req = models.Request()
     event = workspace.event
     req.event = requestEventId(event)
     req.stations = workspace.stations
@@ -1574,13 +1561,13 @@
 
     parameterData = parameters.snapshot(workspace)
     req.code = code
-    req.parameterData = Request.encodeParameters(parameterData)
+    req.parameterData = models.Request.encodeParameters(parameterData)
 
     req.requestor = request.user
 
     req.save()
 
-    Run.objects.create(
+    models.Run.objects.create(
         request = req,
         status = "new",
         )
@@ -1624,7 +1611,7 @@
             desktop,
             view,
             object_id = event.id,
-            queryset = Event.objects.all(),
+            queryset = models.Event.objects.all(),
             extra_context = {'title': workspace.fsNode.name }
             )
 
@@ -1641,7 +1628,6 @@
 
 
 def configSource(name, event, eventUrl, request, path, desktop):
-    from models import Source
     from list_detail import object_detail
 
     appWindow = desktop.activeWindow
@@ -1649,7 +1635,7 @@
     objId = intOr404(name)
     url = eventUrl + "%d/" % objId
 
-    source = get_object_or_404(Source, id=objId) # Ouch!
+    source = get_object_or_404(models.Source, id=objId) # Ouch!
     sourcesFolder = appWindow.root.index['sources']
     navIcon = gui.File(name, source.eventName, url = url)
     sourcesFolder.appendNode(navIcon)
@@ -1671,7 +1657,7 @@
         appWindow.insertWindow(edit)
 
     if not path:
-        return object_detail(request, desktop, view, object_id = objId, queryset = Source.objects.all())
+        return object_detail(request, desktop, view, object_id = objId, queryset = models.Source.objects.all())
 
     name = path.pop(0)
     if path: raise Http404
@@ -1681,7 +1667,7 @@
             request,
             desktop,
             edit,
-            Source,
+            models.Source,
             object_id = objId,
             post_save_redirect = url,
             )
@@ -1689,7 +1675,7 @@
         return delete_object(
             request,
             desktop,
-            Source,
+            models.Source,
             object_id = objId,
             post_delete_redirect = eventUrl,
             )
@@ -1702,24 +1688,24 @@
 
 
 def eventMaps(workspace, url, request, path, appWindow, desktop):
-    event = workspace.event
     mapsFolder = appWindow.root.index['maps']
     appWindow.path.append(mapsFolder)
     url += "maps/"
 
     if not path:
-        return  HttpResponseRedirect(url + "global/")
+        return  HttpResponseRedirect(url + "stations/")
     
     name = path.pop(0)
     if path or not name in ["global", "stations"]:
         raise Http404
 
-    mapsFolder.appendNode(gui.File("global", "Global", url = url + "global/"))
     mapsFolder.appendNode(gui.File("stations", "Stations", url = url + "stations/"))
+    mapsFolder.appendNode(gui.File("global", "Global", url = url + "global/"))
     appWindow.path.append(mapsFolder.index[name])
     child = gui.ChildWindow(url + name + "/", "View")
 
     if name == "global":
+        event = workspace.event
         mapRoot = "%s/maps/events/%d/stations/%d/global" % (config.root, event.firstSource.id, workspace.stations.id)
         imgList = [img("%s/%s" % (mapRoot, jpg), width=506, height=506)
                    for jpg in ["event.jpg", "antipode.jpg"]]
@@ -1767,14 +1753,14 @@
         # The try-get()-except shouldn't be necessary, but simply
         # using save() causes Django to die in the database backend.
         try:
-            ds = DataSource.objects.get(name=event.dataSource)
-        except DataSource.DoesNotExist:
-            ds = DataSource(event.dataSource)
+            ds = models.DataSource.objects.get(name=event.dataSource)
+        except models.DataSource.DoesNotExist:
+            ds = models.DataSource(event.dataSource)
             ds.save()
         try:
-            ds = Region.objects.get(name=event.regionName)
+            ds = models.Region.objects.get(name=event.regionName)
         except:
-            r = Region(event.regionName)
+            r = models.Region(event.regionName)
             r.save()
 
     resultsURL = request.get_full_path()
@@ -1893,7 +1879,7 @@
     return desktop
 
 def event_detail_gearth(request, object_id):
-    event = get_object_or_404(Event, id=object_id)
+    event = get_object_or_404(models.Event, id=object_id)
     return gearth_object_list(request,
                               queryset = event.source_set.all(),
                               template_name = 'SeismoWebPortal/event_detail_gearth.kml',
@@ -1902,8 +1888,7 @@
 
 
 def cmtsolution_txt(request, object_id):
-    from models import Source
-    source = get_object_or_404(Source, id=object_id)
+    source = get_object_or_404(models.Source, id=object_id)
     response = HttpResponse(mimetype='text/plain')
     response.write(source.cmtSolutionText())
     return response
@@ -1914,7 +1899,7 @@
 
     count = event.source_set.count()
     for event in event.source_set.all():
-        cmtSolution = CMTSolution.createFromDBModel(event)
+        cmtSolution = cmt.CMTSolution.createFromDBModel(event)
         response.write(str(cmtSolution))
 
     return response
@@ -1958,7 +1943,7 @@
             elif data.startswith('CMT search error'):
                 self.state = -1
         elif self.state == 3:
-            self.cmtList = CMTSolution.parse(data)
+            self.cmtList = cmt.CMTSolution.parse(data)
         elif self.state == -2:
             self.error = data
             self.state = -3
@@ -1999,22 +1984,22 @@
     if path: raise Http404
 
     if name == "list":
-        return object_detail(request, desktop, list, StationList.objects.all(), object_id = objId)
+        return object_detail(request, desktop, list, models.StationList.objects.all(), object_id = objId)
 
     if name == "properties":
         objId = stationList.fsNode.id # Ouch!
         return update_object(request,
                              desktop,
                              properties,
-                             FSNode,
+                             models.FSNode,
                              objId,
                              post_save_redirect = url,
-                             follow = FSNode.follow,
+                             follow = models.FSNode.follow,
                              )
     
     if name == "delete":
-        obj = get_object_or_404(StationList, id=objId)
-        return moveObjectToTrash(obj, request, config.root + "/welcome/")
+        obj = get_object_or_404(models.StationList, id=objId)
+        return moveObjectToTrash(obj, request, config.root + "/")
 
     index = Index({
         "stations.txt": stationlist_detail_txt,
@@ -2025,7 +2010,7 @@
 
 
 def stationlist_detail_gearth(request, object_id):
-    stationList = get_object_or_404(StationList, id=object_id)
+    stationList = get_object_or_404(models.StationList, id=object_id)
     kwds = dict(queryset = stationList.station_set.all(),
                 extra_context = {'name': stationList.fsNode.name})
     return station_list_gearth(request, **kwds)
@@ -2056,20 +2041,20 @@
             continue
 
         # get/create the network entry
-        network, created = StationNetwork.objects.get_or_create(code = network, defaults = { 'name': "" })
+        network, created = models.StationNetwork.objects.get_or_create(code = network, defaults = { 'name': "" })
 
         # get/create location
-        location = Location.getLocation(latitude, longitude)
+        location = models.Location.getLocation(latitude, longitude)
 
         # create the station
-        station = Station.objects.create(stationList = stationList,
-                                         code = code,
-                                         name = "",
-                                         network = network,
-                                         status = 1,
-                                         location = location,
-                                         elevation = elevation,
-                                         bur = bur)
+        station = models.Station.objects.create(stationList = stationList,
+                                                code = code,
+                                                name = "",
+                                                network = network,
+                                                status = 1,
+                                                location = location,
+                                                elevation = elevation,
+                                                bur = bur)
     return
 
 
@@ -2098,7 +2083,7 @@
     def save(self, new_data, user):
 
         # Create the new station list.
-        stationList = StationList.objects.create()
+        stationList = models.StationList.objects.create()
         stationList.fsNode.name = new_data['stations']['filename']
         stationList.fsNode.save()
         
@@ -2194,7 +2179,7 @@
 
     response = HttpResponse(mimetype='text/xml')
 
-    parameters = MineosParameters.objectFromSnapshot(request.parameters)
+    parameters = models.MineosParameters.objectFromSnapshot(request.parameters)
     parameters.record_length = request.record_length
 
     t = loader.get_template('SeismoWebPortal/mineos_parameters.pml')
@@ -2212,10 +2197,10 @@
     if request.event < 0:
         raise Http404
 
-    cmt = CMTSolutionModel.objects.get(id = request.event)
-    cmt = CMTSolution().initFromCMTSolutionModel(cmt)
+    cmt = models.CMTSolution.objects.get(id = request.event)
+    cmt = cmt.CMTSolution().initFromCMTSolutionModel(cmt)
 
-    parameters = MineosParameters.objectFromSnapshot(request.parameters)
+    parameters = models.MineosParameters.objectFromSnapshot(request.parameters)
 
     logs = [int(log10(fabs(x))) for x in (cmt.Mrr, cmt.Mtt, cmt.Mpp, cmt.Mrt, cmt.Mrp, cmt.Mtp)]
     biggestLog = max(logs)
@@ -2288,19 +2273,11 @@
 
 def mineos_model_txt(request):
     response = HttpResponse(mimetype='text/plain')
-    parameters = MineosParameters.objectFromSnapshot(request.parameters)
+    parameters = models.MineosParameters.objectFromSnapshot(request.parameters)
     response.write(parameters.model.data)
     return response
 
 
-def config_mineos_models(url, request, path, desktop):
-    return uploadOnly(request, path, desktop,
-                      UploadMineosModelManipulator(),
-                      url,
-                      url + "config/mineos/models/upload/",
-                      "SeismoWebPortal/mineosmodel_upload.html")
-
-
 class UploadMineosModelManipulator(forms.Manipulator):
     
     def __init__(self):
@@ -2331,7 +2308,7 @@
         #parseMineosModel(None, stream)
 
         # Create the new model.
-        model = MineosModel.objects.create(data = content)
+        model = models.MineosModel.objects.create(data = content)
         model.fsNode.name = new_data['model']['filename']
         model.fsNode.save()
 
@@ -2406,11 +2383,11 @@
         if path:
             name = path.pop(0)
             objId = intOr404(name)
-            obj = get_object_or_404(Request, id = objId)
+            obj = get_object_or_404(models.Request, id = objId)
             url += "%d/" % objId
 
             if request.method == 'POST':
-                Run.objects.create(
+                models.Run.objects.create(
                     request = obj,
                     status = "new",
                     )
@@ -2452,7 +2429,7 @@
         print >>html, '<tr><th>id</th><th>requestor</th><th>code</th><th>status</th><th>output</th></tr>'
         print >>html, '</thead>'
         print >>html, '<tbody>'
-        for i, obj in enumerate(Request.objects.all().order_by('-id')):
+        for i, obj in enumerate(models.Request.objects.all().order_by('-id')):
             id = '<a href="%s%d/">%04d</a>' % (url, obj.id, obj.id)
             print >>html, '<tr class=%s><td>%s</td><td>%s%s</td><td>%s</td><td>%s</td><td>%s</td>' % (
                 (i % 2 and 'even' or 'odd'), id, userIcon, obj.requestor.username, CODE_CHOICES[obj.code-1][1],



More information about the cig-commits mailing list