Merge lp:~acoconut/systers/flexible_essays into lp:systers

Proposed by Ana Cutillas
Status: Superseded
Proposed branch: lp:~acoconut/systers/flexible_essays
Merge into: lp:systers
Diff against target: 514 lines (+108/-81)
3 files modified
Mailman/Cgi/listinfo.py (+19/-6)
Mailman/DlistUtils.py (+88/-69)
bin/rmlist (+1/-6)
To merge this branch: bzr merge lp:~acoconut/systers/flexible_essays
Reviewer Review Type Date Requested Status
Robin J Needs Fixing
Review via email: mp+111405@code.launchpad.net

This proposal has been superseded by a proposal from 2012-06-26.

Description of the change

After week 1. You can manually change the questions in the pck file and the program reads the dictionary and displays the questions and the boxes.

To post a comment you must log in.
Revision history for this message
Robin J (robin-jeffries) wrote :

A suggestion for the future: when you put in a syslog statement, put in some identifying text, preferably the file that the message came from and what the data is. e.g.,

 syslog('info', "listinfo.py: essay question data for question %d = %s", i, mlist.questions[i][i+1][2])

In listinfo.py

I'm confused by your parameters --can you remind me what the datastructure is for mlist.questions (what the columns are? I don't understand how you can do things with mlist.questions[i][i+1][0] etc. I think this must have worked "by accident". )

I believe that mailman coding style requires 80 column line lengths. Can you shorten the long lines? (you may need to add some extra parens to make python understand that your new lines are continuation lines).

