Commits

Anonymous committed 6750a65

Renaming of parsetText into parse, changing of routing for more expressive urls, moving correspondents into letters from data, beginnings of content negotiation for letters and alternative representations in json, rdf and xml. Search form included as well.

  • Participants
  • Parent commits 1ffc7ff

Comments (0)

Files changed (19)

openletters/config/routing.py

     map.connect('schema', '/schema', controller='home', action='schema')
     map.connect('/{controller}/', action='index')
     map.connect('/{controller}/{action}')
-    map.connect('/{controller}/{action}/{id}')
+    map.connect('/{controller}/{action}/{author}')
+    map.connect('/{controller}/{action}/{author}/')
+    map.connect('/{controller}/{action}/{author}/{correspondent}')
+    map.connect('/{controller}/{action}/{author}/{correspondent}/')
+    map.connect('/{controller}/{action}/{author}/{correspondent}/{id}')
 
     return map

openletters/controllers/data.py

 import urllib
 
 from openletters.lib.base import BaseController, render
-from openletters.transform import transform_json, transform_xml, transform_rdf
+from openletters.transform.transform_rdf import rdf_transform
 
 log = logging.getLogger(__name__)
 
 class DataController(BaseController):
-    def json(self, id=None):
-        timeline = transform_json.author_timeline(id)
-        if id is None:
-            abort(404)
-        return timeline
     
-    def letter_rdf(self, id=None):
-        response.headers['content-type'] = 'text/xml; charset=utf-8'
-        letter_rdf = transform_rdf.create_rdf_letter(id)
-        if id is None:
-            abort(404)
-            
-        return letter_rdf
-    
-    def endpoint(self):
-        response.headers['content-type'] = 'text/xml; charset=utf-8'
-        letter_rdf = transform_rdf.create_rdf_end()
-        return letter_rdf
-    
-    def correspondent(self, id=None):
-        response.headers['content-type'] = 'text/xml; charset=utf-8'
-        corr_rdf = transform_rdf.create_correspondent(id)
-        if id is None:
-            abort(404)
-            
-        return corr_rdf
+    def endpoint (self):
+        response.headers['Content-Type'] = 'application/rdf+xml; charset=utf-8'
+        rdf = rdf_transform()
+        
+        return rdf.create_rdf_end()

openletters/controllers/letters.py

 
 import genshi
 
+import urllib
+
 from pylons import request, response, session, tmpl_context as c
 from pylons.controllers.util import abort, redirect_to
 
 from openletters.lib.base import BaseController, render
 from openletters import model
-from openletters.parseText import parse_text, parse_date
+from openletters.parse import parse_text, parse_date
 
-from sqlalchemy.orm import join
+from openletters.transform.transform_json import json_transform
+from openletters.transform.transform_xml import xml_transform
+from openletters.transform.transform_rdf import rdf_transform
+
+from openletters.model import dbase
+
+from sqlalchemy import join, and_
 
 log = logging.getLogger(__name__)
 
 class LettersController(BaseController):
+    #def index(self):
+    #    c.site_title = "Open Correspondence"
+    #    
+    #    author = request.params.get('author', '')
+    #    if author:
+    #        pass
+    #    
+    #    else:
+    #        c.page_title = "Index of letters"
+    #        c.letters = model.Session.query(model.Letter).all()
+
+        #return render('letters/index.html')
+        #return render('index.html')
+    ''' 
+      Search for letters 
+    '''
     def index(self):
-        c.site_title = "Open Correspondence"
+
+        c.corres =  model.Session.query(model.Letter.correspondent).distinct().all()
+            
+        return render('letters/search.html')
+    
+    ''' 
+      Redirect for search form 
+    '''
+    def search (self):
         
-        author = request.params.get('author', '')
-        if author:
-            pass
+        sender = request.POST['author']
+        recip = request.POST['recipient']
+        
+        redirect_to(action="view", author= sender, correspondent= recip, id=None)
+        
+    
+    ''' 
+    View returns either the index or the letter text in various formats - json, xml or html -
+    depending on the accept header
+    
+    format of url is /<author name>/<correspondent name>/<letter id>
+    '''
+    def view(self, author=None, correspondent=None, id=None):
+        
+        format = request.headers.get('accept','')
+
+        #author is the base collection so cannot be empty
+        if author is None:
+            abort(404)
+          
+        query_string = model.Session.query(model.Letter).filter(model.Letter.type == author).all()
+
+        
+        if correspondent is not None:
+            corr = urllib.unquote(correspondent)
+            query_string = model.Session.query(model.Letter).filter(model.Letter.type == author).filter(model.Letter.correspondent == corr).all()
+        
+        if id is not None:
+            query_string = model.Session.query(model.Letter).filter(model.Letter.type == author).filter(model.Letter.correspondent == corr).filter(model.Letter.id == id).all()
+            
+        if query_string is None or query_string == []:
+            abort(404)
+        
+        if format == "application/json":
+            
+            response.headers['Content-Type'] = 'application/json'
+            json = json_transform()
+            return json.to_dict(query_string, id)
+        
+        elif format == "application/xml":
+            response.headers['Content-Type'] = 'text/xml'
+            xml = xml_transform()
+            
+            if id is None:
+                return xml.index_xml(query_string)
+            else:
+                return xml.letter_xml(query_string)
+       
+        elif format == "application/rdf+xml":
+            response.headers['Content-Type'] = 'application/rdf+xml; charset=utf-8'
+            rdf = rdf_transform()
+            return rdf.create_rdf_letter(query_string)
         
         else:
-            c.page_title = "Index of letters"
-            c.letters = model.Session.query(model.Letter).all()
+            if id is None:
 
