Changeset 4658


Ignore:
Timestamp:
16/12/08 12:05:09 (11 years ago)
Author:
sdonegan
Message:

This tagged release contains an updated version of revision 3249 currently stable on Proglue (note patched discovery service endpoint & email in discovery.py

NOTE: discovery.ini & ndgDiscovery.conf configured for localhost on SJD PC!

Location:
TI05-delivery/ows_framework/tags/ows_server@3249_ProGlue_Stable/ows_server@3249
Files:
8 edited
1 copied

Legend:

Unmodified
Added
Removed
  • TI05-delivery/ows_framework/tags/ows_server@3249_ProGlue_Stable/ows_server@3249/development.ini

    r3027 r4658  
    1313use = egg:Paste#http 
    1414host = 0.0.0.0 
    15 port = 8080 
     15port = 8081 
    1616 
    1717[pipeline:main] 
     
    5757# execute malicious code after an exception is raised. 
    5858#set debug = false 
     59 
     60############################################################################# 
     61# Logging configuration 
     62[loggers] 
     63keys = root, ows_server 
     64 
     65[handlers] 
     66keys = console 
     67 
     68[formatters] 
     69keys = generic 
     70 
     71[logger_root] 
     72level = INFO 
     73handlers = console 
     74 
     75[logger_ows_server] 
     76level = DEBUG 
     77handlers = 
     78qualname = ows_server 
     79 
     80[handler_console] 
     81class = StreamHandler 
     82args = (sys.stderr,) 
     83level = NOTSET 
     84formatter = generic 
     85 
     86[formatter_generic] 
     87format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s 
     88datefmt = %H:%M:%S 
  • TI05-delivery/ows_framework/tags/ows_server@3249_ProGlue_Stable/ows_server@3249/ndgDiscovery.config

    r3119 r4658  
    1111# the following is the server on which this browse/discovery instance runs! 
    1212#server:         http://localhost 
    13 server:         http://localhost:8080 
     13server:         http://localhost:8081 
    1414 
    1515# 
     
    2222# this is the physical file location of the layout directory on this machine  
    2323#  
    24 layoutdir:      /home/bnl/sandboxes/ndg/TI07-MOLES/trunk/PythonCode/wsgi/ 
     24#layoutdir:      /home/bnl/sandboxes/ndg/TI07-MOLES/trunk/PythonCode/wsgi/ 
     25#layoutdir:     D:\workspace\TI-07 
    2526# 
    2627# this should never be changed 
     
    3031# 
    3132mailserver:       outbox.rl.ac.uk 
    32 metadataMaintainer: b.n.lawrence@rl.ac.uk 
     33metadataMaintainer: s.j.donegan@rl.ac.uk 
    3334repository:       http://localhost:8080 
    34 tbrecipient:      b.n.lawrence@rl.ac.uk 
     35tbrecipient:      s.j.donegan@rl.ac.uk 
    3536 
    3637# The following should only be needed for debugging some parts of the code when running on sandboxes behind a firewall 
  • TI05-delivery/ows_framework/tags/ows_server@3249_ProGlue_Stable/ows_server@3249/ows_server/controllers/discovery.py

    r3249 r4658  
    88from ows_server.models.DIF import DIF 
    99from ows_server.templates.renderDiscoverySet import renderDiscoverySet 
    10 from ows_server.models.DiscoveryState import DiscoveryState,constraints 
     10from ows_server.models.DiscoveryState import DiscoveryState, constraints 
    1111from ows_server.models.ndgObject import ndgObject 
    1212from ows_server.models.Utilities import myConfig 
     
    1515debug=0 
    1616 
     17import logging 
     18log = logging.getLogger(__name__) 
    1719 
    1820class DiscoveryController(BaseController): 
     
    2224        ''' Common setup for controller methods ''' 
    2325        self.cf=request.environ['ndgConfig'] 
    24         self.exist=(self.cf.get('NDG_EXIST','local'),self.cf.get('NDG_EXIST','passwordFile')) 
     26        self.exist=(self.cf.get('NDG_EXIST', 'local'), self.cf.get('NDG_EXIST', 'passwordFile')) 
    2527        self.inputs=dict(parse_querystring(request.environ)) 
    2628        self.message='' 
     
    3840         
    3941        #the following need to be defined 
    40         continuations={'start':1,'howmany':10} 
     42        continuations={'start':1, 'howmany':10} 
    4143        for i in continuations: 
    4244            if i not in self.inputs: self.inputs[i]=continuations[i] 
     
    5355             
    5456        # the next simplest is one that includes texttarget as well ... 
    55         expected=['searchString','textTarget','start','howmany','searchTarget'] 
     57        expected=['searchString', 'textTarget', 'start', 'howmany', 'searchTarget'] 
    5658        self.__checkform(expected) 
    5759     
     
    8082                scope=None 
    8183                 
    82             expected=['bboxN','bboxE','bboxS','bboxW','geoSearchType'] 
     84            expected=['bboxN', 'bboxE', 'bboxS', 'bboxW', 'geoSearchType'] 
    8385            self.__checkform(expected) 
    8486            if self.message!='':  
     
    8789            else: 
    8890                # default form has a global bounding box, NB, internal to this routine we use bbox=[N,W,E,S], not [W,S,E,N]! 
    89                 bbox=[self.inputs['bboxN'],self.inputs['bboxW'],self.inputs['bboxE'],self.inputs['bboxS']] 
     91                bbox=[self.inputs['bboxN'], self.inputs['bboxW'], self.inputs['bboxE'], self.inputs['bboxS']] 
    9092                
    9193                self.__checkbox(bbox) 
     
    9597                     
    9698             
    97             expected=['startDateDay','startDateMon','startDateYear', 
    98                             'endDateDay','endDateMon','endDateYear'] 
     99            expected=['startDateDay', 'startDateMon', 'startDateYear',  
     100                            'endDateDay', 'endDateMon', 'endDateYear'] 
    99101            self.__checkform(expected) 
    100102            if self.message!='':  
     
    103105            else: 
    104106                try: 
    105                     dateRange=[(self.inputs['startDateDay'],self.inputs['startDateMon'],self.inputs['startDateYear']), 
    106                                 (self.inputs['endDateDay'],self.inputs['endDateMon'],self.inputs['endDateYear'])] 
     107                    dateRange=[(self.inputs['startDateDay'], self.inputs['startDateMon'], self.inputs['startDateYear']),  
     108                                (self.inputs['endDateDay'], self.inputs['endDateMon'], self.inputs['endDateYear'])] 
    107109                    #default form has blanks, in which case we don't want to check for date range 
    108                     if dateRange<>[("","",""),("","","")]: 
     110                    if dateRange<>[("", "", ""), ("", "", "")]: 
    109111                        self.__checkdates(dateRange) 
    110112                    else: dateRange=None            
     
    116118         
    117119        if 'constrained' in self.inputs:  
    118             con=self.__buildconstraints(dateRange,bbox,scope,self.inputs['searchString'],self.inputs['geoSearchType']) 
     120            con=self.__buildconstraints(dateRange, bbox, scope, self.inputs['searchString'], self.inputs['geoSearchType']) 
    119121            return self.__advancedPrompt(searchConstraints=con) 
    120122        else: 
    121123            # ------------- ok, now go do the search ----------- 
    122             response=self.doText(self.inputs['searchString'],self.inputs['textTarget'], 
    123                 self.inputs['start'],self.inputs['howmany'],scope=scope,dateRange=dateRange,bbox=bbox, 
     124            response=self.doText(self.inputs['searchString'], self.inputs['textTarget'],  
     125                self.inputs['start'], self.inputs['howmany'], scope=scope, dateRange=dateRange, bbox=bbox,  
    124126                geoSearch=self.inputs['geoSearchType']) 
    125127            return response 
    126128 
    127     def doText(self,searchString,textTarget,start,howmany,scope=None,dateRange=None,bbox=None,geoSearch='overlaps'): 
     129    def doText(self, searchString, textTarget, start, howmany, scope=None, dateRange=None, bbox=None, geoSearch='overlaps'): 
    128130         
    129131        ''' Carry out a text search for <searchString>  
     
    131133        by the DiscoveryTemplate GUI, and are: All, Authors, Parameters ''' 
    132134         
    133         start,howmany=int(start),int(howmany)  # url arguments need conversion ... 
     135         
     136        start, howmany=int(start), int(howmany)  # url arguments need conversion ... 
    134137         
    135138        if self.inputs['searchTarget']=='Discovery': 
     139            log.debug('SELECTING Discovery WS') 
    136140            ws=NS.ndgSearch(logger=logger) 
    137         elif self.inputs['searchTarget'] in ['Browse','NumSim']: 
    138             ws=DR(self.exist[0],pwfile=self.exist[1]) 
     141        elif self.inputs['searchTarget'] in ['Browse', 'NumSim']: 
     142            log.debug('SELECTING Browse WS') 
     143            ws=DR(self.exist[0], pwfile=self.exist[1]) 
    139144            #overriding text target which is ignored currently ... yuck ... 
    140145            textTarget=self.inputs['searchTarget'] 
     
    144149            return render_response('error') 
    145150             
    146         documents=ws.search(searchString,start=start,howmany=howmany,target=textTarget, 
    147                             scope=scope,dateRange=dateRange,bbox=bbox,geoSearchType=geoSearch) 
     151        documents=ws.search(searchString, start=start, howmany=howmany, target=textTarget,  
     152                            scope=scope, dateRange=dateRange, bbox=bbox, geoSearchType=geoSearch) 
     153 
     154                     
    148155        if ws.error !=None: 
    149156            m='' 
     
    153160         
    154161        #build constraints info for report 
    155         searchConstraints=self.__buildconstraints(dateRange,bbox,scope,searchString,geoSearch) 
     162        searchConstraints=self.__buildconstraints(dateRange, bbox, scope, searchString, geoSearch) 
    156163         
    157164        hits=ws.hits 
     
    160167        if hits<howmany:howmany=hits 
    161168         
    162         c.state=DiscoveryState(id,searchString,request.environ,hits,searchConstraints,start,howmany) 
     169        c.state=DiscoveryState(id, searchString, request.environ, hits, searchConstraints, start, howmany) 
    163170        c.querystring=request.environ['QUERY_STRING'] 
    164171         
     
    175182            errors=[] 
    176183            for result in results:  
     184                                 
     185                #log.debug('SJD--1') 
    177186                obj=ndgObject(result[0]) 
     187                 
    178188                obj.setConfig(self.cf) 
     189                 
    179190                try: 
    180                     difs.append(DIF(result[1],ndgObj=obj)) 
    181                 except ValueError,e: 
    182                     errors.append((result[0],str(e))) 
     191                    d = DIF(result[1], ndgObj=obj) 
     192                    difs.append(d) 
     193                                        
     194                except ValueError, e: 
     195                    errors.append((result[0], str(e))) 
    183196            if results==[]: 
    184197                c.xml='<p> No results for "%s"!</p>'%searchString 
     
    198211                    else: 
    199212                        dp='[%s]'%dp[0]  
    200                     c.xml+=' (unfortunately %s hits matched unformattable documents from %s, an internal error has been logged):</p>'%(len(errors),dp) 
    201                     status,message=mailHandler(['b.n.lawrence@rl.ac.uk'],'DIF errors',str(errors), 
    202                                     server=self.cf.get('DEFAULT','mailserver')) 
     213                    c.xml+=' (unfortunately %s hits matched unformattable documents from %s, an internal error has been logged):</p>'%(len(errors), dp) 
     214                    status, message=mailHandler(['s.j.donegan@rl.ac.uk'], 'DIF errors', str(errors),  
     215                                    server=self.cf.get('DEFAULT', 'mailserver')) 
    203216                    if not status: 
    204217                        c.xml+='<p> Actually, not even an internal error has been logged. <br/>' 
     
    212225                session.save() 
    213226                if len(c.pageTabs)==1:  
    214                     c.pageTabs.append(('Results',session['results'])) 
     227                    c.pageTabs.append(('Results', session['results'])) 
    215228                elif c.pageTabs[1][0]!='Results': 
    216                         c.pageTabs.insert(1,('Results',session['results'])) 
     229                        c.pageTabs.insert(1, ('Results', session['results'])) 
    217230                return render_response('results') 
    218231                 
    219         except ValueError,e: 
     232        except ValueError, e: 
    220233            if debug:  
    221                 raise ValueError,str(e) 
    222             else: 
    223                 c.xml='<p> Error retrieving documents for %s hits is [%s]</p>'%(hits,e) 
     234                raise ValueError, str(e) 
     235            else: 
     236                c.xml='<p> Error retrieving documents for %s hits is [%s]</p>'%(hits, e) 
    224237                return render_response('content') 
    225         except Exception,e: 
    226                 c.xml='Unknown error %s,%s'%(str(Exception),e) 
     238        except Exception, e: 
     239                c.xml='Unknown error %s,%s'%(str(Exception), e) 
    227240                return render_response('error')                        
    228241         
    229     def __advancedPrompt(self,searchConstraints=None): 
     242    def __advancedPrompt(self, searchConstraints=None): 
    230243        ''' This provides the advanced search input page ''' 
    231244        try: 
    232             discoveryURL=self.cf.get('SEARCH','discoveryURL') 
    233             advancedURL=self.cf.get('SEARCH','advancedURL') 
     245            discoveryURL=self.cf.get('SEARCH', 'discoveryURL') 
     246            advancedURL=self.cf.get('SEARCH', 'advancedURL') 
    234247        except: 
    235248            return 'Error, invalid configuration for search interface' 
    236249        #defaults 
    237         c.bbox='90.0','-180.0','180.0','-90.0' 
    238         c.startDateDay,c.startDateMon,c.startDateYear='','','' 
    239         c.endDateDay,c.endDateMon,c.endDateYear='','','' 
     250        c.bbox='90.0', '-180.0', '180.0', '-90.0' 
     251        c.startDateDay, c.startDateMon, c.startDateYear='', '', '' 
     252        c.endDateDay, c.endDateMon, c.endDateYear='', '', '' 
    240253        c.textTarget='All' 
    241254        c.searchString='' 
     
    246259        if searchConstraints is not None: 
    247260            if searchConstraints['dateRange'] is not None: 
    248                 c.startDateDay,c.startDateMon,c.startDateYear=searchConstraints['dateRange'][0] 
    249                 c.endDateDay,c.endDateMon,c.endDateYear=searchConstraints['dateRange'][1] 
     261                c.startDateDay, c.startDateMon, c.startDateYear=searchConstraints['dateRange'][0] 
     262                c.endDateDay, c.endDateMon, c.endDateYear=searchConstraints['dateRange'][1] 
    250263            if searchConstraints['bbox'] is not None: 
    251264                c.bbox=searchConstraints['bbox'] 
     
    260273        return render_response('advanced') 
    261274         
    262     def __checkbox(self,bbox): 
     275    def __checkbox(self, bbox): 
    263276        m='Invalid bounding box dimensions entered - limits are ' 
    264277        if float(bbox[0])>90.0 or float(bbox[3])<-90.: 
     
    268281            self.message=self.message[:-1]+' -180 (W), 180 (E)!' 
    269282             
    270     def __checkform(self,expected): 
     283    def __checkform(self, expected): 
    271284        ''' Simply checks the inputs to make sure the elements in expected are present ''' 
    272285        message="An incomplete NDG search form was received: " 
     
    276289        if self.message!='':self.message+='[%s]'%self.inputs 
    277290                 
    278     def __checkdates(self,dateRange): 
     291    def __checkdates(self, dateRange): 
    279292        ''' Check input dates for sanity ''' 
    280293        
     
    284297            self.message='Second date must be after first date' 
    285298         
    286     def __buildconstraints(self,dateRange,bbox,scope,searchString,geoSearch): 
     299    def __buildconstraints(self, dateRange, bbox, scope, searchString, geoSearch): 
    287300        ''' Just build a constraint string ''' 
    288         return constraints(dateRange=dateRange,bbox=bbox,scope=scope,searchString=searchString,geoSearchType=geoSearch) 
     301        return constraints(dateRange=dateRange, bbox=bbox, scope=scope, searchString=searchString, geoSearchType=geoSearch) 
    289302         
    290303    #def oneLineSearch(self): 
     
    298311    def semantic(self): 
    299312        self.__setup() 
    300         vs=VS(proxyServer=self.cf.get('DEFAULT','proxyServer')) 
     313        vs=VS(proxyServer=self.cf.get('DEFAULT', 'proxyServer')) 
    301314        if 'searchString' in self.inputs: 
    302315            try: 
    303                 [broader,narrower,synonyms]=vs.getRelated(self.inputs['searchString']) 
     316                [broader, narrower, synonyms]=vs.getRelated(self.inputs['searchString']) 
    304317                #get a base string for the links to new searches 
    305318                if 'start' in self.inputs: del self.inputs['start'] 
     
    307320                self.inputs['searchString']='###SEARCHSSTRING###' 
    308321                q='%s/discovery?'%g.server 
    309                 for i in self.inputs: q+='%s=%s&'%(i,self.inputs[i]) 
     322                for i in self.inputs: q+='%s=%s&'%(i, self.inputs[i]) 
    310323                url=q[0:-1] 
    311324                # and now build the links  
     
    314327                c.synonyms=[] 
    315328                for i in narrower: 
    316                     c.narrower.append((i,url.replace('###SEARCHSSTRING###',i))) 
     329                    c.narrower.append((i, url.replace('###SEARCHSSTRING###', i))) 
    317330                for i in broader: 
    318                     c.broader.append((i,url.replace('###SEARCHSSTRING###',i))) 
     331                    c.broader.append((i, url.replace('###SEARCHSSTRING###', i))) 
    319332                for i in synonyms: 
    320                     c.synonyms.append((i,url.replace('###SEARCHSSTRING###',i))) 
     333                    c.synonyms.append((i, url.replace('###SEARCHSSTRING###', i))) 
    321334                if c.narrower!=[] or c.broader!=[] or c.synonyms!=[]: c.semAvailable=1 
    322             except IOError,e: 
     335            except IOError, e: 
    323336                c.semAvailable=0 
    324337                c.semError=' (No valid reply from vocabulary service)' 
    325338                #This should go in a log file ... 
    326                 print 'ERROR: Vocabulary Service: %s (for search [%s])'%(str(e),self.inputs['searchString']) 
     339                print 'ERROR: Vocabulary Service: %s (for search [%s])'%(str(e), self.inputs['searchString']) 
    327340        else: 
    328             broader,narrower,synonyms=[],[],[] 
     341            broader, narrower, synonyms=[], [], [] 
    329342            c.semAvailable=0 
    330343            c.semError='.' 
    331344         
    332         return render_response('semantic',fragment=True) 
    333      
    334     def moreSearch(self,ws): 
     345        return render_response('semantic', fragment=True) 
     346     
     347    def moreSearch(self, ws): 
    335348        ''' Provides the search on Browse and NumSim content ''' 
    336349        c.results=ws.results 
    337350        c.searchTarget=self.inputs['searchTarget'] 
    338351        for r in c.results: 
    339             n=ndgObject(r.id,config=self.cf) 
    340             r.link={'Browse':n.BURL,'NumSim':n.URL}[c.searchTarget] 
     352            n=ndgObject(r.id, config=self.cf) 
     353            r.link={'Browse':n.BURL, 'NumSim':n.URL}[c.searchTarget] 
    341354         
    342355        return render_response('short_results') 
  • TI05-delivery/ows_framework/tags/ows_server@3249_ProGlue_Stable/ows_server@3249/ows_server/models/DIF.py

    r3159 r4658  
    1919        # if you've egged it this is the way it comes 
    2020        from elementtree import ElementTree as ET 
    21 debug=1 
     21#debug=1 
     22debug=0 
     23import logging 
     24log = logging.getLogger(__name__) 
    2225     
    2326def shortLong(targetList,s,l): 
     
    7881        self.name=helper.getText(self.tree,'Entry_Title') 
    7982        self.abbreviation=self.name[0:min(5,len(self.name))] 
     83         
     84        #log.debug('++++++++++++++++++++++++++++++++++++++++++++++++++++++++') 
     85        #log.debug('self.xml: %s' % self.xml) 
    8086         
    8187        #Note that entity.constraints.html is about access control on the metadata, 
  • TI05-delivery/ows_framework/tags/ows_server@3249_ProGlue_Stable/ows_server@3249/ows_server/models/DiscoveryService_services.py

    r3093 r4658  
    1414# Locator 
    1515class DiscoveryServiceLocator: 
    16     DiscoveryServicePortType_address = "http://glue.badc.rl.ac.uk:8080/axis2/services/DiscoveryService" 
     16    #DiscoveryServicePortType_address = "http://glue.badc.rl.ac.uk:8080/axis2/services/DiscoveryService" 
     17    DiscoveryServicePortType_address = "http://localhost:8080/axis2/services/DiscoveryService" 
    1718    def getDiscoveryServicePortTypeAddress(self): 
    1819        return DiscoveryServiceLocator.DiscoveryServicePortType_address 
  • TI05-delivery/ows_framework/tags/ows_server@3249_ProGlue_Stable/ows_server@3249/ows_server/models/ndgObject.py

    r3073 r4658  
     1debug=0 
     2import logging 
     3log = logging.getLogger(__name__) 
     4 
    15class ndgObject: 
    26 
     
    1418        at a service registry, but that's not yet available. ''' 
    1519        uri=str(uri) #Dom had problem with unicode coming in here ... dunno why @@@@ 
     20       
     21       # log.debug('incoming uri: %s' % uri) 
    1622        self.gettable=-1  # The specific record is not known to be gettable via any service 
    1723                          # other values of gettable allowed are  
     
    6167            # it might not be, in which case we have to build up all the possible views upon it. 
    6268            # But remember only data entity b records have discovery records ... 
     69             
     70             
     71             
    6372            server=self.config.get('DISCOVERY','default') 
    6473            self.viewService='%s/view/'%server 
  • TI05-delivery/ows_framework/tags/ows_server@3249_ProGlue_Stable/ows_server@3249/ows_server/models/ndgSearch.py

    r3093 r4658  
    22from DiscoveryService_services_types import * 
    33from DiscoveryService_services import * 
     4from ows_server.models.DIF import DIF # so can sort the difs in doc list 
    45import os.path 
    56 
     
    1920 
    2021from xml.dom import expatbuilder 
     22 
     23debug=0 
     24 
     25import logging 
     26log = logging.getLogger(__name__) 
    2127 
    2228class ExpatReaderClass: 
     
    119125        ''' Parse the list of documents, and retrieve them directly ''' 
    120126         
    121         if self.documents is None: return [] 
    122         if self.logger: itime=time.time() 
     127        if self.documents is None: return [] 
     128        if self.logger: itime=time.time() 
     129         
    123130          
    124131        #create a request object 
    125132        request=doPresentRequest() 
    126         #get an instance of the Documents holder class 
    127         DocList=request.new_documents() 
     133        #get an instance of the Documents holder class 
     134        DocList=request.new_documents() 
     135         
    128136        request.Documents=DocList 
    129         DocList.Document=self.documents 
     137        DocList.Document=self.documents 
    130138        request.Format=format 
    131139        result=self.server.doPresent(request) 
     140         
     141        #log.debug('request: %s' % request.Format) 
     142         
    132143        if result._status: 
    133144            docs=result.Documents.Document 
    134         else: 
     145        else: 
    135146            raise ValueError('Error retrieving [%s] was [%s]'% 
    136147                        (self.documents,result._statusMessage)) 
     
    138149            etime=time.time()-itime 
    139150            self.logger.info('Document Load [n=%s] took [%ss]'%(len(self.documents),etime)) 
     151             
     152        #if got here, need to sort the docs returned to make sure they match the indexing of the filename as new postgres cannot be relied upon... 
     153        sortedDocs = [] 
     154         
    140155 
    141156        return docs 
     
    175190    def getLabelledDocs(self,format='original'): 
    176191        ''' Returns all the documents in sequence in a labelled list of strings''' 
     192         
    177193        if self.hits==0: return [] 
    178194        #filenames=self.documents.Document 
     
    181197        filenames=self.documents 
    182198        i=len(filenames) 
     199         
     200            
    183201        j=len(responses) 
    184202        if i!=j: 
     
    190208            ####results.append((filenames[i].strip('.xml'),responses[i])) 
    191209            ####Wow, 'higem.xml'.strip('.xml') = hige ... and that's what split is supposed to do! 
     210             
    192211            ff=os.path.splitext(filenames[i]) 
    193212            results.append((ff[0],responses[i])) 
     213               
     214            #log.debug('ff: %s' % ff[0] ) 
     215             
    194216        return results 
    195217         
  • TI05-delivery/ows_framework/tags/ows_server@3249_ProGlue_Stable/ows_server@3249/ows_server/templates/meta.kid

    r3167 r4658  
    172172                <span py:if="d.briefCitation!=''"> 
    173173                    <span class="ndgem">Citation:</span> ${d.briefCitation}</span> 
    174                 <span class="ndgem"> Title:</span> 
     174                <span class="ndgem"> Title Smitle:</span> 
    175175                    <span py:replace="abbreviate(d.name,70,0)"/> 
    176176                <br/> 
Note: See TracChangeset for help on using the changeset viewer.