[Zope] RE: [squishdot] Mailing Content into Zope/Squishdot

Dan Pierson dan@remote.control.com
Mon, 28 Feb 2000 11:27:21 -0500


This message is in MIME format. Since your mail reader does not understand
this format, some or all of this message may not be legible.

------_=_NextPart_000_01BF8208.B02093DC
Content-Type: text/plain;
	charset="iso-8859-1"

In response to a couple of requests, here's the code that I use to post from
our automation mailing list to the site http://beta.control.com/.  Please
feel free to visit the site, I've meant to announce it here for a while.  It
will go public (and change its url to www.control.com) sometime in March.
The site will be down briefly later today while I upgrade it from Zope 2.0.x
to 2.1.4.

postToControlCom is an executable Python script that uses xmlrpc to talk to
Squishdot.
paths.py is a hack stolen from Mailman that lets postToControlCom use a bit
of Mailman code.

SquishExternals.py contains a Zope ExternalMethod that worries about trying
to figure out where to add a reply in Squishdot.  It's far from perfect; the
current indexing scheme that Squishdot uses doesn't like some of the Subject
lines that show up on the list fairly often (e.g. "words" that contain
hyphens, letters and numbers mixed together such as "IEC1131-3").  Since the
indexing stuff has changed with ZCatalog and there'll be a new Squishdot
soon, we've just been living with the problems for now.

Note, the use of __call__ in SquishExternals.py depends on a minor fix I
made to Squishdot.  Squishdot's standard __call___ method doesn't permit you
to pass a dictionary instead of putting the search arguments in REQUEST.
This caused problems for us because it was too easy for unwanted search keys
to pollute REQUEST.  To fix this, find the reference to REQUEST.set in the
__call__ method and replace it with:

            try:
                REQUEST.set('summary',body)
            except AttributeError:
                # Assume we got a dictionary instead of a Request object
                REQUEST['summary'] = body

Dan Pierson, Control.com, <dan@control.com>



------_=_NextPart_000_01BF8208.B02093DC
Content-Type: application/octet-stream;
	name="postToControlCom"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="postToControlCom"

#!/usr/bin/python=0A=
=0A=
import re=0A=
import string=0A=
import paths=0A=
import xmlrpclib=0A=
from Mailman import Message=0A=
=0A=
# Here's a sample posting call.  It doesn't appear to be possible to =
bypass=0A=
# moderation this way.=0A=
#x.control_com.addPosting('This is a test','Dan Pierson','''This is =
only a test.=0A=
# Had it been a real posting, it would make sense.''', =
'dan@control.com', '', '', 'A test posting', 'News')=0A=
=0A=
tag_table =3D {'APPS'     : 'Applications',=0A=
             'BUSN'     : 'Business',=0A=
             'COMM'     : 'Communications',=0A=
             'ENGR'     : 'Engineering',=0A=
             'HMI'      : 'HMI',=0A=
             'INFO'     : 'Information',=0A=
             'LANG'     : 'Languages',=0A=
             'LinuxPLC' : 'LinuxPLC Project',=0A=
             'MOTION'   : 'Motion Control',=0A=
             'NEWS'     : 'News',=0A=
             'PC'       : 'PCs in Automation',=0A=
             'PLCS'     : 'PLCs',=0A=
             'PROC'     : 'Process Control',=0A=
             'SENSE'    : 'Sensors',=0A=
             'SOFT'     : 'Software in Automation'}=0A=
=0A=
tag_re =3D re.compile(r"((?:re|fw): *)*([A-Za-z]*)[,:]", =
re.IGNORECASE)=0A=
all_tags_re =3D re.compile(r"((?:re|fw): *)*([A-Za-z, ]+):(.*$)", =
re.IGNORECASE)=0A=
=0A=
def title2subject(title):=0A=
    m =3D tag_re.match(title)=0A=
    if m:=0A=
        return tag_table[m.group(2)]=0A=
    else:=0A=
        return ""=0A=