-        return render('letters/index.html')
-        return render('index.html')
+                for letter in query_string:
+                    c.page_title = "Letters written by " + parse_text.author_full(self, letter.type)
+                    if correspondent is not None:
+                        c.page_title += " to " + letter.correspondent
+                        
+                    c.letters = query_string                
+                return render('letters/index.html')
+            else:
+                for letter in query_string:
+                    c.page_title = "Letter written from " + parse_text.author_full(self, letter.type) + " to " + letter.correspondent
+                    c.author = parse_text.author_full(self, letter.type)
+                    c.correspondent = letter.correspondent
+                    c.letter_date = letter.letter_date
+                    c.letter_text = letter.letter_text
+                    c.id = letter.id
+                    c.type = model.Session.query(model.Source).get(letter.volume)
+                    
+                return render('letters/view.html')
         
-    def view(self, id=None):
-        c.letter = model.Session.query(model.Letter).get(id)
-        c.type = model.Session.query(model.Source).get(c.letter.volume)
+    
+    '''
+      Method to return details about a correspondent
+    '''
+    def correspondent(self, author=None):
         
-        if c.letter is None:
+        if author is None:
             abort(404)
+            
+        format = request.headers.get('accept','')
+        #format = "application/xml"
+        #response.headers['content-type'] = 'text/xml; charset=utf-8'
+
+        if format == "application/rdf+xml":
+            response.headers['content-type'] = 'application/rdf + xml; charset=utf-8'
+            rdf = rdf_transform()
+            return rdf.create_correspondent(author, self.corr_dict(author))
+        
+        elif format == "application/xml":
+            response.headers['content-type'] = 'text/xml; charset=utf-8'
+            xml = xml_transform()
+            return xml.corres_xml(author, self.corr_dict(author))
+        
+        elif format == "application/json":
+            response.headers['content-type'] = 'application/json;'
+            json = json_transform()
+            return json.corr_json(author, self.corr_dict(author))
         else:
-            return render('letters/view.html')
+            c.page_title = urllib.unquote(author)
+            c.author = author
+            c.nicks = self.corr_dict(author)
+ 
+            return render('letters/correspondent.html')
+            
+        return corr_rdf
+
+    def corr_dict(self, corr):
         
+        letter = {}  
+        letter = dbase.get_correspondent(corr)
+    
+        letter_items = letter.items()
+        letter_items.sort()
+        
+        return letter_items

openletters/parse/ReadMe.txt

+This folder will contain tools to parse the text file into the Redis store.
+
+17 April 2010 - IE to convert current PHP scripts into Python

openletters/parse/__init__.py

Empty file added.

openletters/parse/parse_date.py

+import re, datetime, time
+
+'''
+  Gets the relevant line from the letter then breaks it up
+  I think that this should be eventually returned as ISO8601 format (YYYY-MM-DD)
+  as it allows for interoperability and transformation
+  TODO: Work on parsing dates like Christmas Eve as 12-24
+'''
+
+def parseDate (letter):
+    dateObj = ''
+    dateObj = parseLineDate(letter)
+    return dateObj
+
+''' 
+  Function finds the line with the year
+'''
+def parseLineDate(obj):
+    ret_obj = ''
+    retObj = ''
+    h = re.compile(".*(\d{4})")
+    mat_m = h.match(obj)
+    if mat_m:
+        retObj = mat_m.group()
+     
+    i = 0
+    yr = ''
+    mth = ''
+    day = ''
+    for i in retObj.split():
+        
+        if (parseYear(i)):
+            yr = parseYear(i)
+            
+        if (parseMonth(i)):
+            mth = parseMonth(i)
+            
+        if (parseDay(i)):
+            d = parseDay(i)
+            if str(d[0]).isdigit():
+                day = d[0]
+        
+    #padding the dates with 0    
+    ret_obj = str(yr).rjust(4,'0')+"-"+str(mth).rjust(2,'0')+"-"+str(day).rjust(2,'0')
+    return ret_obj
+
+''' 
+  Function finds the year.  
+'''   
+def parseYear (date):
+    yrLine = ''
+    yrObj = re.compile("(\d{4})")
+    yrMatch = yrObj.match(date)
+    if yrMatch:
+        yrLine = yrMatch.group()
+    
+    return yrLine
+
+''' 
+  Function finds the month  
+'''   
+def parseMonth (date):
+    mthLine = ''
+
+    month = dict([ ('january',1), ('february',2), ('march',3), ('april',4), ('may',5), ('june',6), ('july',7), ('august',8), ('september',9),
+ ('october',10), ('november',11), ('december',12) ])
+    
+    if str(date).lower() in month:
+          mthLine = month[str(date).lower()]
+    else:
+          mthLine = 00
+          
+    return mthLine
+
+
+''' 
+  Function finds the day  
+  It looks for simple patterns
+'''   
+def parseDay (date):
+    dtLine = ''
+
+    if "nd" in date:
+        dtLine = date.split("nd")
+    elif "rd" in date:
+        dtLine = date.split("rd")
+    elif "th" in date:
+        dtLine = date.split("th")
+    elif "st" in date:
+        dtLine = date.split("st")
+    
+    return dtLine
+
+def reformat(dtline):
+    
+    if dtline is "0000-00-00":
+        letterdate = "No date"
+    else:
+        dt = dtline.split("-")
+        print "dt", int(dt[1])
+        letterdate = str(dt[0]) +","+ format_month(int(dt[1]))+","+ str(dt[2])
+        
+    return letterdate
+
+def format_month (mth):
+    
+    if mth is 00:
+        month = "no month"
+    else:
+        month = datetime.date.strftime(mth, '%B')
+    
+    return month
+   
+def parse_human_date (dtline): 
+    ret_date = ''
+    print "date", dtline
+    if dtline is "0000-00-00":
+        ret_date = "No date"
+    else:
+        ret_date = dtline
+        
+    return ret_date

openletters/parse/parse_text.py