For things you are planning to do later (even if later is in the indefinite future, please start the comment with:
#TODO (your name):
that way it's easy to grep the code and find things you left for later.

As best I can tell, you didn't actually make any changes to DlistUtils.py. Is that correct? If not, can you point out the places you made changes (it looks like these were the changes to fix our nasty lower case bug; maybe you had to import that file onto your branch?)

Where do you define mlist.questions? (presumably in MailList.py, but why wasn't that included in your merge?) If you just manually created a mailing list with questions, please put that in the description of the change, showing exactly what is in that faked data structure.

Use the description of the change to tell your reviewers the things they need to know to understand this code (like the mlist.questions data structure)

review: Needs Fixing
Revision history for this message
Ana Cutillas (acoconut) wrote :

> Review: Needs Fixing
>
> A suggestion for the future: when you put in a syslog statement, put in
> some identifying text, preferably the file that the message came from and
> what the data is. e.g.,
>
> syslog('info', "listinfo.py: essay question data for question %d = %s",
> i, mlist.questions[i][i+1][2])
>

Mmm... I thought I had gotten rid of all the syslog statements, I was just
using them for my own testing purposes.

>
> In listinfo.py
>
> I'm confused by your parameters --can you remind me what the datastructure
> is for mlist.questions (what the columns are? I don't understand how you
> can do things with mlist.questions[i][i+1][0] etc. I think this must have
> worked "by accident". )
>

[{1: ['talk about yourself', 'talk', 'largebox', 300, 'top']}, {2: ['Are
you a woman?', 'woman', 'checkbox', 0, 'bottom']}]

So basically:

[{question_number: ['question description', 'short version of question',
'type of box', 'size of box', 'position']}]

Why do you think this works by accident?

>
> I believe that mailman coding style requires 80 column line lengths. Can
> you shorten the long lines? (you may need to add some extra parens to make
> python understand that your new lines are continuation lines).
>

Ok, I will. There are some files in the systers folders that have very long
lines too, should I shorten those too? I mean code that I didn't write but
has very long lines.

>
> For things you are planning to do later (even if later is in the
> indefinite future, please start the comment with:
> #TODO (your name):
> that way it's easy to grep the code and find things you left for later.
>
> As best I can tell, you didn't actually make any changes to DlistUtils.py.
> Is that correct? If not, can you point out the places you made changes
> (it looks like these were the changes to fix our nasty lower case bug;
> maybe you had to import that file onto your branch?)
>

I only modified listinfo.py for the changes for the first week.

>
> Where do you define mlist.questions? (presumably in MailList.py, but why
> wasn't that included in your merge?) If you just manually created a
> mailing list with questions, please put that in the description of the
> change, showing exactly what is in that faked data structure.
>

mlist.questions was something that mailman was already using. It used to
get the one question from the pickle file, now it gets the data structure I
posted earlier.

> Use the description of the change to tell your reviewers the things they
> need to know to understand this code (like the mlist.questions data
> structure)
>
>
> Please, let me know if all this made or not sense and I'll make the
changes and resubmit .

> --
> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/111405
> You are the owner of lp:~acoconut/systers/flexible_essays.
>

Revision history for this message
Robin J (robin-jeffries) wrote :

1. It's fine to leave the syslog statements in (you can make a grep for them at the end of the summer). If they happen to make it into production, we turn DEBUG off there, so there is no harm. And you never know when a bug will come back to you

2. For the way you march down the mlist.questions data structure, I don't understand how you got it to work. If I understand it correctly, given

[{1: ['talk about yourself', 'talk', 'largebox', 300, 'top']}, {2: ['Are
you a woman?', 'woman', 'checkbox', 0, 'bottom']}]

your tests are for mlist.questions[i][i+1][2]

the first time through that is mlist.questions[0][1][2] (in line 15 in the diff) -- that would be 'talk' above.)

The next time through it is mlist.questions[1][2][2] -- which doesn't exist, right? shouldn't your test (and other items) be mlist.questions[i][1][2]?

(also, I missed this earlier. It would be better code to have your loop statement in line 12 in the diff be
  for question in mlist.questions:
(range has to take that number an convert it to a location in the data structure, and this just marches down the data structure)

If you do that, then the test becomes something like
if question[1][2] ==...

(see how this is clearer? You could make it even clearer by making this a named structure (where you might have question.boxtype for the above), but for the small number of times you are going to reference it, it may not be worth it. But we may want to revisit this when we see how complex the admin UI is and how much improvement in clarity it gives)

3. For the updating of mlist.questions. I understand more now, but I think there is going to be an issue when you do the admin GUI -- this is not where things that are changed in the admin gui are stored (they are not part of mlist). This is not something you introduced, but it is something you will need to correct when you get to doing the admin GUI for this. Let's leave that issue until later.

Revision history for this message
Ana Cutillas (acoconut) wrote :

Yes, I see what you meant about the for loop and I just got done fixing it.
I had to add an index though, I'm going to push the new version so that you
can see it. I couldn't figure out a way of doing it without an index.

As for the lines length, there are lines a lot longer that I didn't write.
I made mine shorter, let me know if I should change the ones I didn't write
too.

Thanks Robin! I love learning little things like the for loop thing. :)

On Tue, Jun 26, 2012 at 7:26 AM, Robin J <email address hidden> wrote:

> 1. It's fine to leave the syslog statements in (you can make a grep for
> them at the end of the summer). If they happen to make it into production,
> we turn DEBUG off there, so there is no harm. And you never know when a
> bug will come back to you
>
> 2. For the way you march down the mlist.questions data structure, I don't
> understand how you got it to work. If I understand it correctly, given
>
> [{1: ['talk about yourself', 'talk', 'largebox', 300, 'top']}, {2: ['Are
> you a woman?', 'woman', 'checkbox', 0, 'bottom']}]
>
> your tests are for mlist.questions[i][i+1][2]
>
> the first time through that is mlist.questions[0][1][2] (in line 15 in
> the diff) -- that would be 'talk' above.)
>
> The next time through it is mlist.questions[1][2][2] -- which doesn't
> exist, right? shouldn't your test (and other items) be
> mlist.questions[i][1][2]?
>
> (also, I missed this earlier. It would be better code to have your loop
> statement in line 12 in the diff be
> for question in mlist.questions:
> (range has to take that number an convert it to a location in the data
> structure, and this just marches down the data structure)
>
> If you do that, then the test becomes something like
> if question[1][2] ==...
>
> (see how this is clearer? You could make it even clearer by making this a
> named structure (where you might have question.boxtype for the above), but
> for the small number of times you are going to reference it, it may not be
> worth it. But we may want to revisit this when we see how complex the
> admin UI is and how much improvement in clarity it gives)
>
> 3. For the updating of mlist.questions. I understand more now, but I
> think there is going to be an issue when you do the admin GUI -- this is
> not where things that are changed in the admin gui are stored (they are not
> part of mlist). This is not something you introduced, but it is something
> you will need to correct when you get to doing the admin GUI for this.
> Let's leave that issue until later.
>
> --
> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/111405
> You are the owner of lp:~acoconut/systers/flexible_essays.
>

lp:~acoconut/systers/flexible_essays updated
84. By Ana Cutillas <email address hidden>

Fixed the for loop that iterates through the list of questions, the
lines' length and other format issues.

Revision history for this message
Robin J (robin-jeffries) wrote :
Download full text (3.2 KiB)

That's what code reviews are for.

I will look at the changed code as soon as I can, but I have out of town
guests tonight, and I don't know if I can steal some cycles from work or
not.

Robin

On Tue, Jun 26, 2012 at 10:34 AM, Ana Cutillas <email address hidden> wrote:

> Yes, I see what you meant about the for loop and I just got done fixing it.
> I had to add an index though, I'm going to push the new version so that you
> can see it. I couldn't figure out a way of doing it without an index.
>
> As for the lines length, there are lines a lot longer that I didn't write.
> I made mine shorter, let me know if I should change the ones I didn't write
> too.
>
> Thanks Robin! I love learning little things like the for loop thing. :)
>
> On Tue, Jun 26, 2012 at 7:26 AM, Robin J <email address hidden> wrote:
>
> > 1. It's fine to leave the syslog statements in (you can make a grep for
> > them at the end of the summer). If they happen to make it into
> production,
> > we turn DEBUG off there, so there is no harm. And you never know when a
> > bug will come back to you
> >
> > 2. For the way you march down the mlist.questions data structure, I
> don't
> > understand how you got it to work. If I understand it correctly, given
> >
> > [{1: ['talk about yourself', 'talk', 'largebox', 300, 'top']}, {2: ['Are
> > you a woman?', 'woman', 'checkbox', 0, 'bottom']}]
> >
> > your tests are for mlist.questions[i][i+1][2]
> >
> > the first time through that is mlist.questions[0][1][2] (in line 15 in
> > the diff) -- that would be 'talk' above.)
> >
> > The next time through it is mlist.questions[1][2][2] -- which doesn't
> > exist, right? shouldn't your test (and other items) be
> > mlist.questions[i][1][2]?
> >
> > (also, I missed this earlier. It would be better code to have your loop
> > statement in line 12 in the diff be
> > for question in mlist.questions:
> > (range has to take that number an convert it to a location in the data
> > structure, and this just marches down the data structure)
> >
> > If you do that, then the test becomes something like
> > if question[1][2] ==...
> >
> > (see how this is clearer? You could make it even clearer by making this
> a
> > named structure (where you might have question.boxtype for the above),
> but
> > for the small number of times you are going to reference it, it may not
> be
> > worth it. But we may want to revisit this when we see how complex the
> > admin UI is and how much improvement in clarity it gives)
> >
> > 3. For the updating of mlist.questions. I understand more now, but I
> > think there is going to be an issue when you do the admin GUI -- this is
> > not where things that are changed in the admin gui are stored (they are
> not
> > part of mlist). This is not something you introduced, but it is
> something
> > you will need to correct when you get to doing the admin GUI for this.
> > Let's leave that issue until later.
> >
> > --
> >
> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/111405
> > You are the owner of lp:~acoconut/systers/flexible_essays.
> >
>
> --
> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/111405
> You are reviewing the pro...

Read more...

lp:~acoconut/systers/flexible_essays updated
85. By Ana Cutillas <email address hidden>

Changed the data structure to store the questions. Now it looks like:

[['Talk about yourself', 'talk', 'largebox', 300, 'top'], ['Are you a woman?', 'woman', 'checkbox', 0, 'bottom']]

86. By Ana Cutillas <email address hidden>

Changed the name of the variable questions to html_questions.

87. By Ana Cutillas <email address hidden>

Added changes to the DlistUtils.py file to handle the database
operations to the new table form_question.

88. By Ana Cutillas <email address hidden>

Added the database backend and the functionality to connect forms to the database.

89. By Ana Cutillas <email address hidden>

Made changes to the database and to the way we store the answers. Made
changes to the way we handle the information coming from the form.

90. By Ana Cutillas <email address hidden>

Cleaned up listinfo.py

91. By Ana Cutillas <email address hidden>

Cleaned up.

92. By Ana Cutillas <email address hidden>

Cleaned up DlistUtils and changed the error message in subscribe. Added
comments.

93. By Ana Cutillas <email address hidden>

Removed the unused function get_answer from DlistUtils.py

94. By Ana Cutillas <email address hidden>

Added changes to show the new essays in the pending subscriptions table.

Handle the possibility of a user trying to subscribe twice before the
subscription is reviewed.

95. By Ana Cutillas <email address hidden>

Added the functionality to check that the answers aren't longer than
what the database can hold.

96. By Ana Cutillas <email address hidden>

Added the error message saying that the user hasn't been subscribe to
the list when a user tries to subscribe with a pending subscription.

97. By Ana Cutillas <email address hidden>

Made the changes suggested by Robin: concerning error messages and
asserting the size of the answers.

Added the code to mark answers as accepted in the database when a user
is accepted.

98. By Ana Cutillas <email address hidden>

Changed the logic in the checking of answers in subscribe.py
Cleaned up DlistUtils.py

99. By Ana Cutillas <email address hidden>

Added support for setting answers to rejected in the database.

Unmerged revisions

99. By Ana Cutillas <email address hidden>

Added support for setting answers to rejected in the database.

98. By Ana Cutillas <email address hidden>

Changed the logic in the checking of answers in subscribe.py
Cleaned up DlistUtils.py

97. By Ana Cutillas <email address hidden>

Made the changes suggested by Robin: concerning error messages and
asserting the size of the answers.

Added the code to mark answers as accepted in the database when a user
is accepted.

96. By Ana Cutillas <email address hidden>

Added the error message saying that the user hasn't been subscribe to
the list when a user tries to subscribe with a pending subscription.

95. By Ana Cutillas <email address hidden>

Added the functionality to check that the answers aren't longer than
what the database can hold.

94. By Ana Cutillas <email address hidden>

Added changes to show the new essays in the pending subscriptions table.

Handle the possibility of a user trying to subscribe twice before the
subscription is reviewed.

93. By Ana Cutillas <email address hidden>

Removed the unused function get_answer from DlistUtils.py

92. By Ana Cutillas <email address hidden>

Cleaned up DlistUtils and changed the error message in subscribe. Added
comments.

91. By Ana Cutillas <email address hidden>

Cleaned up.

90. By Ana Cutillas <email address hidden>

Cleaned up listinfo.py

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Mailman/Cgi/listinfo.py'
2--- Mailman/Cgi/listinfo.py 2009-08-14 15:41:17 +0000
3+++ Mailman/Cgi/listinfo.py 2012-06-26 17:39:24 +0000
4@@ -22,6 +22,7 @@
5 import os
6 import cgi
7
8+from Mailman import DlistUtils
9 from Mailman import mm_cfg
10 from Mailman import Utils
11 from Mailman import MailList
12@@ -36,7 +37,6 @@
13 i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
14
15
16-
17
18 def main():
19 parts = Utils.GetPathPieces()
20 if not parts:
21@@ -62,7 +62,6 @@
22 list_listinfo(mlist, language)
23
24
25-
26
27 def listinfo_overview(msg=''):
28 # Present the general listinfo overview
29 hostname = Utils.get_domain()
30@@ -152,7 +151,6 @@
31 print doc.Format()
32
33
34-
35
36 def list_listinfo(mlist, lang):
37 # Generate list specific listinfo
38 doc = HeadlessDocument()
39@@ -190,9 +188,24 @@
40 # If activated, the content and presence of questions and a field to answer them. Added by Systers.
41 try:
42 if mlist.essay_enabled:
43- replacements['<mm-questions>'] = """<TR>
44- <TD COLSPAN="3">""" + mlist.questions
45- replacements['<mm-essay-box>'] = TextArea('essay', '', rows=8, cols=60).Format() + """ </TD></TR>"""# Added to support subscribe essays
46+ questions = [] # Added to support subscribe essays
47+ index = 1
48+ for question in mlist.questions:
49+ questions.append("""<TR>
50+ <TD COLSPAN="3">""" + question[index][0] + """</TD>""")
51+ if (question[index][2] == 'smallbox'):
52+ questions.append("""<TD>""" + TextArea(question[index][1],\
53+ '', rows=1, cols=60).Format() + """</TD></TR>""")
54+ elif (question[index][2] == 'largebox'):
55+ questions.append("""<TD>""" + TextArea(question[index][1], \
56+ '', rows=8, cols=60).Format() + """</TD></TR>""")
57+ elif (question[index][2] == 'checkbox'):
58+ #TODO (Ana Cutillas): consider creating a class like TextArea for checkboxes?
59+ questions.append("""<TD><input type='checkbox' name='""" + \
60+ question[index][1] + """' value ='""" + \
61+ question[index][1] + """'></TD></TR>""")
62+ index = index + 1
63+ replacements['<mm-questions>'] = ''.join(questions)
64 else:
65 replacements['<mm-questions>'] = ''
66 replacements['<mm-essay-box>'] = ''
67
68=== modified file 'Mailman/DlistUtils.py'
69--- Mailman/DlistUtils.py 2010-01-31 20:29:48 +0000
70+++ Mailman/DlistUtils.py 2012-06-26 17:39:24 +0000
71@@ -40,17 +40,17 @@
72 if self == classObject:
73 if self.store == None:#for the case when same object is used for calling more than one functions
74 self.store = Store(self.database)
75- result = f(self,*args)
76+ result = f(self, *args)
77 self.store.commit()
78 self.store.close()
79 self.store = None
80 else:#To handle the nesting issue
81- result = f(self,*args)
82+ result = f(self, *args)
83 self.store.commit()
84 else:#for the case whenever a new object calls its decorated member function
85 classObject = self
86 self.store = Store(self.database)
87- result = f(self,*args)
88+ result = f(self, *args)
89 self.store.commit()
90 self.store.close()
91 self.store = None
92@@ -60,14 +60,14 @@
93 #Define all the classes corresponding to the tables in the database
94 class Subscriber(object):
95 __storm_table__ = "subscriber"
96- subscriber_id = Int(primary = True,default = AutoReload)
97+ subscriber_id = Int(primary = True, default = AutoReload)
98 mailman_key = Unicode()
99 preference = Int(default = 1)
100 format = Int(default = 3)
101 deleted = Bool(default = False)
102 suppress = Int(default = 0)
103
104- def __init__(self,mlist):
105+ def __init__(self, mlist):
106 self.mlist = mlist
107 self.database = getConn(mlist)
108
109@@ -76,11 +76,11 @@
110 if addr == None:
111 return None
112
113- command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(addr.lower(),'utf-8'))\na = [(subscriber.subscriber_id) for subscriber in result]\n"
114+ command = "result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(addr.lower(),'utf-8'))\na = [(subscriber.subscriber_id) for subscriber in result]\n"
115 if DEBUG_MODE:
116 syslog('info', "DlistUtils:(getSubscriber_id_raw)executing query:\n%s", command)
117 #storm recognizes unicode only
118- result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(addr.lower(),"utf-8"))
119+ result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(addr,"utf-8"))
120 a = [(subscriber.subscriber_id) for subscriber in result]
121 if DEBUG_MODE:
122 syslog('info', 'value of a is: %s\n', a)
123@@ -136,70 +136,70 @@
124 @decfunc
125 def setDisable(self, member, flag):
126 """Disable/enable delivery based on mm_cfg.DisableDelivery"""
127- command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"
128+ command = "result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"
129 if DEBUG_MODE:
130- syslog('info', 'DlistUtils(setDisable):Executing query:\n%s\n Member whoes suppress value is to be found \n %s', command,member)
131- result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8"))
132+ syslog('info', 'DlistUtils(setDisable):Executing query:\n%s\n Member whose suppress value is to be found \n %s', command, member)
133+ result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,"utf-8"))
134 oldval = [(subscriber.suppress) for subscriber in result]
135 if oldval == []:
136 if DEBUG_MODE:
137 syslog('info','oldval is an empty list.\nThis can happen either because of\n 1)Permission issues (Do a: bin/check_perms)\n 2)Inconsistency between database and pickle files (A user is in the database but not in pickle files or vice versa,Do a bin/find_problems.py)')
138 oldval = oldval[0]
139 if DEBUG_MODE:
140- syslog('info','the value of oldval is %s:',oldval)
141+ syslog('info','the value of oldval is %s:', oldval)
142
143 if flag:
144 newval = oldval | 1 # Disable delivery
145 else:
146 newval = oldval & ~1 # Enable delivery
147 if DEBUG_MODE:
148- syslog('info','the value of newval is %s:',newval)
149- command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(suppress = newval)\n"
150+ syslog('info','the value of newval is %s:', newval)
151+ command = "self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8')).set(suppress = newval)\n"
152 if DEBUG_MODE:
153 syslog('info', 'DlistUtils(setDisable):Executing query:\n%s', command)
154- self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(suppress = newval)
155+ self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,"utf-8")).set(suppress = newval)
156
157 @decfunc
158 def setDigest(self, member, flag):
159 """Disable/enable delivery based on user digest status"""
160
161- command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"
162+ command = "result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"
163 if DEBUG_MODE:
164 syslog('info', 'DlistUtils(setDigest):Executing query:\n%s', command)
165- result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8"))
166+ result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,"utf-8"))
167 oldval = [(subscriber.suppress) for subscriber in result]
168 if oldval == []:
169 if DEBUG_MODE:
170 syslog('info','oldval is an empty list.\nThis can happen either because of\n 1)permission issues (Do a: bin/check_perms)\n 2)Inconsistency between database and pickle files (A user is in the database but not in pickle files or vice versa,Do a bin/find_problems.py)')
171 oldval = oldval[0]
172 if DEBUG_MODE:
173- syslog('info','value of oldval %s:',oldval)
174+ syslog('info','value of oldval %s:', oldval)
175
176 if flag:
177 newval = oldval | 2 # Suppress delivery (in favor of digests)
178 else:
179 newval = oldval & ~2 # Enable delivery (instead of digests)
180
181- command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(suppress = newval)\n"
182+ command = "self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8')).set(suppress = newval)\n"
183 if DEBUG_MODE:
184 syslog('info', 'DlistUtils(setDigest):Executing query:\n%s', command)
185- self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(suppress = newval)
186+ self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,"utf-8")).set(suppress = newval)
187
188 @decfunc
189 def changePreference(self, member, preference):
190 """Change a user's default preference for new threads."""
191- command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(preference = preference)\n"
192+ command = "self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8')).set(preference = preference)\n"
193 if DEBUG_MODE:
194 syslog('info', 'DlistUtils(changePreference):Executing query:\n%s', command)
195- self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(preference = preference)
196+ self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,"utf-8")).set(preference = preference)
197
198 @decfunc
199 def changeFormat(self, member, format):
200 """Change a user's preferred delivery format (plain text and/or html)"""
201- command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(format = format)\n"
202+ command = "self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8')).set(format = format)\n"
203 if DEBUG_MODE:
204 syslog('info', 'DlistUtils(changeFormat):Executing query:\n%s', command)
205- self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(format = format)
206+ self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,"utf-8")).set(format = format)
207
208 ### We have to watch out for a tricky case (that actually happened):
209 ### User foo@bar.com changes her address to something already in the
210@@ -207,32 +207,32 @@
211 @decfunc
212 def changeAddress(self, oldaddr, newaddr):
213 """Change email address in SQL database"""
214- #if DEBUG_MODE:
215- #syslog('info', "Changing email address on %s from '%s' to '%s'", self.mlist.internal_name(), oldaddr, newaddr)
216+ if DEBUG_MODE:
217+ syslog('info', "Changing email address on %s from '%s' to '%s'", self.mlist.internal_name(), oldaddr, newaddr)
218 ## Check if newaddr is in sql database
219- num_matches = self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8")).count()
220+ num_matches = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(newaddr.lower(),"utf-8")).count()
221
222 if num_matches > 1:
223- #syslog('error', 'Multiple users with same key (%s): %s', self.mlist.internal_name(), newaddr)
224+ syslog('error', 'Multiple users with same key (%s): %s', self.mlist.internal_name(), newaddr)
225 return
226 if num_matches == 1:
227 self.mergeSubscribers(oldaddr, newaddr)
228
229- command = "num_matches = self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,'utf-8')).count()\nself.store.find(Subscriber,Subscriber.mailman_key == unicode(oldaddr,'utf-8')).set(mailman_key = unicode(newaddr,'utf-8'))\nself.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,'utf-8')).set(deleted = False)\n"
230+ command = "num_matches = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(newaddr,'utf-8')).count()\nself.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(oldaddr,'utf-8')).set(mailman_key = unicode(newaddr,'utf-8'))\nself.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(newaddr.lower(),'utf-8')).set(deleted = False)\n"
231 if DEBUG_MODE:
232 syslog('info', 'DlistUtils(changeAddress):Executing query:\n%s', command)
233- self.store.find(Subscriber,Subscriber.mailman_key == unicode(oldaddr,"utf-8")).set(mailman_key = unicode(newaddr,"utf-8"))
234+ self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(oldaddr,"utf-8")).set(mailman_key = unicode(newaddr,"utf-8"))
235 self.store.commit()
236- self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8")).set(deleted = False)
237+ self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(newaddr.lower(),"utf-8")).set(deleted = False)
238
239 @decfunc
240 def unsubscribeFromList(self, key):
241 """Indicate that a user has unsubscribed by setting the deleted flag in the subscriber table."""
242
243- command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,'utf-8')).set(deleted = True)\nself.store.find(Alias,Alias.subscriber_id == subscriber_id).remove()\n"
244+ command = "self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key,'utf-8')).set(deleted = True)\nself.store.find(Alias,Alias.subscriber_id == subscriber_id).remove()\n"
245 if DEBUG_MODE:
246 syslog('info', 'DlisUtils(unsubscribeFromList):Executing query:\n%s', command)
247- self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8")).set(deleted = True)
248+ self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key,"utf-8")).set(deleted = True)
249 # get the subscriber id from the mailman_key & delete all aliases
250 subscriber_id = self.getSubscriber_id_raw(key)
251
252@@ -245,20 +245,27 @@
253 def subscribeToList(self, key):
254 """Add a member to the subscriber database, or change the record from deleted if it was already present."""
255
256- command = "count = (self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,'utf-8'))).count()\n"
257+ command = "count = (self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),'utf-8'))).count()\n"
258 if DEBUG_MODE:
259 syslog('info', 'DlistUtils(subscribeToList):Executing query:\n%s', command)
260 # First see if member is subscribed with deleted field = false
261- count = (self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8"))).count()
262+ count = (self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),"utf-8"))).count()
263
264 if count:
265- command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,'utf-8')).set(deleted = False)"
266+ command = "self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),'utf-8')).set(deleted = False)"
267 if DEBUG_MODE:
268 syslog('info', 'DlistUtils(subscribeToList):Executing query:\n%s', command)
269- self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8")).set(deleted = False)
270+ self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),"utf-8")).set(deleted = False)
271+
272+ result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),"utf-8"))
273+ Email = [(subscriber.mailman_key) for subscriber in result]
274+ Email = Email[0]
275+ if Email != key: #That is if one was in lowercase and other in uppercase then update mailman_key with case sensitivity same as key
276+ self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),"utf-8")).set(mailman_key = unicode(key,"utf-8"))
277+
278 else:
279 # format is text-only (1) by default
280- command = "subscriber = self.store.add(Subscriber())\nsubscriber.mailman_key = unicode(key,'utf-8')\nsubscriber.preference = 1\nsubscriber.deleted = False\nsubscriber.format = 1\nsubscriber.suppress = 0"
281+ command = "subscriber = self.store.add(Subscriber())\nmailman_key = unicode(key,'utf-8')\npreference = 1\ndeleted = False\nformat = 1\nsuppress = 0"
282 if DEBUG_MODE:
283 syslog('info', 'DlistUtils(subscribeToList)Executing query:\n%s', command)
284 self.mailman_key = unicode(key,"utf-8")
285@@ -273,12 +280,12 @@
286 # use the original subscriber id as the ID going forward
287 if DEBUG_MODE:
288 syslog('info', 'Executing commands of mergeSubscribers:')
289- result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(oldaddr,"utf-8"))
290+ result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(oldaddr,"utf-8"))
291 new_id = [(subscriber.subscriber_id) for subscriber in result]
292 new_id = new_id[0]
293 if DEBUG_MODE:
294 syslog('info', 'the value of new_id: %s', new_id)
295- result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8"))
296+ result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(newaddr.lower(),"utf-8"))
297 obsolete_id = [(subscriber.subscriber_id) for subscriber in result]
298 obsolete_id = obsolete_id[0]
299 if DEBUG_MODE:
300@@ -291,13 +298,13 @@
301
302 class Message(object):
303 __storm_table__ = "message"
304- message_id = Int(primary = True,default = AutoReload)
305+ message_id = Int(primary = True, default = AutoReload)
306 sender_id = Int()
307- subscriber = Reference(sender_id,Subscriber.subscriber_id)
308+ subscriber = Reference(sender_id, Subscriber.subscriber_id)
309 subject = Unicode()
310 thread_id = Int()
311
312- def __init__(self,mlist):
313+ def __init__(self, mlist):
314 self.mlist = mlist
315 self.database = getConn(mlist)
316
317@@ -314,7 +321,7 @@
318 threadID = -1
319 command = "message = self.store.add(Message())\nmessage.sender_id = senderID\nmessage.thread_id = threadID\nmessage.subject = unicode(subject,'utf-8')\nmessageID = self.store.find(Message).max(Message.message_id)\n"
320 if DEBUG_MODE:
321- syslog('info','DlistUtils:(createMessage)executing query:\n%s',command)
322+ syslog('info','DlistUtils:(createMessage)executing query:\n%s', command)
323 #message_id has autoreload set,its value will be serially updated in database
324 self.sender_id = senderID
325 self.thread_id = threadID
326@@ -322,19 +329,19 @@
327 self.store.add(self)
328 messageID = self.store.find(Message).max(Message.message_id)
329 if DEBUG_MODE:
330- syslog('info','Result of query(messageID) is: %s\n',messageID)
331+ syslog('info','Result of query(messageID) is: %s\n', messageID)
332 return messageID
333
334 class Thread(object):
335 __storm_table__ = "thread"
336- thread_id = Int(primary = True,default = AutoReload)
337+ thread_id = Int(primary = True, default = AutoReload)
338 thread_name = Unicode()
339 base_message_id = Int()
340- message = Reference(base_message_id,Message.message_id)
341+ message = Reference(base_message_id, Message.message_id)
342 status = Int(default = 0)
343 parent = Int()
344
345- def __init__(self,mlist):
346+ def __init__(self, mlist):
347 self.mlist = mlist
348 self.database = getConn(mlist)
349
350@@ -347,7 +354,7 @@
351 senderID = subscriber.getSubscriber_id(msg, msgdata, safe=1, loose=1)
352 command = "thread = self.store.add(Thread())\nthread.base_message_id = msgdata['message_id']\nthreadID = self.store.find(Thread).max(Thread.thread_id)\nself.store.find(Message,Message.message_id == msgdata['message_id']).set(thread_id = threadID)\n"
353 if DEBUG_MODE:
354- syslog('info','DlistUtils:(createThread)executing query:\n%s',command)
355+ syslog('info','DlistUtils:(createThread)executing query:\n%s', command)
356 self.base_message_id = msgdata['message_id']
357 self.store.add(self)
358 threadID = self.store.find(Thread).max(Thread.thread_id)
359@@ -370,7 +377,7 @@
360 threadBase = threadBase[:13]
361 command = "num = self.store.find(Thread,Thread.thread_name == unicode(threadBase,'utf-8')).count()\nself.store.find(Thread,Thread.thread_id == threadID).set(thread_name = threadName)\n"
362 if DEBUG_MODE:
363- syslog('info','DlistUtils:(createThread)executing query:\n%s',command)
364+ syslog('info','DlistUtils:(createThread)executing query:\n%s', command)
365 # If the threadBase is not unique, make threadBase unique by appending a number
366 num = self.store.find(Thread,Thread.thread_name.like(unicode(threadBase + "%","utf-8"))).count()
367 if not num:
368@@ -405,26 +412,24 @@
369 msg['To'] = '%s+%s@%s' % (self.mlist.internal_name(),
370 name,
371 self.mlist.host_name)
372- for i in (1,2):
373+ for i in (1, 2):
374 # different footers for different prefs, so we need to queue separately
375 if(i==1):
376 #For condition where preference = True
377 pref=True
378- # command = "lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]\n"
379- # if DEBUG_MODE:
380- # syslog('info', 'DlistUtils:(newThread)executing query:\n%sfor pref = true\n', command)
381+ if DEBUG_MODE:
382+ syslog('info', 'DlistUtils:(newThread)executing query:\nfor pref = true\n\n')
383 if(i==2):
384 #For condition where preference = False
385 pref=False
386- # command = "lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]\n"
387- # if DEBUG_MODE:
388- # syslog('info', 'DlistUtils:(newThread)executing query:\n%sfor pref = false\n', command)
389+ if DEBUG_MODE:
390+ syslog('info', 'DlistUtils:(newThread)executing query:\nfor pref = false\n\n')
391
392 #Execute a SELECT statement, to find the list of matching subscribers.
393 result_new_sql = self.store.find(Subscriber,And(Subscriber.preference == pref,Subscriber.deleted == False,Subscriber.suppress == 0))
394 lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]
395 if DEBUG_MODE:
396- syslog('info', 'value of lists: %s\n', lists)
397+ syslog('info','value of lists: %s\n', lists)
398 self.email_recipients(msg, msgdata, lists, pref)
399
400 # Make original message go to nobody (but be archived)
401@@ -514,12 +519,12 @@
402 override = Override(self.mlist)
403 override.override(msg, msgdata, 0)
404
405- def alphanumericOnly(self,s):
406+ def alphanumericOnly(self, s):
407 """Filter any non-letter characters from a string"""
408 result = [letter for letter in s if letter in string.ascii_letters or letter in string.digits]
409 return string.join(result, '')
410
411- def subjectToName(self,subject, threadID):
412+ def subjectToName(self, subject, threadID):
413 """Return a lower-case name for a new thread based on the subject, if present, or on the threadID"""
414 result = None
415
416@@ -589,9 +594,9 @@
417 __storm_table__ = "alias"
418 pseudonym = Unicode(primary = True)
419 subscriber_id = Int()
420- subscriber = Reference(subscriber_id,Subscriber.subscriber_id)
421+ subscriber = Reference(subscriber_id, Subscriber.subscriber_id)
422
423- def __init__(self,mlist):
424+ def __init__(self, mlist):
425 self.mlist = mlist
426 self.database = getConn(mlist)
427
428@@ -604,8 +609,8 @@
429 syslog('info', 'DlistUtils(canonicalize_sender)Executing query:\n%s', command)
430
431 for alias in aliases:
432- #if DEBUG_MODE:
433- #syslog('info', 'Checking if <%s> is an alias', alias)
434+ if DEBUG_MODE:
435+ syslog('info', 'Checking if <%s> is an alias', alias)
436 result = self.store.find((Subscriber,Alias),And(Alias.pseudonym == unicode(alias,'utf-8'),Subscriber.subscriber_id == Alias.subscriber_id))
437 lists = [(subscriber.mailman_key.encode('utf-8')) for (subscriber,alias) in result]
438
439@@ -654,12 +659,12 @@
440 __storm_table__ = "override"
441 __storm_primary__ = "subscriber_id", "thread_id"
442 subscriber_id = Int()
443- subscriber = Reference(subscriber_id,Subscriber.subscriber_id)
444+ subscriber = Reference(subscriber_id, Subscriber.subscriber_id)
445 thread_id = Int()
446- thread = Reference(thread_id,Thread.thread_id)
447+ thread = Reference(thread_id, Thread.thread_id)
448 preference = Int()
449
450- def __init__(self,mlist):
451+ def __init__(self, mlist):
452 self.mlist = mlist
453 self.database = getConn(mlist)
454
455@@ -709,10 +714,9 @@
456
457 return 1
458
459- def overrideURL(self,list_name, host_name, thread_reference, preference):
460+ def overrideURL(self, list_name, host_name, thread_reference, preference):
461 return 'http://%s/mailman/options/%s?override=%s&preference=%d' % (host_name, list_name, thread_reference, preference)
462
463-
464 #Functions Independent of the database tables in DlistUtils
465
466 def GetEmailAddresses(mlist, subscribers):
467@@ -752,13 +756,22 @@
468
469 def _create_database(name):
470 """Create a database. This requires us to not be in a transaction."""
471- conn = pgdb.connect(host='localhost', database='postgres',user='mailman', password='mailman')
472+ conn = pgdb.connect(host='localhost', database='postgres', user='mailman', password='mailman')
473 old_iso_lvl = conn.isolation_level
474 conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
475 cursor = conn.cursor()
476 cursor.execute('create database "%s"' % name)
477 conn.set_isolation_level(old_iso_lvl)
478
479+def _remove_database(name):
480+ """Remove a database. This requires us to not be in a transaction."""
481+ conn = pgdb.connect(host='localhost', database='postgres', user='mailman', password='mailman')
482+ old_iso_lvl = conn.isolation_level
483+ conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
484+ cursor = conn.cursor()
485+ cursor.execute('DROP DATABASE "%s"' % name)
486+ conn.set_isolation_level(old_iso_lvl)
487+
488 def executeSQL(mlist, commands):
489 """Execute a sequence of SQL commands that create tables in the database."""
490 database = getConn(mlist)
491@@ -834,3 +847,9 @@
492 mlist.Save()
493 if alreadyLocked == 0:
494 mlist.Unlock()
495+
496+def remove_dlist(listname):
497+ """ Deletes the corresponding postgres database and tables."""
498+ _remove_database(listname)
499+ if DEBUG_MODE:
500+ syslog('info', "Database %s removed\n", listname)
501
502=== modified file 'bin/rmlist'
503--- bin/rmlist 2009-07-31 10:49:01 +0000
504+++ bin/rmlist 2012-06-26 17:39:24 +0000
505@@ -152,12 +152,7 @@
506
507 # Remove the database in PostgreSQL if it was a dynamic list. Added by Systers.
508 try:
509- conn = DlistUtils.getConn(None)
510- old_iso_lvl = conn.isolation_level
511- conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
512- cursor = conn.cursor()
513- cursor.execute('DROP DATABASE "%s"' % listname)
514- conn.set_isolation_level(old_iso_lvl)
515+ DlistUtils.remove_dlist(listname)
516 except:
517 syslog('error', 'Tried to remove the database for %s, no such database (ok if it was a non dlist). If it was a dlist, the postgreSQL database for this list needs to be removed manually.' % listname)
518

Subscribers

People subscribed via source and target branches