=0A=
def cleanTitle(title):=0A=
    m =3D all_tags_re.match(title)=0A=
    if m:=0A=
        return m.group(1), m.group(3)=0A=
    else:=0A=
        raise 'Incorrect Subject line "%s"' % title=0A=
=0A=
=0A=
summary_re =3D re.compile(r"(.*?[.?!:])", re.DOTALL)=0A=
=0A=
def postMessage(msg):=0A=
    #from BasicAuthentication import BasicAuthTransport=0A=
    #transport =3D BasicAuthTransport('control','change;me;1')=0A=
    x =3D xmlrpclib.Server('http://silver.control.com/zope/')=0A=
=0A=
    title =3D msg.getheader('Subject')=0A=
    subject =3D title2subject(title)=0A=
    #print 'Title: %s\nSubject: %s' % (title, subject)=0A=
    res, title =3D cleanTitle(title)=0A=
    #print msg.getaddr('From')=0A=
    author,email =3D msg.getaddr('From')=0A=
=0A=
    import string=0A=
    try:=0A=
       string.index(msg.body, '\r\n')=0A=
    except ValueError:=0A=
       body =3D string.split(msg.body, '\n')=0A=
       msg.body =3D string.join(body, '\r\n')=0A=
=0A=
    summary =3D ''=0A=
    m =3D summary_re.match(msg.body)=0A=
    if m: summary =3D m.group(1)=0A=
    =0A=
    fp =3D open('/home/zope/alist/post.log', 'a+')=0A=
=0A=
    if subject:=0A=
        if res:=0A=
            fp.write('Reply: %s from %s\n' % (title, author))=0A=
            x.control_com.postSquishdotReply(title, author, msg.body, =
email, '', '',=0A=
                                             summary, subject, =
"Automation List")=0A=
        else:=0A=
            fp.write('Article: %s from %s\n' % (title, author))=0A=
            x.control_com.addPosting(title, author, msg.body, email, =
'', '',=0A=
                                     summary, subject, "Automation =
List")=0A=
    else:=0A=
        fp.write('Post failed: No subject in "%s from %s".\n' % (title, =
author))=0A=
=0A=
    fp.close()=0A=
=0A=
=0A=
msg =3D Message.IncomingMessage()=0A=
postMessage(msg)=0A=
=0A=

------_=_NextPart_000_01BF8208.B02093DC
Content-Type: application/octet-stream;
	name="paths.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="paths.py"

# -*- python -*-=0A=
=0A=
# Copyright (C) 1998 by the Free Software Foundation, Inc.=0A=
#=0A=
# This program is free software; you can redistribute it and/or=0A=
# modify it under the terms of the GNU General Public License=0A=
# as published by the Free Software Foundation; either version 2=0A=
# of the License, or (at your option) any later version.=0A=
# =0A=
# This program is distributed in the hope that it will be useful,=0A=
# but WITHOUT ANY WARRANTY; without even the implied warranty of=0A=
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the=0A=
# GNU General Public License for more details.=0A=
# =0A=
# You should have received a copy of the GNU General Public License=0A=
# along with this program; if not, write to the Free Software =0A=
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, =
USA.=0A=
=0A=
# This file becomes paths.py which is installed in may directories.  =
By=0A=
# importing this module, sys.path gets `hacked' so that the =
$prefix/Mailman=0A=
# directory is inserted at the start of that list.  That directory =
really=0A=
# contains the Mailman modules in package form.  This file exports =
two=0A=
# attributes that other modules may use to get the absolute path to =
the=0A=
# installed Mailman distribution.=0A=
=0A=
# some scripts expect this attribute to be in this module=0A=
prefix =3D '/home/mailman'=0A=
exec_prefix =3D '${prefix}'=0A=
=0A=
# work around a bogus autoconf 2.12 bug=0A=
if exec_prefix =3D=3D '${prefix}':=0A=
    exec_prefix =3D prefix=0A=
=0A=
# hack the path to include the parent directory of the $prefix/Mailman =
package=0A=
# directory.=0A=
import sys=0A=
sys.path.insert(0, prefix)=0A=

------_=_NextPart_000_01BF8208.B02093DC
Content-Type: application/octet-stream;
	name="SquishExternals.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="SquishExternals.py"

import string=0A=
import re=0A=
from zLOG import LOG=0A=
=0A=
junkwords =3D ['and', 'the', 'a']=0A=
=0A=
def massageSubject(subject):=0A=
    subj =3D string.split(string.lower(subject))=0A=
    result =3D []=0A=
    for word in subj:=0A=
        # Delete 're:', 'fw:', etc.=0A=
        if len(word) =3D=3D 3 and word[0] in string.lowercase and \=0A=
           word[1] in string.lowercase and word[2] =3D=3D ':':=0A=
            continue=0A=
        # Delete random noise=0A=
        if word in junkwords:=0A=
            continue=0A=
        result.append(word)=0A=
    return string.join(result)=0A=
=0A=
def postSquishdotReply(self, title, author, body, email=3D'', =
notify=3D'', file=3D'',=0A=
                       summary=3D'', subject=3D'', dept=3D'',=0A=
                       REQUEST=3DNone, RESPONSE=3DNone):=0A=
=0A=
    #LOG('Squishdot', 100, 'postSquishdotReply: self =3D %s' % =
`self`)=0A=
    results =3D self.__call__({'title':title, 'subject':subject, =
'op':'articles'})=0A=
=0A=
    #LOG('Squishdot', 100, 'postSquishdotReply: results =3D %s' % =