+import re
+import datetime as dtime
+
+'''
+   Parsing methods
+'''
+
+def parseCorrespondent (line):
+        letter = ''
+        correspondent = re.compile("(?=:).*", re.IGNORECASE)
+        m = correspondent.match(line)
+        if m:
+            letter = m.group()
+            return letter  
+
+#Could be useful for FOAF
+# needs tidying up - need to cast tuple to string to clean it up
+def parseSalutation (n):
+        sal = ''
+        n = n.replace("MY", "").replace(",","")
+        if "DEAREST" in n:
+            sal = n.split("DEAREST")
+        elif "DEAR" in n:
+            sal = n.split("DEAR")  
+        elif "RESPECTED" in n:
+            sal = n.split("RESPECTED")
+    
+        return sal
+    
+def stripPunc (urlstring, type=''):
+    urlstring = str(urlstring)
+    ret_url = ''
+    ret_url = urlstring.replace("]","")
+    ret_url = ret_url.replace("[", "")
+    ret_url = ret_url.replace(".", "")
+    ret_url = ret_url.replace(": ", "")
+    ret_url = ret_url.replace('"', "")
+    if type == "url":
+        ret_url = ret_url.replace(" ", "")
+        ret_url = ret_url.strip().lower()
+    
+    return ret_url
+
+'''
+Method to look for text inside balanced quotes to find names
+but exclude long exclamations
+'''
+def parse_balanced_quotes (text):
+    ret_quotes = []
+    m_quote = ''
+    bq = re.findall('"([^\\"]+)"', text)
+    bq = str(bq).replace("[", "").replace("u'", "").replace("]", "").replace("'", "").strip()
+
+    if "," in str(bq):
+        for a in str(bq).split(","): 
+            if str(a[0:1]).isspace():
+                str(a).strip() 
+            if str(a[0:1]) is "u":
+                str(a).replace("u", "")
+
+            if str(a[0:1]).isupper() and "!" not in a and len(str(a)) < 40:
+                ret_quotes.append(a)  
+            else:
+                pass     
+    else:
+        if str(bq[0:1]).isspace():
+                str(bq[0:1]).strip()
+        if str(bq[0:1]) is "u":
+                str(bq[0:1]).replace("u", "")
+                           
+        if str(bq[0:1]).isupper and "!" not in bq and len(str(bq)) < 40:
+            ret_quotes.append(bq)         
+        else:
+            pass
+        
+    return ret_quotes
+
+'''
+ Method to look for certain patterns in names
+ This only works if the name has an honorific. 
+ '''
+def parseProperNames (text):
+    
+    ret_name = []
+    
+    name = re.findall("(?:Mrs)\w{1,3}",text)
+    ret_name.append(name)
+
+    name = re.findall("(?:Mr)\w{1,3}",text)
+    ret_name.append(name)
+
+    name = re.findall("(?:Ms)\w{1,3}",text)
+    ret_name.append(name)
+    
+    name = re.findall("(?:Miss)\w{1,3}",text)
+    ret_name.append(name)
+
+    name = re.findall("(?:Master)\w{1,3}",text)
+    ret_name.append(name)
+
+    name = re.findall("(?:Lord)\w{1,3}",text)
+    ret_name.append(name)
+
+    name = re.findall("(?:Lady)\w{1,3}",text)
+    ret_name.append(name)
+
+        
+    return ret_name 
+
+''' 
+Method to return the full author name from db representation 
+'''
+def author_full (self, author):
+        
+    full_author = ''
+    if "dickens" in author:
+        full_author = "Charles Dickens" 
+        
+    return full_author

openletters/parseText/ReadMe.txt

-This folder will contain tools to parse the text file into the Redis store.
-
-17 April 2010 - IE to convert current PHP scripts into Python

openletters/parseText/__init__.py

Empty file removed.

openletters/parseText/parse_date.py

-import re, datetime, time
-
-'''
-  Gets the relevant line from the letter then breaks it up
-  I think that this should be eventually returned as ISO8601 format (YYYY-MM-DD)
-  as it allows for interoperability and transformation
-  TODO: Work on parsing dates like Christmas Eve as 12-24
-'''
-
-def parseDate (letter):
-    dateObj = ''
-    dateObj = parseLineDate(letter)
-    return dateObj
-
-''' 
-  Function finds the line with the year
-'''
-def parseLineDate(obj):
-    ret_obj = ''
-    retObj = ''
-    h = re.compile(".*(\d{4})")
-    mat_m = h.match(obj)
-    if mat_m:
-        retObj = mat_m.group()
-     
-    i = 0
-    yr = ''
-    mth = ''
-    day = ''
-    for i in retObj.split():
-        
-        if (parseYear(i)):
-            yr = parseYear(i)
-            
-        if (parseMonth(i)):
-            mth = parseMonth(i)
-            
-        if (parseDay(i)):
-            d = parseDay(i)
-            if str(d[0]).isdigit():
-                day = d[0]
-        
-    #padding the dates with 0    
-    ret_obj = str(yr).rjust(4,'0')+"-"+str(mth).rjust(2,'0')+"-"+str(day).rjust(2,'0')
-    return ret_obj
-
-''' 
-  Function finds the year.  
-'''   
-def parseYear (date):
-    yrLine = ''
-    yrObj = re.compile("(\d{4})")
-    yrMatch = yrObj.match(date)
-    if yrMatch:
-        yrLine = yrMatch.group()
-    
-    return yrLine
-
-''' 
-  Function finds the month  
-'''   
-def parseMonth (date):
-    mthLine = ''
-
-    month = dict([ ('january',1), ('february',2), ('march',3), ('april',4), ('may',5), ('june',6), ('july',7), ('august',8), ('september',9),
- ('october',10), ('november',11), ('december',12) ])
-    
-    if str(date).lower() in month:
-          mthLine = month[str(date).lower()]
-    else:
-          mthLine = 00
-          
-    return mthLine
-
-
-''' 
-  Function finds the day  
-  It looks for simple patterns
-'''   
-def parseDay (date):
-    dtLine = ''
-
-    if "nd" in date:
-        dtLine = date.split("nd")
-    elif "rd" in date:
-        dtLine = date.split("rd")
-    elif "th" in date:
-        dtLine = date.split("th")
-    elif "st" in date:
-        dtLine = date.split("st")
-    
-    return dtLine
-
-def reformat(dtline):
-    
-    if dtline is "0000-00-00":
-        letterdate = "No date"
-    else:
-        dt = dtline.split("-")
-        print "dt", int(dt[1])
-        letterdate = str(dt[0]) +","+ format_month(int(dt[1]))+","+ str(dt[2])
-        
-    return letterdate
-
-def format_month (mth):
-    
-    if mth is 00:
-        month = "no month"
-    else:
-        month = datetime.date.strftime(mth, '%B')
-    
-    return month
-   
-def parse_human_date (dtline): 
-    ret_date = ''
-    print "date", dtline
-    if dtline is "0000-00-00":
-        ret_date = "No date"
-    else:
-        ret_date = dtline
-        
-    return ret_date

