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: 521 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
beachbrake Pending
Robin J Pending
Review via email: mp+112331@code.launchpad.net

This proposal supersedes a proposal from 2012-06-26.

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

Description of the change

I changed the data structure to store the current version of questions. Now it looks like this:

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

And then changed the code to loop through this information.

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

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 : Posted in a previous version of this proposal

> 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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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.
>

Revision history for this message
Ana Cutillas (acoconut) wrote : Posted in a previous version of this proposal
Download full text (29.7 KiB)

Mm, ignore the import of DlistUtils for revision, I accidentally left it
there from when I was trying to make the STORM stuff work. Sorry!

On Tue, Jun 26, 2012 at 7:46 PM, Ana Cutillas <email address hidden> wrote:

> Ana Cutillas has proposed merging lp:~acoconut/systers/flexible_essays
> into lp:systers.
>
> Requested reviews:
> Robin J (robin-jeffries)
>
> For more details, see:
> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/112178
>
> I fixed everything Robin told me too:
>
> - The for loop that goes through the list of questions.
> - The length of the lines I wrote.
> - Deleted the syslog statements I wrote when I was debugging because they
> weren't really useful.
> - Added a TODO line.
> --
> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/112178
> You are the owner of lp:~acoconut/systers/flexible_essays.
>
> === modified file 'Mailman/Cgi/listinfo.py'
> --- Mailman/Cgi/listinfo.py 2009-08-14 15:41:17 +0000
> +++ Mailman/Cgi/listinfo.py 2012-06-26 17:45:29 +0000
> @@ -22,6 +22,7 @@
> import os
> import cgi
>
> +from Mailman import DlistUtils
> from Mailman import mm_cfg
> from Mailman import Utils
> from Mailman import MailList
> @@ -36,7 +37,6 @@
> i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
>
>
> -
> def main():
> parts = Utils.GetPathPieces()
> if not parts:
> @@ -62,7 +62,6 @@
> list_listinfo(mlist, language)
>
>
> -
> def listinfo_overview(msg=''):
> # Present the general listinfo overview
> hostname = Utils.get_domain()
> @@ -152,7 +151,6 @@
> print doc.Format()
>
>
> -
> def list_listinfo(mlist, lang):
> # Generate list specific listinfo
> doc = HeadlessDocument()
> @@ -190,9 +188,24 @@
> # If activated, the content and presence of questions and a field to
> answer them. Added by Systers.
> try:
> if mlist.essay_enabled:
> - replacements['<mm-questions>'] = """<TR>
> - <TD COLSPAN="3">""" + mlist.questions
> - replacements['<mm-essay-box>'] = TextArea('essay', '',
> rows=8, cols=60).Format() + """ </TD></TR>"""# Added to support
> subscribe essays
> + questions = [] # Added to support subscribe essays
> + index = 1
> + for question in mlist.questions:
> + questions.append("""<TR>
> + <TD COLSPAN="3">""" + question[index][0] + """</TD>""")
> + if (question[index][2] == 'smallbox'):
> + questions.append("""<TD>""" +
> TextArea(question[index][1],\
> + '', rows=1, cols=60).Format() +
> """</TD></TR>""")
> + elif (question[index][2] == 'largebox'):
> + questions.append("""<TD>""" +
> TextArea(question[index][1], \
> + '', rows=8, cols=60).Format() +
> """</TD></TR>""")
> + elif (question[index][2] == 'checkbox'):
> + #TODO (Ana Cutillas): consider creating a class like
> TextArea for checkboxes?
> + questions.append("""<TD><input type='checkbox'
> name='""" + \
> + question[index][1] + """' value ='""" + \
> + ...

Revision history for this message
Robin J (robin-jeffries) wrote : Posted in a previous version of this proposal
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...

Revision history for this message
Robin J (robin-jeffries) wrote : Posted in a previous version of this proposal
Download full text (32.0 KiB)

Ana, please test things before you send out the code for review.

And also add priya and me both to all the reviews. We will trade off who
is reviewing things, but it's good for both of us to see the others
comments.

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

> Mm, ignore the import of DlistUtils for revision, I accidentally left it
> there from when I was trying to make the STORM stuff work. Sorry!
>
> On Tue, Jun 26, 2012 at 7:46 PM, Ana Cutillas <email address hidden> wrote:
>
>> Ana Cutillas has proposed merging lp:~acoconut/systers/flexible_essays
>> into lp:systers.
>>
>> Requested reviews:
>> Robin J (robin-jeffries)
>>
>> For more details, see:
>> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/112178
>>
>> I fixed everything Robin told me too:
>>
>> - The for loop that goes through the list of questions.
>> - The length of the lines I wrote.
>> - Deleted the syslog statements I wrote when I was debugging because they
>> weren't really useful.
>> - Added a TODO line.
>> --
>> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/112178
>> You are the owner of lp:~acoconut/systers/flexible_essays.
>>
>> === modified file 'Mailman/Cgi/listinfo.py'
>> --- Mailman/Cgi/listinfo.py 2009-08-14 15:41:17 +0000
>> +++ Mailman/Cgi/listinfo.py 2012-06-26 17:45:29 +0000
>> @@ -22,6 +22,7 @@
>> import os
>> import cgi
>>
>> +from Mailman import DlistUtils
>> from Mailman import mm_cfg
>> from Mailman import Utils
>> from Mailman import MailList
>> @@ -36,7 +37,6 @@
>> i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
>>
>>
>> -
>> def main():
>> parts = Utils.GetPathPieces()
>> if not parts:
>> @@ -62,7 +62,6 @@
>> list_listinfo(mlist, language)
>>
>>
>> -
>> def listinfo_overview(msg=''):
>> # Present the general listinfo overview
>> hostname = Utils.get_domain()
>> @@ -152,7 +151,6 @@
>> print doc.Format()
>>
>>
>> -
>> def list_listinfo(mlist, lang):
>> # Generate list specific listinfo
>> doc = HeadlessDocument()
>> @@ -190,9 +188,24 @@
>> # If activated, the content and presence of questions and a field to
>> answer them. Added by Systers.
>> try:
>> if mlist.essay_enabled:
>> - replacements['<mm-questions>'] = """<TR>
>> - <TD COLSPAN="3">""" + mlist.questions
>> - replacements['<mm-essay-box>'] = TextArea('essay', '',
>> rows=8, cols=60).Format() + """ </TD></TR>"""# Added to support
>> subscribe essays
>> + questions = [] # Added to support subscribe essays
>> + index = 1
>>
>
No, you don't need index; actually, index should always be 1

 for the data structure

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

the first time through, question is
{1: ['talk about yourself', 'talk', 'largebox', 300, 'top']}
and the second time through it is
{2: ['Are you a woman?', 'woman', 'checkbox', 0, 'bottom']}

To address 'largebox' and 'checkbox' you want
question[1][2] not
question[index][2]

(If you had tested your code, you would have seen that this causes problems)

(for now, I...

Revision history for this message
Ana Cutillas (acoconut) wrote : Posted in a previous version of this proposal
Download full text (34.4 KiB)

Mmm... I definitely tested it before submitting and I actually went back to
it right now and it is working in my environment. AND when I change index
for 1, it doesn't work. I even opened a python interpreter earlier today
and tested the for loop without index and with index and it wouldn't work
with index. I can write up my testes again and show them to you.

It's almost 3am right now (but 82ºF (28 ºC) - I am a Spaniard but heat...
it kills me. I don´t know if I'll ever be able to handle it.) but I will
try the data structure you proposed tomorrow and let you know how that
goes. You're right that I'm not really doing anything with the question
numbers.

On Tue, Jun 26, 2012 at 11:58 PM, Robin J <email address hidden> wrote:

> Ana, please test things before you send out the code for review.
>
> And also add priya and me both to all the reviews. We will trade off who
> is reviewing things, but it's good for both of us to see the others
> comments.
>
>
> On Tue, Jun 26, 2012 at 10:48 AM, Ana Cutillas <email address hidden>
> wrote:
>
> > Mm, ignore the import of DlistUtils for revision, I accidentally left it
> > there from when I was trying to make the STORM stuff work. Sorry!
> >
> > On Tue, Jun 26, 2012 at 7:46 PM, Ana Cutillas <email address hidden>
> wrote:
> >
> >> Ana Cutillas has proposed merging lp:~acoconut/systers/flexible_essays
> >> into lp:systers.
> >>
> >> Requested reviews:
> >> Robin J (robin-jeffries)
> >>
> >> For more details, see:
> >>
> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/112178
> >>
> >> I fixed everything Robin told me too:
> >>
> >> - The for loop that goes through the list of questions.
> >> - The length of the lines I wrote.
> >> - Deleted the syslog statements I wrote when I was debugging because
> they
> >> weren't really useful.
> >> - Added a TODO line.
> >> --
> >>
> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/112178
> >> You are the owner of lp:~acoconut/systers/flexible_essays.
> >>
> >> === modified file 'Mailman/Cgi/listinfo.py'
> >> --- Mailman/Cgi/listinfo.py 2009-08-14 15:41:17 +0000
> >> +++ Mailman/Cgi/listinfo.py 2012-06-26 17:45:29 +0000
> >> @@ -22,6 +22,7 @@
> >> import os
> >> import cgi
> >>
> >> +from Mailman import DlistUtils
> >> from Mailman import mm_cfg
> >> from Mailman import Utils
> >> from Mailman import MailList
> >> @@ -36,7 +37,6 @@
> >> i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
> >>
> >>
> >> -
> >> def main():
> >> parts = Utils.GetPathPieces()
> >> if not parts:
> >> @@ -62,7 +62,6 @@
> >> list_listinfo(mlist, language)
> >>
> >>
> >> -
> >> def listinfo_overview(msg=''):
> >> # Present the general listinfo overview
> >> hostname = Utils.get_domain()
> >> @@ -152,7 +151,6 @@
> >> print doc.Format()
> >>
> >>
> >> -
> >> def list_listinfo(mlist, lang):
> >> # Generate list specific listinfo
> >> doc = HeadlessDocument()
> >> @@ -190,9 +188,24 @@
> >> # If activated, the content and presence of questions and a field to
> >> answer them. Added by Systers.
> >> try:
> >> if mlist.essay_enabled:
> >> - replacements['<mm-questions>'] = """<TR>
> >> - ...

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

OK, we are definitely making progress. Some questions and one change request:
   - what do fields 3 and 4 mean in your data structure (0, and 'bottom' for the checkbox?)
   - I'm not sure what you are doing for the checkbox (because I don't really understand that html). The part that confuses me is that you are using question[1] there twice. One part I think you are naming the checkbox, but why the second time? It seems to also be the value?

My requested change is that questions is too similar to mlist.questions. I think the code will be much easier for someone else to read if you change that to something like html_questions

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

On Thu, Jun 28, 2012 at 6:51 AM, Robin J <email address hidden> wrote:

> OK, we are definitely making progress. Some questions and one change
> request:
> - what do fields 3 and 4 mean in your data structure (0, and 'bottom'
> for the checkbox?)
>

I am not really doing anything with them yet. They are there for when I
start adding options to make the forms beautiful and let the admin move the
boxes around. Field 3 is the size of the box, a checkbox would look the
same with size 3000 or 0, so we can leave it to 0. Field 4 is the relative
position, I haven't really given it a lot of thought yet, I am currently
printing them in the order they appear in the list. Do you think it would
be better if I took those fields out of the data structure for now if I'm
not using them yet?

  - I'm not sure what you are doing for the checkbox (because I don't
> really understand that html). The part that confuses me is that you are
> using question[1] there twice. One part I think you are naming the
> checkbox, but why the second time? It seems to also be the value?
>
> According to what I read on the internet, there are two fields in the html
syntax to add checkboxes: name and value. Name is just the name of the
checkbox, and value is the value that is submitted if the checkbox is
checked. That's why I'm using the same string for both.

So the HTML code for a checkbox would look like this:

<TD><input type='checkbox' name='woman' value='woman'></TD>

> My requested change is that questions is too similar to mlist.questions.
> I think the code will be much easier for someone else to read if you
> change that to something like html_questions
>

Ok, changed! Let me know what you think about the extra 3 and 4 fields so I
can either change that or resubmit this again.

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

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

On Thu, Jun 28, 2012 at 3:41 AM, Ana Cutillas <email address hidden> wrote:

> On Thu, Jun 28, 2012 at 6:51 AM, Robin J <email address hidden> wrote:
>
> > OK, we are definitely making progress. Some questions and one change
> > request:
> > - what do fields 3 and 4 mean in your data structure (0, and 'bottom'
> > for the checkbox?)
> >
>
> I am not really doing anything with them yet. They are there for when I
> start adding options to make the forms beautiful and let the admin move the
> boxes around. Field 3 is the size of the box, a checkbox would look the
> same with size 3000 or 0, so we can leave it to 0. Field 4 is the relative
> position, I haven't really given it a lot of thought yet, I am currently
> printing them in the order they appear in the list. Do you think it would
> be better if I took those fields out of the data structure for now if I'm
> not using them yet?
>

No, feel free to keep them, since it doesn't change the way you interact
with the rest of the data structure. My guess is that you may need to
change some of them (probably a relative position than an absolute one),
but that can happen later.

>
> - I'm not sure what you are doing for the checkbox (because I don't
> > really understand that html). The part that confuses me is that you are
> > using question[1] there twice. One part I think you are naming the
> > checkbox, but why the second time? It seems to also be the value?
> >
> > According to what I read on the internet, there are two fields in the
> html
> syntax to add checkboxes: name and value. Name is just the name of the
> checkbox, and value is the value that is submitted if the checkbox is
> checked. That's why I'm using the same string for both.
>
> So the HTML code for a checkbox would look like this:
>
> <TD><input type='checkbox' name='woman' value='woman'></TD>
>

OK, get it now. I can't see any harm in using the same value for both of
them, but you might keep this in mind if you run into a bug -- that the
wrong field maybe is being addressed.

>
> > My requested change is that questions is too similar to mlist.questions.
> > I think the code will be much easier for someone else to read if you
> > change that to something like html_questions
> >
>
> Ok, changed! Let me know what you think about the extra 3 and 4 fields so I
> can either change that or resubmit this again.
>
> Go for it.

>
> > --
> >
> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/112331
> > You are the owner of lp:~acoconut/systers/flexible_essays.
> >
>
> --
> https://code.launchpad.net/~acoconut/systers/flexible_essays/+merge/112331
> You are requested to review the proposed merge of
> lp:~acoconut/systers/flexible_essays into lp:systers.
>

lp:~acoconut/systers/flexible_essays updated
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
=== modified file 'Mailman/Cgi/listinfo.py'
--- Mailman/Cgi/listinfo.py 2009-08-14 15:41:17 +0000
+++ Mailman/Cgi/listinfo.py 2012-06-28 22:40:23 +0000
@@ -22,6 +22,7 @@
22import os22import os
23import cgi23import cgi
2424
25from Mailman import DlistUtils
25from Mailman import mm_cfg26from Mailman import mm_cfg
26from Mailman import Utils27from Mailman import Utils
27from Mailman import MailList28from Mailman import MailList
@@ -36,7 +37,6 @@
36i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)37i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
3738
3839
39
4040
41def main():41def main():
42 parts = Utils.GetPathPieces()42 parts = Utils.GetPathPieces()
43 if not parts:43 if not parts:
@@ -53,6 +53,8 @@
53 syslog('error', 'No such list "%s": %s', listname, e)53 syslog('error', 'No such list "%s": %s', listname, e)
54 return54 return
5555
56 ##acoconut formquestion = DlistUtils.FormQuestion(mlist)
57 ##acoconut formquestion.recordAnswer("coconut@coconut.net", 1, "yes, i am", 1)
56 # See if the user want to see this page in other language58 # See if the user want to see this page in other language
57 cgidata = cgi.FieldStorage()59 cgidata = cgi.FieldStorage()
58 language = cgidata.getvalue('language')60 language = cgidata.getvalue('language')
@@ -62,7 +64,6 @@
62 list_listinfo(mlist, language)64 list_listinfo(mlist, language)
6365
6466
65
6667
67def listinfo_overview(msg=''):68def listinfo_overview(msg=''):
68 # Present the general listinfo overview69 # Present the general listinfo overview
69 hostname = Utils.get_domain()70 hostname = Utils.get_domain()
@@ -152,7 +153,6 @@
152 print doc.Format()153 print doc.Format()
153154
154155
155
156156
157def list_listinfo(mlist, lang):157def list_listinfo(mlist, lang):
158 # Generate list specific listinfo158 # Generate list specific listinfo
159 doc = HeadlessDocument()159 doc = HeadlessDocument()
@@ -190,9 +190,22 @@
190 # If activated, the content and presence of questions and a field to answer them. Added by Systers.190 # If activated, the content and presence of questions and a field to answer them. Added by Systers.
191 try:191 try:
192 if mlist.essay_enabled:192 if mlist.essay_enabled:
193 replacements['<mm-questions>'] = """<TR>193 html_questions = [] # Added to support subscribe essays
194 <TD COLSPAN="3">""" + mlist.questions194 for question in mlist.questions:
195 replacements['<mm-essay-box>'] = TextArea('essay', '', rows=8, cols=60).Format() + """ </TD></TR>"""# Added to support subscribe essays195 html_questions.append("""<TR>
196 <TD COLSPAN="3">""" + question[0] + """</TD>""")
197 if (question[2] == 'smallbox'):
198 html_questions.append("""<TD>""" + TextArea(question[1],\
199 '', rows=1, cols=60).Format() + """</TD></TR>""")
200 elif (question[2] == 'largebox'):
201 html_questions.append("""<TD>""" + TextArea(question[1], \
202 '', rows=8, cols=60).Format() + """</TD></TR>""")
203 elif (question[2] == 'checkbox'):
204 #TODO (Ana Cutillas): consider creating a class like TextArea for checkboxes?
205 html_questions.append("""<TD><input type='checkbox' name='""" + \
206 question[1] + """' value ='""" + \
207 question[1] + """'></TD></TR>""")
208 replacements['<mm-questions>'] = ''.join(html_questions)
196 else:209 else:
197 replacements['<mm-questions>'] = ''210 replacements['<mm-questions>'] = ''
198 replacements['<mm-essay-box>'] = ''211 replacements['<mm-essay-box>'] = ''
199212
=== modified file 'Mailman/DlistUtils.py'
--- Mailman/DlistUtils.py 2010-01-31 20:29:48 +0000
+++ Mailman/DlistUtils.py 2012-06-28 22:40:23 +0000
@@ -40,17 +40,17 @@
40 if self == classObject:40 if self == classObject:
41 if self.store == None:#for the case when same object is used for calling more than one functions41 if self.store == None:#for the case when same object is used for calling more than one functions
42 self.store = Store(self.database)42 self.store = Store(self.database)
43 result = f(self,*args)43 result = f(self, *args)
44 self.store.commit()44 self.store.commit()
45 self.store.close()45 self.store.close()
46 self.store = None46 self.store = None
47 else:#To handle the nesting issue47 else:#To handle the nesting issue
48 result = f(self,*args)48 result = f(self, *args)
49 self.store.commit()49 self.store.commit()
50 else:#for the case whenever a new object calls its decorated member function50 else:#for the case whenever a new object calls its decorated member function
51 classObject = self51 classObject = self
52 self.store = Store(self.database)52 self.store = Store(self.database)
53 result = f(self,*args)53 result = f(self, *args)
54 self.store.commit()54 self.store.commit()
55 self.store.close()55 self.store.close()
56 self.store = None56 self.store = None
@@ -60,14 +60,14 @@
60#Define all the classes corresponding to the tables in the database60#Define all the classes corresponding to the tables in the database
61class Subscriber(object):61class Subscriber(object):
62 __storm_table__ = "subscriber"62 __storm_table__ = "subscriber"
63 subscriber_id = Int(primary = True,default = AutoReload)63 subscriber_id = Int(primary = True, default = AutoReload)
64 mailman_key = Unicode()64 mailman_key = Unicode()
65 preference = Int(default = 1)65 preference = Int(default = 1)
66 format = Int(default = 3)66 format = Int(default = 3)
67 deleted = Bool(default = False)67 deleted = Bool(default = False)
68 suppress = Int(default = 0)68 suppress = Int(default = 0)
6969
70 def __init__(self,mlist):70 def __init__(self, mlist):
71 self.mlist = mlist71 self.mlist = mlist
72 self.database = getConn(mlist)72 self.database = getConn(mlist)
7373
@@ -76,11 +76,11 @@
76 if addr == None:76 if addr == None:
77 return None77 return None
7878
79 command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(addr.lower(),'utf-8'))\na = [(subscriber.subscriber_id) for subscriber in result]\n"79 command = "result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(addr.lower(),'utf-8'))\na = [(subscriber.subscriber_id) for subscriber in result]\n"
80 if DEBUG_MODE:80 if DEBUG_MODE:
81 syslog('info', "DlistUtils:(getSubscriber_id_raw)executing query:\n%s", command)81 syslog('info', "DlistUtils:(getSubscriber_id_raw)executing query:\n%s", command)
82 #storm recognizes unicode only82 #storm recognizes unicode only
83 result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(addr.lower(),"utf-8")) 83 result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(addr,"utf-8"))
84 a = [(subscriber.subscriber_id) for subscriber in result]84 a = [(subscriber.subscriber_id) for subscriber in result]
85 if DEBUG_MODE:85 if DEBUG_MODE:
86 syslog('info', 'value of a is: %s\n', a)86 syslog('info', 'value of a is: %s\n', a)
@@ -136,70 +136,70 @@
136 @decfunc136 @decfunc
137 def setDisable(self, member, flag):137 def setDisable(self, member, flag):
138 """Disable/enable delivery based on mm_cfg.DisableDelivery"""138 """Disable/enable delivery based on mm_cfg.DisableDelivery"""
139 command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"139 command = "result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"
140 if DEBUG_MODE:140 if DEBUG_MODE:
141 syslog('info', 'DlistUtils(setDisable):Executing query:\n%s\n Member whoes suppress value is to be found \n %s', command,member)141 syslog('info', 'DlistUtils(setDisable):Executing query:\n%s\n Member whose suppress value is to be found \n %s', command, member)
142 result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8"))142 result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,"utf-8"))
143 oldval = [(subscriber.suppress) for subscriber in result]143 oldval = [(subscriber.suppress) for subscriber in result]
144 if oldval == []:144 if oldval == []:
145 if DEBUG_MODE:145 if DEBUG_MODE:
146 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)')146 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)')
147 oldval = oldval[0]147 oldval = oldval[0]
148 if DEBUG_MODE:148 if DEBUG_MODE:
149 syslog('info','the value of oldval is %s:',oldval)149 syslog('info','the value of oldval is %s:', oldval)
150150
151 if flag:151 if flag:
152 newval = oldval | 1 # Disable delivery152 newval = oldval | 1 # Disable delivery
153 else:153 else:
154 newval = oldval & ~1 # Enable delivery154 newval = oldval & ~1 # Enable delivery
155 if DEBUG_MODE:155 if DEBUG_MODE:
156 syslog('info','the value of newval is %s:',newval)156 syslog('info','the value of newval is %s:', newval)
157 command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(suppress = newval)\n"157 command = "self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8')).set(suppress = newval)\n"
158 if DEBUG_MODE:158 if DEBUG_MODE:
159 syslog('info', 'DlistUtils(setDisable):Executing query:\n%s', command)159 syslog('info', 'DlistUtils(setDisable):Executing query:\n%s', command)
160 self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(suppress = newval) 160 self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,"utf-8")).set(suppress = newval)
161161
162 @decfunc162 @decfunc
163 def setDigest(self, member, flag):163 def setDigest(self, member, flag):
164 """Disable/enable delivery based on user digest status"""164 """Disable/enable delivery based on user digest status"""
165165
166 command = "result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"166 command = "result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8'))\noldval = [(subscriber.suppress) for subscriber in result]\n"
167 if DEBUG_MODE:167 if DEBUG_MODE:
168 syslog('info', 'DlistUtils(setDigest):Executing query:\n%s', command)168 syslog('info', 'DlistUtils(setDigest):Executing query:\n%s', command)
169 result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8"))169 result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,"utf-8"))
170 oldval = [(subscriber.suppress) for subscriber in result]170 oldval = [(subscriber.suppress) for subscriber in result]
171 if oldval == []:171 if oldval == []:
172 if DEBUG_MODE:172 if DEBUG_MODE:
173 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)')173 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)')
174 oldval = oldval[0]174 oldval = oldval[0]
175 if DEBUG_MODE:175 if DEBUG_MODE:
176 syslog('info','value of oldval %s:',oldval)176 syslog('info','value of oldval %s:', oldval)
177177
178 if flag:178 if flag:
179 newval = oldval | 2 # Suppress delivery (in favor of digests)179 newval = oldval | 2 # Suppress delivery (in favor of digests)
180 else:180 else:
181 newval = oldval & ~2 # Enable delivery (instead of digests)181 newval = oldval & ~2 # Enable delivery (instead of digests)
182182
183 command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(suppress = newval)\n"183 command = "self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8')).set(suppress = newval)\n"
184 if DEBUG_MODE:184 if DEBUG_MODE:
185 syslog('info', 'DlistUtils(setDigest):Executing query:\n%s', command)185 syslog('info', 'DlistUtils(setDigest):Executing query:\n%s', command)
186 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)
187187
188 @decfunc188 @decfunc
189 def changePreference(self, member, preference):189 def changePreference(self, member, preference):
190 """Change a user's default preference for new threads."""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"191 command = "self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8')).set(preference = preference)\n"
192 if DEBUG_MODE:192 if DEBUG_MODE:
193 syslog('info', 'DlistUtils(changePreference):Executing query:\n%s', command) 193 syslog('info', 'DlistUtils(changePreference):Executing query:\n%s', command)
194 self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(preference = preference)194 self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,"utf-8")).set(preference = preference)
195195
196 @decfunc196 @decfunc
197 def changeFormat(self, member, format):197 def changeFormat(self, member, format):
198 """Change a user's preferred delivery format (plain text and/or html)"""198 """Change a user's preferred delivery format (plain text and/or html)"""
199 command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,'utf-8')).set(format = format)\n"199 command = "self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,'utf-8')).set(format = format)\n"
200 if DEBUG_MODE:200 if DEBUG_MODE:
201 syslog('info', 'DlistUtils(changeFormat):Executing query:\n%s', command)201 syslog('info', 'DlistUtils(changeFormat):Executing query:\n%s', command)
202 self.store.find(Subscriber,Subscriber.mailman_key == unicode(member,"utf-8")).set(format = format) 202 self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(member,"utf-8")).set(format = format)
203203
204 ### We have to watch out for a tricky case (that actually happened):204 ### We have to watch out for a tricky case (that actually happened):
205 ### User foo@bar.com changes her address to something already in the205 ### User foo@bar.com changes her address to something already in the
@@ -207,32 +207,32 @@
207 @decfunc207 @decfunc
208 def changeAddress(self, oldaddr, newaddr):208 def changeAddress(self, oldaddr, newaddr):
209 """Change email address in SQL database"""209 """Change email address in SQL database"""
210 #if DEBUG_MODE:210 if DEBUG_MODE:
211 #syslog('info', "Changing email address on %s from '%s' to '%s'", self.mlist.internal_name(), oldaddr, newaddr)211 syslog('info', "Changing email address on %s from '%s' to '%s'", self.mlist.internal_name(), oldaddr, newaddr)
212 ## Check if newaddr is in sql database 212 ## Check if newaddr is in sql database
213 num_matches = self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8")).count()213 num_matches = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(newaddr.lower(),"utf-8")).count()
214214
215 if num_matches > 1:215 if num_matches > 1:
216 #syslog('error', 'Multiple users with same key (%s): %s', self.mlist.internal_name(), newaddr)216 syslog('error', 'Multiple users with same key (%s): %s', self.mlist.internal_name(), newaddr)
217 return217 return
218 if num_matches == 1:218 if num_matches == 1:
219 self.mergeSubscribers(oldaddr, newaddr)219 self.mergeSubscribers(oldaddr, newaddr)
220220
221 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"221 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"
222 if DEBUG_MODE:222 if DEBUG_MODE:
223 syslog('info', 'DlistUtils(changeAddress):Executing query:\n%s', command)223 syslog('info', 'DlistUtils(changeAddress):Executing query:\n%s', command)
224 self.store.find(Subscriber,Subscriber.mailman_key == unicode(oldaddr,"utf-8")).set(mailman_key = unicode(newaddr,"utf-8")) 224 self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(oldaddr,"utf-8")).set(mailman_key = unicode(newaddr,"utf-8"))
225 self.store.commit() 225 self.store.commit()
226 self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8")).set(deleted = False)226 self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(newaddr.lower(),"utf-8")).set(deleted = False)
227227
228 @decfunc228 @decfunc
229 def unsubscribeFromList(self, key):229 def unsubscribeFromList(self, key):
230 """Indicate that a user has unsubscribed by setting the deleted flag in the subscriber table."""230 """Indicate that a user has unsubscribed by setting the deleted flag in the subscriber table."""
231231
232 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"232 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"
233 if DEBUG_MODE:233 if DEBUG_MODE:
234 syslog('info', 'DlisUtils(unsubscribeFromList):Executing query:\n%s', command) 234 syslog('info', 'DlisUtils(unsubscribeFromList):Executing query:\n%s', command)
235 self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8")).set(deleted = True)235 self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key,"utf-8")).set(deleted = True)
236 # get the subscriber id from the mailman_key & delete all aliases 236 # get the subscriber id from the mailman_key & delete all aliases
237 subscriber_id = self.getSubscriber_id_raw(key)237 subscriber_id = self.getSubscriber_id_raw(key)
238238
@@ -245,20 +245,27 @@
245 def subscribeToList(self, key):245 def subscribeToList(self, key):
246 """Add a member to the subscriber database, or change the record from deleted if it was already present."""246 """Add a member to the subscriber database, or change the record from deleted if it was already present."""
247247
248 command = "count = (self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,'utf-8'))).count()\n"248 command = "count = (self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),'utf-8'))).count()\n"
249 if DEBUG_MODE:249 if DEBUG_MODE:
250 syslog('info', 'DlistUtils(subscribeToList):Executing query:\n%s', command)250 syslog('info', 'DlistUtils(subscribeToList):Executing query:\n%s', command)
251 # First see if member is subscribed with deleted field = false251 # First see if member is subscribed with deleted field = false
252 count = (self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8"))).count()252 count = (self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),"utf-8"))).count()
253253
254 if count:254 if count:
255 command = "self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,'utf-8')).set(deleted = False)"255 command = "self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),'utf-8')).set(deleted = False)"
256 if DEBUG_MODE:256 if DEBUG_MODE:
257 syslog('info', 'DlistUtils(subscribeToList):Executing query:\n%s', command)257 syslog('info', 'DlistUtils(subscribeToList):Executing query:\n%s', command)
258 self.store.find(Subscriber,Subscriber.mailman_key == unicode(key,"utf-8")).set(deleted = False)258 self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),"utf-8")).set(deleted = False)
259
260 result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),"utf-8"))
261 Email = [(subscriber.mailman_key) for subscriber in result]
262 Email = Email[0]
263 if Email != key: #That is if one was in lowercase and other in uppercase then update mailman_key with case sensitivity same as key
264 self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(key.lower(),"utf-8")).set(mailman_key = unicode(key,"utf-8"))
265
259 else:266 else:
260 # format is text-only (1) by default267 # format is text-only (1) by default
261 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"268 command = "subscriber = self.store.add(Subscriber())\nmailman_key = unicode(key,'utf-8')\npreference = 1\ndeleted = False\nformat = 1\nsuppress = 0"
262 if DEBUG_MODE:269 if DEBUG_MODE:
263 syslog('info', 'DlistUtils(subscribeToList)Executing query:\n%s', command)270 syslog('info', 'DlistUtils(subscribeToList)Executing query:\n%s', command)
264 self.mailman_key = unicode(key,"utf-8")271 self.mailman_key = unicode(key,"utf-8")
@@ -273,12 +280,12 @@
273 # use the original subscriber id as the ID going forward280 # use the original subscriber id as the ID going forward
274 if DEBUG_MODE:281 if DEBUG_MODE:
275 syslog('info', 'Executing commands of mergeSubscribers:')282 syslog('info', 'Executing commands of mergeSubscribers:')
276 result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(oldaddr,"utf-8"))283 result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(oldaddr,"utf-8"))
277 new_id = [(subscriber.subscriber_id) for subscriber in result]284 new_id = [(subscriber.subscriber_id) for subscriber in result]
278 new_id = new_id[0]285 new_id = new_id[0]
279 if DEBUG_MODE:286 if DEBUG_MODE:
280 syslog('info', 'the value of new_id: %s', new_id)287 syslog('info', 'the value of new_id: %s', new_id)
281 result = self.store.find(Subscriber,Subscriber.mailman_key == unicode(newaddr,"utf-8"))288 result = self.store.find(Subscriber,Subscriber.mailman_key.lower() == unicode(newaddr.lower(),"utf-8"))
282 obsolete_id = [(subscriber.subscriber_id) for subscriber in result]289 obsolete_id = [(subscriber.subscriber_id) for subscriber in result]
283 obsolete_id = obsolete_id[0] 290 obsolete_id = obsolete_id[0]
284 if DEBUG_MODE:291 if DEBUG_MODE:
@@ -291,13 +298,13 @@
291298
292class Message(object):299class Message(object):
293 __storm_table__ = "message"300 __storm_table__ = "message"
294 message_id = Int(primary = True,default = AutoReload)301 message_id = Int(primary = True, default = AutoReload)
295 sender_id = Int()302 sender_id = Int()
296 subscriber = Reference(sender_id,Subscriber.subscriber_id)303 subscriber = Reference(sender_id, Subscriber.subscriber_id)
297 subject = Unicode()304 subject = Unicode()
298 thread_id = Int()305 thread_id = Int()
299306
300 def __init__(self,mlist):307 def __init__(self, mlist):
301 self.mlist = mlist308 self.mlist = mlist
302 self.database = getConn(mlist)309 self.database = getConn(mlist)
303310
@@ -314,7 +321,7 @@
314 threadID = -1321 threadID = -1
315 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"322 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"
316 if DEBUG_MODE:323 if DEBUG_MODE:
317 syslog('info','DlistUtils:(createMessage)executing query:\n%s',command)324 syslog('info','DlistUtils:(createMessage)executing query:\n%s', command)
318 #message_id has autoreload set,its value will be serially updated in database 325 #message_id has autoreload set,its value will be serially updated in database
319 self.sender_id = senderID326 self.sender_id = senderID
320 self.thread_id = threadID327 self.thread_id = threadID
@@ -322,19 +329,19 @@
322 self.store.add(self) 329 self.store.add(self)
323 messageID = self.store.find(Message).max(Message.message_id) 330 messageID = self.store.find(Message).max(Message.message_id)
324 if DEBUG_MODE:331 if DEBUG_MODE:
325 syslog('info','Result of query(messageID) is: %s\n',messageID)332 syslog('info','Result of query(messageID) is: %s\n', messageID)
326 return messageID333 return messageID
327334
328class Thread(object):335class Thread(object):
329 __storm_table__ = "thread"336 __storm_table__ = "thread"
330 thread_id = Int(primary = True,default = AutoReload)337 thread_id = Int(primary = True, default = AutoReload)
331 thread_name = Unicode()338 thread_name = Unicode()
332 base_message_id = Int()339 base_message_id = Int()
333 message = Reference(base_message_id,Message.message_id)340 message = Reference(base_message_id, Message.message_id)
334 status = Int(default = 0)341 status = Int(default = 0)
335 parent = Int()342 parent = Int()
336343
337 def __init__(self,mlist):344 def __init__(self, mlist):
338 self.mlist = mlist345 self.mlist = mlist
339 self.database = getConn(mlist)346 self.database = getConn(mlist)
340347
@@ -347,7 +354,7 @@
347 senderID = subscriber.getSubscriber_id(msg, msgdata, safe=1, loose=1)354 senderID = subscriber.getSubscriber_id(msg, msgdata, safe=1, loose=1)
348 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"355 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"
349 if DEBUG_MODE:356 if DEBUG_MODE:
350 syslog('info','DlistUtils:(createThread)executing query:\n%s',command)357 syslog('info','DlistUtils:(createThread)executing query:\n%s', command)
351 self.base_message_id = msgdata['message_id']358 self.base_message_id = msgdata['message_id']
352 self.store.add(self)359 self.store.add(self)
353 threadID = self.store.find(Thread).max(Thread.thread_id) 360 threadID = self.store.find(Thread).max(Thread.thread_id)
@@ -370,7 +377,7 @@
370 threadBase = threadBase[:13]377 threadBase = threadBase[:13]
371 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"378 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"
372 if DEBUG_MODE:379 if DEBUG_MODE:
373 syslog('info','DlistUtils:(createThread)executing query:\n%s',command)380 syslog('info','DlistUtils:(createThread)executing query:\n%s', command)
374 # If the threadBase is not unique, make threadBase unique by appending a number381 # If the threadBase is not unique, make threadBase unique by appending a number
375 num = self.store.find(Thread,Thread.thread_name.like(unicode(threadBase + "%","utf-8"))).count()382 num = self.store.find(Thread,Thread.thread_name.like(unicode(threadBase + "%","utf-8"))).count()
376 if not num:383 if not num:
@@ -405,26 +412,24 @@
405 msg['To'] = '%s+%s@%s' % (self.mlist.internal_name(),412 msg['To'] = '%s+%s@%s' % (self.mlist.internal_name(),
406 name,413 name,
407 self.mlist.host_name) 414 self.mlist.host_name)
408 for i in (1,2):415 for i in (1, 2):
409 # different footers for different prefs, so we need to queue separately416 # different footers for different prefs, so we need to queue separately
410 if(i==1):417 if(i==1):
411 #For condition where preference = True418 #For condition where preference = True
412 pref=True419 pref=True
413 # command = "lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]\n"420 if DEBUG_MODE:
414 # if DEBUG_MODE:421 syslog('info', 'DlistUtils:(newThread)executing query:\nfor pref = true\n\n')
415 # syslog('info', 'DlistUtils:(newThread)executing query:\n%sfor pref = true\n', command)
416 if(i==2):422 if(i==2):
417 #For condition where preference = False423 #For condition where preference = False
418 pref=False424 pref=False
419 # command = "lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]\n"425 if DEBUG_MODE:
420 # if DEBUG_MODE:426 syslog('info', 'DlistUtils:(newThread)executing query:\nfor pref = false\n\n')
421 # syslog('info', 'DlistUtils:(newThread)executing query:\n%sfor pref = false\n', command)
422427
423 #Execute a SELECT statement, to find the list of matching subscribers.428 #Execute a SELECT statement, to find the list of matching subscribers.
424 result_new_sql = self.store.find(Subscriber,And(Subscriber.preference == pref,Subscriber.deleted == False,Subscriber.suppress == 0))429 result_new_sql = self.store.find(Subscriber,And(Subscriber.preference == pref,Subscriber.deleted == False,Subscriber.suppress == 0))
425 lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]430 lists = [(subscriber.mailman_key.encode('utf-8')) for subscriber in result_new_sql]
426 if DEBUG_MODE:431 if DEBUG_MODE:
427 syslog('info', 'value of lists: %s\n', lists)432 syslog('info','value of lists: %s\n', lists)
428 self.email_recipients(msg, msgdata, lists, pref)433 self.email_recipients(msg, msgdata, lists, pref)
429434
430 # Make original message go to nobody (but be archived)435 # Make original message go to nobody (but be archived)
@@ -514,12 +519,12 @@
514 override = Override(self.mlist)519 override = Override(self.mlist)
515 override.override(msg, msgdata, 0)520 override.override(msg, msgdata, 0)
516521
517 def alphanumericOnly(self,s):522 def alphanumericOnly(self, s):
518 """Filter any non-letter characters from a string"""523 """Filter any non-letter characters from a string"""
519 result = [letter for letter in s if letter in string.ascii_letters or letter in string.digits]524 result = [letter for letter in s if letter in string.ascii_letters or letter in string.digits]
520 return string.join(result, '')525 return string.join(result, '')
521526
522 def subjectToName(self,subject, threadID):527 def subjectToName(self, subject, threadID):
523 """Return a lower-case name for a new thread based on the subject, if present, or on the threadID"""528 """Return a lower-case name for a new thread based on the subject, if present, or on the threadID"""
524 result = None529 result = None
525530
@@ -589,9 +594,9 @@
589 __storm_table__ = "alias" 594 __storm_table__ = "alias"
590 pseudonym = Unicode(primary = True)595 pseudonym = Unicode(primary = True)
591 subscriber_id = Int()596 subscriber_id = Int()
592 subscriber = Reference(subscriber_id,Subscriber.subscriber_id)597 subscriber = Reference(subscriber_id, Subscriber.subscriber_id)
593598
594 def __init__(self,mlist):599 def __init__(self, mlist):
595 self.mlist = mlist600 self.mlist = mlist
596 self.database = getConn(mlist)601 self.database = getConn(mlist)
597602
@@ -604,8 +609,8 @@
604 syslog('info', 'DlistUtils(canonicalize_sender)Executing query:\n%s', command)609 syslog('info', 'DlistUtils(canonicalize_sender)Executing query:\n%s', command)
605610
606 for alias in aliases:611 for alias in aliases:
607 #if DEBUG_MODE:612 if DEBUG_MODE:
608 #syslog('info', 'Checking if <%s> is an alias', alias)613 syslog('info', 'Checking if <%s> is an alias', alias)
609 result = self.store.find((Subscriber,Alias),And(Alias.pseudonym == unicode(alias,'utf-8'),Subscriber.subscriber_id == Alias.subscriber_id))614 result = self.store.find((Subscriber,Alias),And(Alias.pseudonym == unicode(alias,'utf-8'),Subscriber.subscriber_id == Alias.subscriber_id))
610 lists = [(subscriber.mailman_key.encode('utf-8')) for (subscriber,alias) in result]615 lists = [(subscriber.mailman_key.encode('utf-8')) for (subscriber,alias) in result]
611616
@@ -654,12 +659,12 @@
654 __storm_table__ = "override"659 __storm_table__ = "override"
655 __storm_primary__ = "subscriber_id", "thread_id"660 __storm_primary__ = "subscriber_id", "thread_id"
656 subscriber_id = Int()661 subscriber_id = Int()
657 subscriber = Reference(subscriber_id,Subscriber.subscriber_id)662 subscriber = Reference(subscriber_id, Subscriber.subscriber_id)
658 thread_id = Int()663 thread_id = Int()
659 thread = Reference(thread_id,Thread.thread_id)664 thread = Reference(thread_id, Thread.thread_id)
660 preference = Int()665 preference = Int()
661666
662 def __init__(self,mlist):667 def __init__(self, mlist):
663 self.mlist = mlist668 self.mlist = mlist
664 self.database = getConn(mlist)669 self.database = getConn(mlist)
665670
@@ -709,10 +714,9 @@
709714
710 return 1715 return 1
711716
712 def overrideURL(self,list_name, host_name, thread_reference, preference):717 def overrideURL(self, list_name, host_name, thread_reference, preference):
713 return 'http://%s/mailman/options/%s?override=%s&preference=%d' % (host_name, list_name, thread_reference, preference)718 return 'http://%s/mailman/options/%s?override=%s&preference=%d' % (host_name, list_name, thread_reference, preference)
714719
715
716#Functions Independent of the database tables in DlistUtils720#Functions Independent of the database tables in DlistUtils
717721
718def GetEmailAddresses(mlist, subscribers):722def GetEmailAddresses(mlist, subscribers):
@@ -752,13 +756,22 @@
752756
753def _create_database(name):757def _create_database(name):
754 """Create a database. This requires us to not be in a transaction."""758 """Create a database. This requires us to not be in a transaction."""
755 conn = pgdb.connect(host='localhost', database='postgres',user='mailman', password='mailman')759 conn = pgdb.connect(host='localhost', database='postgres', user='mailman', password='mailman')
756 old_iso_lvl = conn.isolation_level760 old_iso_lvl = conn.isolation_level
757 conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)761 conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
758 cursor = conn.cursor()762 cursor = conn.cursor()
759 cursor.execute('create database "%s"' % name) 763 cursor.execute('create database "%s"' % name)
760 conn.set_isolation_level(old_iso_lvl)764 conn.set_isolation_level(old_iso_lvl)
761765
766def _remove_database(name):
767 """Remove a database. This requires us to not be in a transaction."""
768 conn = pgdb.connect(host='localhost', database='postgres', user='mailman', password='mailman')
769 old_iso_lvl = conn.isolation_level
770 conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
771 cursor = conn.cursor()
772 cursor.execute('DROP DATABASE "%s"' % name)
773 conn.set_isolation_level(old_iso_lvl)
774
762def executeSQL(mlist, commands):775def executeSQL(mlist, commands):
763 """Execute a sequence of SQL commands that create tables in the database."""776 """Execute a sequence of SQL commands that create tables in the database."""
764 database = getConn(mlist)777 database = getConn(mlist)
@@ -834,3 +847,9 @@
834 mlist.Save()847 mlist.Save()
835 if alreadyLocked == 0:848 if alreadyLocked == 0:
836 mlist.Unlock()849 mlist.Unlock()
850
851def remove_dlist(listname):
852 """ Deletes the corresponding postgres database and tables."""
853 _remove_database(listname)
854 if DEBUG_MODE:
855 syslog('info', "Database %s removed\n", listname)
837856
=== modified file 'bin/rmlist'
--- bin/rmlist 2009-07-31 10:49:01 +0000
+++ bin/rmlist 2012-06-28 22:40:23 +0000
@@ -152,12 +152,7 @@
152152
153 # Remove the database in PostgreSQL if it was a dynamic list. Added by Systers.153 # Remove the database in PostgreSQL if it was a dynamic list. Added by Systers.
154 try:154 try:
155 conn = DlistUtils.getConn(None)155 DlistUtils.remove_dlist(listname)
156 old_iso_lvl = conn.isolation_level
157 conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
158 cursor = conn.cursor()
159 cursor.execute('DROP DATABASE "%s"' % listname)
160 conn.set_isolation_level(old_iso_lvl)
161 except:156 except:
162 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)157 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)
163158

Subscribers

People subscribed via source and target branches