`results`)=0A=
    #LOG('Squishdot', 100, 'postSquishdotReply: len(results) =3D %d' % =
len(results))=0A=
=0A=
    if len(results):=0A=
=0A=
        article =3D None=0A=
        for res in results:=0A=
            #LOG('Squishdot', 100, '   find("%s", "%s")' % \=0A=
            #    (string.lower(res.title), =
string.lower(string.strip(title))))=0A=
            #if string.find(string.lower(res.title), =
string.lower(string.strip(title))) >=3D 0:=0A=
            #    article =3D res=0A=
            #    break=0A=
            ressubj =3D massageSubject(res.title)=0A=
            subj =3D massageSubject(title)=0A=
            #LOG('Squishdot', 100, '    "%s" =3D=3D "%s")' % (ressubj, =
subj))=0A=
            if ressubj =3D=3D subj:=0A=
                article =3D res=0A=
                break=0A=
        if article =3D=3D None:=0A=
            article =3D results[0]=0A=
            LOG('Squishdot', 100, 'postSquishdotReply: no match found, =
using "%s"' % article.title)=0A=
=0A=
        # If there are multiple Articles with the same title and =
subject,=0A=
        # just use the first one.=0A=
        #LOG('Squishdot', 100, 'postSquishdotReply: article =3D %s' % =
`article`)=0A=
        article.addPosting(title, author, body, email, notify, file,=0A=
                           REQUEST, RESPONSE)=0A=
        #LOG('Squishdot', 100, 'postSquishdotReply: added a Reply')=0A=
=0A=
    else:=0A=
        #LOG('Squishdot', 100, 'postSquishdotReply: not found so =
posting new ')=0A=
        self.addPosting(title, author, body, email, notify, file,     =
=0A=
                        summary, subject, dept, REQUEST, RESPONSE)=0A=
        #LOG('Squishdot', 100, 'postSquishdotReply: added an =
Article')=0A=
=0A=
    # It appears that calls via xmlrpclib throw:=0A=
    #   xmlrpclib.ProtocolError:=0A=
    #   <ProtocolError for silver.control.com/zope/: 500 Internal =
Server Error>=0A=
    # if a method called by it doesn't return a string.=0A=
    # This is bogus, but:=0A=
    return 'Done'=0A=

------_=_NextPart_000_01BF8208.B02093DC--