openletters/parseText/parse_text.py

-import re
-import datetime as dtime
-
-'''
-   Parsing methods
-'''
-
-def parseCorrespondent (line):
-        letter = ''
-        correspondent = re.compile("(?=:).*", re.IGNORECASE)
-        m = correspondent.match(line)
-        if m:
-            letter = m.group()
-            return letter  
-
-#Could be useful for FOAF
-# needs tidying up - need to cast tuple to string to clean it up
-def parseSalutation (n):
-        sal = ''
-        n = n.replace("MY", "").replace(",","")
-        if "DEAREST" in n:
-            sal = n.split("DEAREST")
-        elif "DEAR" in n:
-            sal = n.split("DEAR")  
-        elif "RESPECTED" in n:
-            sal = n.split("RESPECTED")
-    
-        return sal
-    
-def stripPunc (urlstring, type=''):
-    urlstring = str(urlstring)
-    ret_url = ''
-    ret_url = urlstring.replace("]","")
-    ret_url = ret_url.replace("[", "")
-    ret_url = ret_url.replace(".", "")
-    ret_url = ret_url.replace(": ", "")
-    ret_url = ret_url.replace('"', "")
-    if type == "url":
-        ret_url = ret_url.replace(" ", "")
-        ret_url = ret_url.strip().lower()
-    
-    return ret_url
-
-'''
-Method to look for text inside balanced quotes to find names
-but exclude long exclamations
-'''
-def parse_balanced_quotes (text):
-    ret_quotes = []
-    m_quote = ''
-    bq = re.findall('"([^\\"]+)"', text)
-    bq = str(bq).replace("[", "").replace("u'", "").replace("]", "").replace("'", "").strip()
-
-    if "," in str(bq):
-        for a in str(bq).split(","): 
-            if str(a[0:1]).isspace():
-                str(a).strip() 
-            if str(a[0:1]) is "u":
-                str(a).replace("u", "")
-
-            if str(a[0:1]).isupper() and "!" not in a and len(str(a)) < 40:
-                ret_quotes.append(a)  
-            else:
-                pass     
-    else:
-        if str(bq[0:1]).isspace():
-                str(bq[0:1]).strip()
-        if str(bq[0:1]) is "u":
-                str(bq[0:1]).replace("u", "")
-                           
-        if str(bq[0:1]).isupper and "!" not in bq and len(str(bq)) < 40:
-            ret_quotes.append(bq)         
-        else:
-            pass
-        
-    return ret_quotes
-
-'''
- Method to look for certain patterns in names
- This only works if the name has an honorific. 
- '''
-def parseProperNames (text):
-    
-    ret_name = []
-    
-    name = re.findall("(?:Mrs)\w{1,3}",text)
-    ret_name.append(name)
-
-    name = re.findall("(?:Mr)\w{1,3}",text)
-    ret_name.append(name)
-
-    name = re.findall("(?:Ms)\w{1,3}",text)
-    ret_name.append(name)
-    
-    name = re.findall("(?:Miss)\w{1,3}",text)
-    ret_name.append(name)
-
-    name = re.findall("(?:Master)\w{1,3}",text)
-    ret_name.append(name)
-
-    name = re.findall("(?:Lord)\w{1,3}",text)
-    ret_name.append(name)
-
-    name = re.findall("(?:Lady)\w{1,3}",text)
-    ret_name.append(name)
-
-        
-    return ret_name 

openletters/templates/letters/correspondent.html

+<html xmlns:py="http://genshi.edgewall.org/"
+  xmlns:xi="http://www.w3.org/2001/XInclude"
+  py:strip="True" >
+
+  <py:def function="page_title">c.page_title</py:def>
+
+  <div py:def="content">
+    <div class="letter">
+
+        <h4>${c.author}</h4>
+        <p>Also known as:</p>
+          <ul>
+          <div py:for="nick, t in c.nicks">
+            <li>${nick}</li>
+          </div>
+          </ul>
+        <!--<h5>Letters</h5>
+          <ul>
+            <li><a href="${corr.type}/${corr.correspondent}/${corr.id}">${corr.type}/${corr.correspondent}/${corr.id}</a></li>
+          </ul>-->
+
+    </div>
+  </div>
+
+  <xi:include href="../layout.html" />
+</html>

openletters/templates/letters/index.html

 <html xmlns:py="http://genshi.edgewall.org/"
- xmlns:xi="http://www.w3.org/2001/XInclude">
+ xmlns:xi="http://www.w3.org/2001/XInclude" 
+ py:strip="True">
 
  <py:def function="page_title">Letters - Home</py:def>
 
 <div py:def="content">
  <p>Current letters in the database organized by their associated
- author.</p>
+ author.
+ </p>
+ 
 
- <h3>Charles Dickens</h3>
+<div py:for="letter in c.letters">
+ <h3>
+   <div py:choose="">
+      <span py:when="letter.id == 'dickens'">Charles Dickens</span>
+      <span py:otherwise=""> </span>
+   </div>
+ </h3>
  <ul>
- <li py:for="letter in c.letters">
- <a href="${url(controller='letters', action='view',
- id=letter.id)}">${letter.correspondent} - ${letter.letter_date}</a> -
- ${' '.join(letter.letter_text.split()[:10])}
- </li>
+   <li >
+     <a href="${url(controller='letters', action='view', author=letter.type, correspondent = letter.correspondent,
+     id=letter.id)}">${letter.correspondent} -  
+     <div py:choose = ""> 
+       <span py:when="letter.letter_date == '0000-00-00'">No date</span>
+       <span py:otherwise="">${letter.letter_date[-2:]}, ${letter.letter_date[5:7]}, ${letter.letter_date[:4]}</span>
+     </div>
+    </a> -
+    ${' '.join(letter.letter_text.split()[:10])}
+  </li>
  </ul>
  </div>
+ </div>
 
  <xi:include href="../layout.html" />
 </html> 

openletters/templates/letters/search.html

+<html xmlns:py="http://genshi.edgewall.org/"
+  xmlns:xi="http://www.w3.org/2001/XInclude"
+  py:strip = "True">
+
+  <py:def function="page_title">Letters - Search</py:def>
+
+  <div py:def="content">
+    <div class="letter">
+    <form action="search" method="post">
+    <p>This form will allow you to search for collections of letters by correspondent.
+    The selection is currently limited to the letters of Charles Dickens but more authors 
+    will be added. </p>
+    
+    <p>If you want to look at the index for all the letters, just submit the form without a correspondent
+    or you can <a href="view/dickens">view them here</a>.</p>
+    <p>Please choose a correspondent from the menu.</p>
+    <input type="hidden" name="author" value="dickens" /><br />
+    Search by correspondent: <br />
+  
+       <select name="recipient">
+          <option value="">Please select correspondent...</option>
+            <div py:for="corr in c.corres">
+               <option value="${corr.correspondent}">${corr.correspondent}</option>
+            </div>
+       </select>
+       
+    <input type="submit" name="Search Letters" />
+    </form>
+  </div>
+  </div>
+
+  <xi:include href="../layout.html" />
+</html>

openletters/templates/letters/view.html

 <html xmlns:py="http://genshi.edgewall.org/"
-  xmlns:xi="http://www.w3.org/2001/XInclude">
+  xmlns:xi="http://www.w3.org/2001/XInclude"
+  py:strip = "True">
 
   <py:def function="page_title">Letters - View</py:def>
 
   <div py:def="content">
     <div class="letter">
-       <p><strong>To: ${c.letter.correspondent}</strong>, <strong>${c.letter.letter_date}</strong></p>
-      ${h.literal(h.markdown(c.letter.letter_text))}
-      <a href="../../data/letter_rdf/${c.letter.id}">Rdf version</a>
+      ${h.literal(h.markdown(c.letter_text))}
+      <!--<a href="../../../../data/letter_rdf/${c.id}">Rdf version</a>-->
       <p>&nbsp;</p>
       <p>Source: ${getattr(c.type, 'title', '')}<br />${getattr(c.type, 'author', '')}</p>
       <p>${getattr(c.type, 'publn_data', '')} : ${getattr(c.type, 'publn_date', '')}</p>
   </div>
 
   <xi:include href="../layout.html" />
-</html>
-
+</html>

openletters/tests/functional/test_letters.py

     def test_view(self):
         letter = Fixtures.letter()
         response = self.app.get(url(controller='letters', action='view',
-            id=letter.id))
+            author = 'dickens',
+            correspondent = 'Lord Lytton',
+            id='879'))
         print letter.correspondent
         restext = response.body.decode('utf8').encode('ascii', 'ignore')
         # print unicode(response.body, encoding='utf8')
-        assert 'To: %s' % letter.correspondent in response, restext
+        assert '[Sidenote: %s ]' % letter.correspondent in response, restext
 
+    def test_correspondent(self):
+        letter = Fixtures.letter()
+        response = self.app.get(url(controller='letters', action="correspondent", 
+            author="Miss Hogarth" ))
+        assert 'Miss Hogarth' in response

openletters/transform/transform_json.py

 except ImportError:
     import simplejson as json
 
-from openletters.model import dbase
+class json_transform:
   
-def author_timeline (author=None):
+
+    '''
+    Function to return the text as json
+    '''
+    def to_dict (self, letterobj, type):
+
+        dict = '{'
+        
+        if type is None:
+            dict += "index: {"
+        else:
+            dict += "letter: {"
+         
+        for l in letterobj:
+            
+            dict +=  str(l.id) + ': [ {'
+            dict += '"author": "' + l.type
+            dict += '", "correspondent: "' + l.correspondent
+            dict += ', "date": "' + l.letter_date
+            
+            if l.letter_text:
+                #remove the brackets in the letter
+                text = l.letter_text.replace("[", "").replace("]", "")
+                dict += '", "text": "' + text
+            
+            dict += '"}],'
+        
+        #remove the last comma
+        dict = dict[0: -1]
+        
+        dict += '} }'
+            
+        return self.jsonify(dict)
     
-    if author is None:
-        a_timeline = "No author passed through"
-        return a_timeline
-    
-    else:
-        a_timeline = ''
-        author_index = "{'dateTimeFormat:iso8601, events:["
-        letters = []
-        letters = dbase.create_correspondents(author)
-        letter_items = letters.items()
-        letter_items.sort()
-    
-        for count, body in letter_items:
-            author_index += "{"
-            author_index += "type: %s, description: %s, id: %s, title: %s, start: %s" % (author, author_timeline_letter(str(body[3]), str(body[1]), str(body[2])) , body[1], body[1], body[2])
-            author_index += "}"
+        '''
+    Function to return the text as json
+    '''
+    def corr_json (self, author, letterobj):
+
+        dict = '{'
+
+        dict +=  str(author) + ': [ '
+        dict += '"correspondent": "' + author
+
+        for l, txt in letterobj:
+            dict += '", "nick: "' + l
         
-        author_index += "]}"
-    
-        a_timeline = json.dumps(author_index, sort_keys=True, indent=4)
+        dict += '"]'
+        
+        dict += '}'
+            
+        return self.jsonify(dict)
 
-    return a_timeline
-
-def author_timeline_letter (id, correspondent, time):
-    
-    a_letter_text = ''
-    
-    a_letter_text = "Letter written to " + correspondent + " on " + time + "."
-    a_letter_text +=  'The text can be viewed at <a href="http://www.opencorrespondence.org/letters/view/'+id+'">http://www.opencorrespondence.org/letters/view/'+id+'</a>'
-    
-    return a_letter_text
+    def jsonify (self, output):
+        return json.dumps(output, sort_keys = True, indent=4)

openletters/transform/transform_rdf.py

 from openletters.model import dbase
-from openletters.parseText import parse_text
+from openletters.parse import parse_text
+
 
 import urllib
+
 #import rdflib
 #from rdflib.graph import ConjunctiveGraph as Graph
 #from rdflib import plugin
 #dublin_core = Namespace('http://purl.org/dc/elements/1.1/')
 #base_uri = "http://www.opencorrespondence.org/schema"
 
-
-'''
-  creates an rdf representation of letter used to load into the triple store
-  @param uri 
-  @return letter_rdf
-  '''
-def create_rdf_letter (uri):
-
-#uses OWL, FOAF, DC and letter Purl schemas
-    letter_rdf = '<rdf:RDF\n'
-    letter_rdf += 'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n'
-    letter_rdf += 'xmlns:letter="http://purl.org/letter/"\n'
-    letter_rdf += 'xmlns:time-entry="http://www.isi.edu/~pan/damltime/time-entry.owl#"\n'
-    letter_rdf += 'xmlns:foaf="http://xmlns.com/foaf/0.1/"\n'
-    letter_rdf += 'xmlns:dc ="http://purl.org/dc/elements/1.1/"\n'
-    letter_rdf += 'xml:base="http://www.opencorrespondence.org/" >\n'
+class rdf_transform:
+    '''
+      creates an rdf representation of letter used to load into the triple store
+      @param letters
+      @return letter_rdf
+      '''
+    def create_rdf_letter (self, letters):
     
-    letter_rdf += '<rdf:Description rdf:about="letters/view/'+uri+'">'
-    letter_rdf += '<dc:author>Charles Dickens</dc:author>'
-        
-    letter = {}  
-    letter = dbase.get_letter_rdf(uri)
-    
-    letter_items = letter.items()
-    letter_items.sort()
-    
-    for url, text in letter_items:
-        letter_rdf += '<time-entry:inCalendarClockDataType rdf:datatype="xsd:dateTime">'+str(text[3])+'T00:00:00</time-entry:inCalendarClockDataType>'
-        #still need to put in a foaf:Person link for the person - we have potential nickname data in the db
-        
-        letter_rdf += '<letter:Correspondent>'+str(text[1])+'</letter:Correspondent>'
-        #this section will parse for proper names in due course
-        #commented out whilst code is being ported
-        #letter_name = parse_text.parseProperNames(text)
-       # print"names, ", letter_name
-        
-        #for name in letter_name:
-        #    letter_rdf += "<letter:personReferred>%s</letter:personReferred>" %(name)
-                           
-        letter_quotes = parse_text.parse_balanced_quotes(text[2])
-        for quote in letter_quotes:
-            if str(quote[0:1]).isupper and "!" not in quote:
-                letter_rdf += "<letter:textReferred>%s</letter:textReferred>\n" %(parse_text.stripPunc(quote))
-        
-    letter_rdf += "</rdf:Description>"
-    letter_rdf += "</rdf:RDF>"
-    return letter_rdf
-
-def create_rdf_end ():
-    #default_graph_uri = "http://rdflib.net/rdfstore"
-    #configString = "host=localhost,user=root,password=enoch,db=rdfstore"
-    # Get the mysql plugin. You may have to install the python mysql libraries
-    #store = plugin.get('MySQL', Store)('rdfstore')
-    # Open previously created store, or create it if it doesn't exist yet
-    #rt = store.open(configString, create=False)
-    #if rt == NO_STORE:
-    # There is no underlying MySQL infrastructure, create it
-    #    store.open(configString,create=True)
-    #else:
-    #    assert rt == VALID_STORE,"The underlying store is corrupted"
-    # There is a store, use it
-    #graph = Graph(identifier = URIRef(default_graph_uri))
-#uses OWL, FOAF, DC and letter Purl schemas
-    letter_rdf = '<rdf:RDF\n'
-    letter_rdf += 'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n'
-    letter_rdf += 'xmlns:letter="http://purl.org/letter/"\n'
-    letter_rdf += 'xmlns:time-entry="http://www.isi.edu/~pan/damltime/time-entry.owl#"\n'
-    letter_rdf += 'xmlns:foaf="http://xmlns.com/foaf/0.1/"\n'
-    letter_rdf += 'xmlns:dc ="http://purl.org/dc/elements/1.1/"\n'
-    letter_rdf += 'xml:base="http://www.opencorrespondence.org" >\n'
-    
-    
-    letter = {}  
-    letter = dbase.get_endpoint_rdf()
-
-    letter_items = letter.items()
-    letter_items.sort()
-    
-    
-    for url, text in letter_items:
-        letter_rdf += '<rdf:Description rdf:about="/letters/view/'+ str(url)+'">\n'
-        letter_rdf += '<dc:author>Charles Dickens</dc:author>\n'
-        #graph.add((owl_time, owl_time['inCalendarClockDataType'], Literal(str(text[3])+'T00:00:00')))
-        #graph.add(dublin_core, dublin_core['author'], Literal('Charles Dickens'))
-        #graph.add(Letter, Letter['Correspondent'], Literal(str(text[1])))
-        
-        letter_rdf += '<time-entry:inCalendarClockDataType rdf:datatype="xsd:dateTime">'+str(text[3])+'T00:00:00</time-entry:inCalendarClockDataType>\n'
-        #still need to put in a foaf:Person link for the person - we have potential nickname data in the db
-        
-        letter_rdf += '<letter:Correspondent rdf:resource="/data/correspondent/'+urllib.quote(str(text[1]))+'" />\n'
-        letter_rdf += '<foaf:nick>'+str(text[4])+'</foaf:nick>\n'
-        
-        #this section will parse for proper names in due course
-        #commented out whilst code is being ported
-        #letter_name = parse_text.parseProperNames(text)
-       # print"names, ", letter_name
-        
-        #for name in letter_name:
-        #    letter_rdf += "<letter:personReferred>%s</letter:personReferred>" %(name)
-                           
-        letter_quotes = parse_text.parse_balanced_quotes(text[2])
-        for quote in letter_quotes:
-            #the length is to remove anything really long
-            if str(quote[0:1]).isupper and "!" not in quote and len(str(quote)) < 40:
-                #graph.add(Letter, Letter['textReferred'], Literal(parse_text.stripPunc(quote)))
-                letter_rdf += "<letter:textReferred>%s</letter:textReferred>\n" %(parse_text.stripPunc(quote))
-        
-        letter_rdf += "</rdf:Description>"
-        
-    letter_rdf += "</rdf:RDF>"
-    #letter_rdf = graph.serialize
-    return letter_rdf
-    
-def create_correspondent(corr):
-        u_corr = urllib.unquote(corr)
-        
+    #uses OWL, FOAF, DC and letter Purl schemas
         letter_rdf = '<rdf:RDF\n'
         letter_rdf += 'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n'
         letter_rdf += 'xmlns:letter="http://purl.org/letter/"\n'
         letter_rdf += 'xmlns:time-entry="http://www.isi.edu/~pan/damltime/time-entry.owl#"\n'
         letter_rdf += 'xmlns:foaf="http://xmlns.com/foaf/0.1/"\n'
-        letter_rdf += 'xml:base="http://www.opencorrespondence.org/data/correspondent" >\n'
+        letter_rdf += 'xmlns:dc ="http://purl.org/dc/elements/1.1/"\n'
+        letter_rdf += 'xml:base="http://www.opencorrespondence.org/" >\n'
         
-        letter_rdf += '<rdf:Description rdf:about="/'+urllib.quote(corr)+'">\n'       
-        letter_rdf += '<letter:Correspondent>'+u_corr+'</letter:Correspondent>\n'
+        for l in letters:
+            
+            letter_rdf += '<rdf:Description rdf:about="letters/view/' + l.type + '/' + urllib.quote(l.correspondent) + '/' + str(l.id) +'">'
+            letter_rdf += '<dc:author>Charles Dickens</dc:author>'
+    
+            letter_rdf += '<time-entry:inCalendarClockDataType rdf:datatype="xsd:dateTime">'+str(l.letter_date)+'T00:00:00</time-entry:inCalendarClockDataType>'
+                #still need to put in a foaf:Person link for the person - we have potential nickname data in the db
+                
+            letter_rdf += '<letter:Correspondent>'+l.correspondent+'</letter:Correspondent>'
+            letter_rdf += '<foaf:nick>'+l.salutation+'</foaf:nick>'
+                #this section will parse for proper names in due course
+                #commented out whilst code is being ported
+                #letter_name = parse_text.parseProperNames(text)
+               # print"names, ", letter_name
+                
+                #for name in letter_name:
+                #    letter_rdf += "<letter:personReferred>%s</letter:personReferred>" %(name)
+                                   
+            letter_quotes = parse_text.parse_balanced_quotes(l.letter_text)
+            for quote in letter_quotes:
+                 if str(quote[0:1]).isupper and "!" not in quote:
+                    letter_rdf += "<letter:textReferred>%s</letter:textReferred>\n" %(parse_text.stripPunc(quote))
+                
+            letter_rdf += "</rdf:Description>"
+            
+        letter_rdf += "</rdf:RDF>"
+        
+        return letter_rdf
+    
+    def create_rdf_end (self):
+        #default_graph_uri = "http://rdflib.net/rdfstore"
+        #configString = "host=localhost,user=root,password=enoch,db=rdfstore"
+        # Get the mysql plugin. You may have to install the python mysql libraries
+        #store = plugin.get('MySQL', Store)('rdfstore')
+        # Open previously created store, or create it if it doesn't exist yet
+        #rt = store.open(configString, create=False)
+        #if rt == NO_STORE:
+        # There is no underlying MySQL infrastructure, create it
+        #    store.open(configString,create=True)
+        #else:
+        #    assert rt == VALID_STORE,"The underlying store is corrupted"
+        # There is a store, use it
+        #graph = Graph(identifier = URIRef(default_graph_uri))
+    #uses OWL, FOAF, DC and letter Purl schemas
+        letter_rdf = '<rdf:RDF\n'
+        letter_rdf += 'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n'
+        letter_rdf += 'xmlns:letter="http://purl.org/letter/"\n'
+        letter_rdf += 'xmlns:time-entry="http://www.isi.edu/~pan/damltime/time-entry.owl#"\n'
+        letter_rdf += 'xmlns:foaf="http://xmlns.com/foaf/0.1/"\n'
+        letter_rdf += 'xmlns:dc ="http://purl.org/dc/elements/1.1/"\n'
+        letter_rdf += 'xml:base="http://www.opencorrespondence.org" >\n'
+        
         
         letter = {}  
-        letter = dbase.get_correspondent(u_corr)
-
+        letter = dbase.get_endpoint_rdf()
+    
         letter_items = letter.items()
         letter_items.sort()
-
+        
+        
         for url, text in letter_items:
-            letter_rdf += "<foaf:nick>%s</foaf:nick>\n" %(str(url))
+            letter_rdf += '<rdf:Description rdf:about="/letters/view/'+ str(url)+'">\n'
+            letter_rdf += '<dc:author>Charles Dickens</dc:author>\n'
+            #graph.add((owl_time, owl_time['inCalendarClockDataType'], Literal(str(text[3])+'T00:00:00')))
+            #graph.add(dublin_core, dublin_core['author'], Literal('Charles Dickens'))
+            #graph.add(Letter, Letter['Correspondent'], Literal(str(text[1])))
+            
+            letter_rdf += '<time-entry:inCalendarClockDataType rdf:datatype="xsd:dateTime">'+str(text[3])+'T00:00:00</time-entry:inCalendarClockDataType>\n'
+            #still need to put in a foaf:Person link for the person - we have potential nickname data in the db
+            
+            letter_rdf += '<letter:Correspondent rdf:resource="/letters/correspondent/'+urllib.quote(str(text[1]))+'" />\n'
+            letter_rdf += '<foaf:nick>'+str(text[4])+'</foaf:nick>\n'
+            
+            #this section will parse for proper names in due course
+            #commented out whilst code is being ported
+            #letter_name = parse_text.parseProperNames(text)
+           # print"names, ", letter_name
+            
+            #for name in letter_name:
+            #    letter_rdf += "<letter:personReferred>%s</letter:personReferred>" %(name)
+                               
+            letter_quotes = parse_text.parse_balanced_quotes(text[2])
+            for quote in letter_quotes:
+                #the length is to remove anything really long
+                if str(quote[0:1]).isupper and "!" not in quote and len(str(quote)) < 40:
+                    #graph.add(Letter, Letter['textReferred'], Literal(parse_text.stripPunc(quote)))
+                    letter_rdf += "<letter:textReferred>%s</letter:textReferred>\n" %(parse_text.stripPunc(quote))
+            
+            letter_rdf += "</rdf:Description>"
+            
+        letter_rdf += "</rdf:RDF>"
+        #letter_rdf = graph.serialize
+        return letter_rdf
         
-        letter_rdf += '</rdf:Description>'
-        letter_rdf += '</rdf:RDF>'
-        
-        return letter_rdf
+    def create_correspondent(self, corr, letter_items):
+            u_corr = unicode(corr)
+            
+            letter_rdf = '<rdf:RDF\n'
+            letter_rdf += 'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"\n'
+            letter_rdf += 'xmlns:letter="http://purl.org/letter/"\n'
+            letter_rdf += 'xmlns:time-entry="http://www.isi.edu/~pan/damltime/time-entry.owl#"\n'
+            letter_rdf += 'xmlns:foaf="http://xmlns.com/foaf/0.1/"\n'
+            letter_rdf += 'xml:base="http://www.opencorrespondence.org/letters/correspondent" >\n'
+            
+            letter_rdf += '<rdf:Description rdf:about="/'+urllib.quote(u_corr)+'">\n'       
+            letter_rdf += '<letter:Correspondent>'+u_corr+'</letter:Correspondent>\n'
+            
+            #letter = {}  
+            #letter = dbase.get_correspondent(corr)
+    
+            #letter_items = letter.items()
+            #letter_items.sort()
+    
+            for url, text in letter_items:
+                if url is not None or url != '':
+                    letter_rdf += "<foaf:nick>%s</foaf:nick>\n" %(str(url))
+            
+            letter_rdf += '</rdf:Description>'
+            letter_rdf += '</rdf:RDF>'
+            
+            return letter_rdf
     

openletters/transform/transform_xml.py

 import xml.etree.ElementTree as ET
 
-from openletters.model import dbase
+class xml_transform:
  
-#create an XML of letters for a defined author
-def createIndex (author):
+    '''create an XML of letters for a defined author'''
+    def index_xml (self, letters):
+    
+        root = ET.Element("opencorrespondence")
+        
+        
+        for l in letters:
+            letter = ET.SubElement(root, "letter")
+            url = ET.SubElement(letter, "author")
+            url.text = unicode(l.type)
+            author = ET.SubElement(letter, "correspondent")
+            author.text = unicode(l.correspondent)
+            date = ET.SubElement(letter, "date")
+            date.text = unicode(l.letter_date)
+            id = ET.SubElement(letter, "id")
+            id.text = unicode(str(l.id))
+            
+            if l.letter_text:
+                
+                l_text = ET.SubElement(letter, "text")
+                l_text.text = unicode(self.xml_encode(l.letter_text))
+   
+        doc = ET.tostring(root, "UTF-8")
+    
+        return doc
+    
+    def letter_xml (self, letters):
+            
+        root = ET.Element("opencorrespondence")
+        
+        
+        for l in letters:
+            letter = ET.SubElement(root, "letter")
+            url = ET.SubElement(letter, "author")
+            url.text = unicode(l.type)
+            date = ET.SubElement(letter, "date")
+            date.text = unicode(l.letter_date)
+            author = ET.SubElement(letter, "correspondent")
+            author.text = unicode(l.correspondent)
+            l_text = ET.SubElement(letter, "letter_text")
+            l_text.text = unicode(l.letter_text)
 
-    doc = ''
-    letter = []
-    #letterIndexUrl, letterIndexCorr, letterIndexDt = data.indexAuthor(author)
-    letter = dbase.index_author(author)
+            
+            if l.letter_text:
+                
+                l_text = ET.SubElement(letter, "text")
+                l_text.text = unicode(self.xml_encode(l.letter_text))
+   
+        doc = ET.tostring(root, "UTF-8")
+    
+        return doc
+    
+    def corres_xml (self, corr, letters):
+        
+        root = ET.Element("opencorrespondence")
+        
+        letter = ET.SubElement(root, "person")
+        author = ET.SubElement(letter, "correspondent")
+        author.text = unicode(corr)
+        
+        for name, url in letters:
+ 
+            nick = ET.SubElement(letter, "nick")
+            nick.text = unicode(name)
 
-    root = ET.Element("index")
+   
+        doc = ET.tostring(root, "UTF-8")
+        
+        return doc
 
-    index_items = letter.items()
-    index_items.sort()
-    
-    url = ET.SubElement(root, "url")
-    author = ET.SubElement(url, "correspondent")
-    date = ET.SubElement(author, "date")
-    doc = '<?xml version="1.0" encoding="ISO-8859-1"?>'
-    doc += "<index>"
-    for letter_url, letter_corr in index_items:
-        doc += "<letter>"
-        doc += "<url>http://www.opencorrespondence.org/letter/view/%s</url><corr>%s</corr><date>%s</date>" %(letter_corr[0], letter_corr[1],letter_corr[2])
-        doc += "</letter>"
-    #for key in letter.iteritems():
-    #for n in letter:
-        #url.text = letter_corr[0]
-        #author.text = letter_corr[1]
-        #date.text = letter_corr[2]
-    doc += "</index>"   
-    
-
-    return doc
+    def xml_encode (self, text):
+        
+        return text.replace("&", "&amp;").replace('"', '&quote').replace("'", "&apos;").replace("<", "&lt;").replace(">", "&gt;")
+