Michael Hall has proposed merging lp:~mhall119/loco-directory/686268 into 
lp:loco-directory.

Requested reviews:
  loco-directory-dev (loco-directory-dev)
Related bugs:
  #686268 Add the ability to add agenda items
  https://bugs.launchpad.net/bugs/686268

For more details, see:
https://code.launchpad.net/~mhall119/loco-directory/686268/+merge/47162

Added forms for adding/updating agenda items, as well as a template tag for 
recursively displaying them, and css to make it all look nice.
-- 
https://code.launchpad.net/~mhall119/loco-directory/686268/+merge/47162
Your team loco-directory-dev is requested to review the proposed merge of 
lp:~mhall119/loco-directory/686268 into lp:loco-directory.
=== modified file 'loco_directory/media/css/newstyle.css'
--- loco_directory/media/css/newstyle.css	2011-01-02 18:45:45 +0000
+++ loco_directory/media/css/newstyle.css	2011-01-22 16:08:54 +0000
@@ -434,3 +434,28 @@
 .attendee-mugshot {
 	vertical-align: middle;
 }
+
+.agenda-list {
+    list-style-type: decimal;
+}
+
+.agenda-list .agenda-list {
+    list-style-type: lower-alpha;
+}
+
+.agenda-list .agenda-list .agenda-list {
+    list-style-type: lower-roman;
+}
+
+.agenda-list .agenda-list .agenda-list .agenda-list {
+    list-style-type: circle;
+}
+
+.agenda-title {
+    font-weight: bold;
+}
+
+.agenda-description {
+    margin-left: 50px;
+    font-size: 0.9em;
+}

=== modified file 'loco_directory/meetings/forms.py'
--- loco_directory/meetings/forms.py	2011-01-18 21:13:08 +0000
+++ loco_directory/meetings/forms.py	2011-01-22 16:08:54 +0000
@@ -4,13 +4,23 @@
 from django.utils.translation import ugettext as _
 from django.core.urlresolvers import reverse
 
-from models import BaseMeeting, TeamMeeting
+from models import BaseMeeting, TeamMeeting, AgendaItem
 from common.forms import RenderableMixin
 from userprofiles.models import UserProfile
 
 import pytz
 import urllib
 
+def grouped_user_list(teams):
+    other_members, team_members = [], []
+    for profile in UserProfile.objects.filter(user__groups__name__in=teams):
+        team_members.append((profile.id, str(profile)))
+    for profile in UserProfile.objects.all().exclude(pk__in=[m[0] for m in team_members]):
+        other_members.append((profile.id, str(profile)))
+        
+    return [('', '---------'),
+            (_('Team members'), team_members),
+            (_('Other users'), other_members)]
 
 class BaseMeetingForm(forms.ModelForm, RenderableMixin):
     """ 
@@ -60,22 +70,11 @@
     def __init__(self, teams=None, *args, **kargs):
         super(TeamMeetingForm, self).__init__(*args, **kargs)
         if teams:
-            self.fields['chair'].choices = self.grouped_user_list(teams)
+            self.fields['chair'].choices = grouped_user_list(teams)
             self.fields['channel'].initial = teams[0].irc_chan
         elif self.instance:
-            self.fields['chair'].choices = self.grouped_user_list(teams.iterator())
+            self.fields['chair'].choices = grouped_user_list([team.lp_name for team in self.instance.teams.iterator()])
         
-    def grouped_user_list(self, teams):
-        print dir(teams)
-        other_members, team_members = [], []
-        for profile in UserProfile.objects.filter(user__groups__name__in=teams):
-            team_members.append((profile.id, str(profile)))
-        for profile in UserProfile.objects.all().exclude(user__groups__name__in=teams):
-            other_members.append((profile.id, str(profile)))
-        return [('', '---------'),
-                (_('Team members'), team_members),
-                (_('Other users'), other_members)]
-                
     def save(self):
         start_date = self.cleaned_data['date_begin']
         self.instance.logs = 'http://irclogs.ubuntu.com/%(date)s/%(channel)s.html#t%(time)s' % {'date': start_date.strftime('%Y/%m/%d'),
@@ -83,4 +82,38 @@
                                                                                                 'time': start_date.strftime('%H:%M')}
         return super(TeamMeetingForm, self).save()
         
-        
+class AgendaItemForm(forms.ModelForm, RenderableMixin):
+
+    class Meta:
+        model = AgendaItem
+        exclude = ('meeting','created_date')
+        
+    def __init__(self, *args, **kargs):
+        super(AgendaItemForm, self).__init__(*args, **kargs)
+        parent_choices = AgendaItem.objects.filter(meeting=self.instance.meeting).exclude(pk=self.instance.id)
+        self.fields['parent'].choices = [('', '')]+[(item.id, str(item)) for item in parent_choices]
+        if self.instance:
+            meeting = self.instance.meeting.teammeeting
+            print dir(meeting)
+            self.fields['owner'].choices = grouped_user_list(meeting.teams.iterator())
+        
+class AgendaItemFormSet(forms.models.BaseInlineFormSet):
+
+    def __init__(self, *args, **kargs):
+        super(AgendaItemFormSet, self).__init__(*args, **kargs)
+        self.node_tree = {}
+        
+    def add_fields(self, form, index):
+        super(AgendaItemFormSet, self).add_fields(form, index)
+        self.fields['node_id'] = IntegerField(label=_(u'Node'), 
+                        initial=index, 
+                        required=False)
+        self.node_tree[form.instance.id] = index
+        self.fields['parent_node'] = IntegerField(label=_(u'Parent Node'), 
+                        initial=self.node_tree.get(form.instance.parent, None), 
+                        required=False)
+        
+    def save(self):
+        pass
+    def as_tree(self):
+        pass

=== modified file 'loco_directory/meetings/models.py'
--- loco_directory/meetings/models.py	2011-01-10 16:08:59 +0000
+++ loco_directory/meetings/models.py	2011-01-22 16:08:54 +0000
@@ -92,6 +92,9 @@
         
 class AgendaItemManager(models.Manager):
 
+    def top(self):
+        return self.filter(parent__isnull=True).order_by('order')
+            
     def as_tree(self):
         cache = {}
         tree = []
@@ -109,27 +112,32 @@
 class AgendaItem(models.Model):
 
     class Meta:
-        ordering = ('parent__id', 'order')
+        ordering = ('parent__id', 'order', 'created_date')
         
     meeting = models.ForeignKey(BaseMeeting, verbose_name=_('Meeting'), related_name='agenda', help_text=_('meeting during which this agenda item is to be discussed'))
-    parent = models.ForeignKey('self', verbose_name=_('Parent Agenda Item'), related_name='children', help_text=_('agenda item that contains this item'), blank=True, null=True)
-    order = models.PositiveIntegerField(verbose_name=_('Order'), help_text=_('index number of where this item falls in the agenda'))
+    title = models.CharField(verbose_name=_('Title'), max_length = 150, help_text=_('descriptive name for this item'))
     owner = models.ForeignKey(UserProfile, verbose_name=_('Owner'), help_text=_('person proposing or responsible for this item'))
     created_date = models.DateTimeField(verbose_name=_('Created Date'), auto_now_add=True, help_text=_('timestamp of when this item was created'))
-    title = models.CharField(verbose_name=_('Title'), max_length = 150, help_text=_('descriptive name for this item'))
     description = models.TextField(verbose_name=_('Description'), help_text=_('detailed description of this item'), blank=True, null=True)
+    parent = models.ForeignKey('self', verbose_name=_('Parent Agenda Item'), related_name='children', help_text=_('agenda item that contains this item'), blank=True, null=True)
+    order = models.PositiveIntegerField(verbose_name=_('Order'), help_text=_('index number of where this item falls in the agenda'), default=1)
     log = models.URLField(verbose_name=_('Log URL'), max_length=200, verify_exists=False, help_text=_('URL to this item\'s discussion'), blank=True, null=True)
     
     objects = AgendaItemManager()
     
     def get_sig(self):
-        return '<a href="http://launchpad.net/~%s";>%s</a> %s' % (self.owner.user.username, self.owner.realname, self.created_date)
+        return '<a href="http://launchpad.net/~%s"; target="launchpaduser">%s</a> %s' % (self.owner.user.username, self.owner.realname, self.created_date)
         
     sig = property(get_sig)
     
+    def save(self, *args, **kargs):
+        if not self.created_date:
+            self.created_date = datetime.datetime.now()
+        return super(AgendaItem, self).save(*args, **kargs)    
+    
     def __unicode__(self):
         if self.parent is None:
-            return '%s: %s' % (self.meeting, self.title)
+            return '%s' % self.title
         else:
             return '%s->%s' % (self.parent, self.title)
     

=== added directory 'loco_directory/meetings/templatetags'
=== added file 'loco_directory/meetings/templatetags/__init__.py'
=== added file 'loco_directory/meetings/templatetags/recurse.py'
--- loco_directory/meetings/templatetags/recurse.py	1970-01-01 00:00:00 +0000
+++ loco_directory/meetings/templatetags/recurse.py	2011-01-22 16:08:54 +0000
@@ -0,0 +1,82 @@
+###############################################################################
+# Recurse template tag for Django v1.1
+# Copyright (C) 2008 Lucas Murray
+# http://www.undefinedfire.com
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+###############################################################################
+
+from django import template
+
+register = template.Library()
+
+class RecurseNode(template.Node):
+    def __init__(self, var, name, child, nodeList):
+        self.var = var
+        self.name = name
+        self.child = child
+        self.nodeList = nodeList
+
+    def __repr__(self):
+        return '<RecurseNode>'
+
+    def renderCallback(self, context, vals, level):
+        output = []
+        try:
+            if len(vals):
+                pass
+        except:
+            vals = [vals]
+        if len(vals):
+            if 'loop' in self.nodeList:
+                output.append(self.nodeList['loop'].render(context))
+            for val in vals:
+                context.push()
+                context['level'] = level
+                context[self.name] = val
+                if 'child' in self.nodeList:
+                    output.append(self.nodeList['child'].render(context))
+                    child = self.child.resolve(context)
+                    if child:
+                        output.append(self.renderCallback(context, child, level + 1))
+                if 'endloop' in self.nodeList:
+                    output.append(self.nodeList['endloop'].render(context))
+                else:
+                    output.append(self.nodeList['endrecurse'].render(context))
+                context.pop()
+            if 'endloop' in self.nodeList:
+                output.append(self.nodeList['endrecurse'].render(context))
+        return ''.join(output)
+
+    def render(self, context):
+        vals = self.var.resolve(context)
+        output = self.renderCallback(context, vals, 1)
+        return output
+
+def do_recurse(parser, token):
+    bits = list(token.split_contents())
+    if len(bits) != 6 and bits[2] != 'with' and bits[4] != 'as':
+        raise template.TemplateSyntaxError, "Invalid tag syxtax expected '{% recurse [childVar] with [parents] as [parent] %}'"
+    child = parser.compile_filter(bits[1])
+    var = parser.compile_filter(bits[3])
+    name = bits[5]
+
+    nodeList = {}
+    while len(nodeList) < 4:
+        temp = parser.parse(('child','loop','endloop','endrecurse'))
+        tag = parser.tokens[0].contents
+        nodeList[tag] = temp
+        parser.delete_first_token()
+        if tag == 'endrecurse':
+            break
+
+    return RecurseNode(var, name, child, nodeList)
+do_recurse = register.tag('recurse', do_recurse)
\ No newline at end of file

=== modified file 'loco_directory/meetings/urls.py'
--- loco_directory/meetings/urls.py	2010-12-02 09:12:53 +0000
+++ loco_directory/meetings/urls.py	2011-01-22 16:08:54 +0000
@@ -16,4 +16,6 @@
     url(r'^team/(?P<team_slug>[a-zA-Z0-9\-\.\+?]+)/ical/$', 'meetings.views.team_meeting_list_ical', name='team-meeting-list-ical'),
     url(r'^team/add/$', 'meetings.views.team_meeting_select', name='team-meeting-select'),
 
+    url(r'^team/(?P<team_meeting_id>\d+)/agenda/(?P<agenda_item_id>\d+)/$', 'meetings.views.agenda_item_update', name='agenda-item-update'),
+    url(r'^team/(?P<team_meeting_id>\d+)/agenda/add/$', 'meetings.views.agenda_item_new', name='agenda-item-new'),
 )

=== modified file 'loco_directory/meetings/views.py'
--- loco_directory/meetings/views.py	2011-01-18 20:44:47 +0000
+++ loco_directory/meetings/views.py	2011-01-22 16:08:54 +0000
@@ -6,15 +6,16 @@
 from django.utils.translation import ugettext as _
 from django.core.urlresolvers import reverse
 
-from meetings.models import TeamMeeting
+from meetings.models import TeamMeeting, AgendaItem
 from teams.models import Team
 
-from forms import TeamMeetingForm
+from forms import TeamMeetingForm, AgendaItemForm
 from django.db.models import Q
 
 from common.utils import redirect, simple_iterator
 from common import launchpad
 
+from userprofiles.models import UserProfile
 import datetime
 
 def meeting_list(request):
@@ -262,3 +263,81 @@
     else:
         request.user.message_set.create(message=_('You can not update this team meeting. You are not member of the team or on the LoCo Council.'))
         return redirect( team_meeting_object )
+
+@login_required
+def agenda_item_new(request, team_meeting_id):
+    """
+    new agenda item
+    """
+    team_meeting_object = get_object_or_404(TeamMeeting, pk=team_meeting_id)
+    try:
+        user = UserProfile.objects.get(user=request.user)
+        agenda_item_object = AgendaItem(meeting=team_meeting_object, 
+                                   owner=user,
+                                   created_date=datetime.datetime.now())
+    except UserProfile.DoesNotExist:
+        agenda_item_object = AgendaItem(meeting=team_meeting_object, 
+                                   created_date=datetime.datetime.now())
+    is_member = False
+    for team in team_meeting_object.teams.all():
+        if launchpad.is_team_member(request.user, team):
+            is_member = True
+            break
+    is_on_lc = launchpad.is_user_on_loco_council(request.user)
+        
+    if is_on_lc or is_member:
+        if request.method == 'POST':
+            form = AgendaItemForm(data=request.POST, instance=agenda_item_object)
+            if form.is_valid():
+                agenda_item_object = form.save()
+                request.user.message_set.create(message=_('Meeting agenda updated.'))
+                return redirect( team_meeting_object )
+        else:
+            form = AgendaItemForm(instance=agenda_item_object)
+                
+        context = {
+            'team_meeting_object': team_meeting_object,
+            'form': form,
+        }
+        return render_to_response('meetings/agenda_item_new.html', 
+                                         context, RequestContext(request))
+    else:
+        # XXX: Once we move to a new ACL system, this needs fixing.
+        request.user.message_set.create(message=_('You can not add a new agenda item for this team meeting. You are not member of the team or on the LoCo Council.'))
+        return redirect( team_meeting_object )
+
+@login_required
+def agenda_item_update(request, team_meeting_id, agenda_item_id):
+    """
+    update agenda item
+    """
+    team_meeting_object = get_object_or_404(TeamMeeting, pk=team_meeting_id)
+    agenda_item_object = get_object_or_404(AgendaItem, pk=agenda_item_id)
+    #check if user is admin or owner of a team
+    is_member = False
+    for team in team_meeting_object.teams.all():
+        if launchpad.is_team_member(request.user, team):
+            is_member = True
+            break
+    
+    is_on_lc = launchpad.is_user_on_loco_council(request.user)
+    
+    if is_on_lc or is_member:
+        if request.method == 'POST':		
+            form = AgendaItemForm(data=request.POST, instance=agenda_item_object)
+            if form.is_valid():
+                form.save()
+                request.user.message_set.create(message=_('Meeting agenda updated.'))
+                return redirect( team_meeting_object )
+        else:
+            form = AgendaItemForm(instance=agenda_item_object)
+            
+        context = {
+            'team_meeting_object': team_meeting_object,
+            'form': form,
+        }
+        return render_to_response('meetings/agenda_item_update.html', 
+                                         context, RequestContext(request))
+    else:
+        request.user.message_set.create(message=_('You can not update this team meeting agenda. You are not member of the team or on the LoCo Council.'))
+        return redirect( team_meeting_object )

=== modified file 'loco_directory/services/urls.py'
--- loco_directory/services/urls.py	2010-12-02 07:34:24 +0000
+++ loco_directory/services/urls.py	2011-01-22 16:08:54 +0000
@@ -12,6 +12,7 @@
     url(r'^continents/(.*)$', 'services.views.continent_service', name='continent_service'),
     url(r'^events/(.*)$', 'services.views.team_event_service', name='team_event_service'),
     url(r'^meeting/(.*)$', 'services.views.meeting_service', name='meeting_service'),
+    url(r'^agenda/(.*)$', 'services.views.meeting_agenda_service', name='meeting_agenda_service'),
     url(r'^global/(.*)$', 'services.views.global_event_service', name='global_event_service'),
     url(r'^comments/(.*)$', 'services.views.event_comment_service', name='event_comment_service'),
     url(r'^attendees/(.*)$', 'services.views.event_attendee_service', name='event_attendee_service'),

=== modified file 'loco_directory/services/views.py'
--- loco_directory/services/views.py	2010-12-19 19:20:44 +0000
+++ loco_directory/services/views.py	2011-01-22 16:08:54 +0000
@@ -1,6 +1,6 @@
 from teams.models import Team, Continent, Country, Language
 from events.models import GlobalEvent, TeamEvent, TeamEventComment, Attendee
-from meetings.models import TeamMeeting
+from meetings.models import TeamMeeting, AgendaItem
 from venues.models import Venue
 from userprofiles.models import UserProfile
 from django.contrib.auth.models import User, Group
@@ -22,6 +22,9 @@
 def meeting_service(request, url):
     return model_service(TeamMeeting, request, url)
     
+def meeting_agenda_service(request, url):
+    return model_service(AgendaItem, request, url)
+    
 def global_event_service(request, url):
     return model_service(GlobalEvent, request, url)
 

=== added file 'loco_directory/templates/meetings/agenda_item_new.html'
--- loco_directory/templates/meetings/agenda_item_new.html	1970-01-01 00:00:00 +0000
+++ loco_directory/templates/meetings/agenda_item_new.html	2011-01-22 16:08:54 +0000
@@ -0,0 +1,36 @@
+{% extends "base.html" %}
+{% load i18n %}
+
+{% block title %}{% trans "New Agenda Item" %} | {% trans "Ubuntu LoCo Team Directory" %} {% endblock %}
+
+{% block sub_nav_links %}
+<a class="sub-nav-item" href="{% url team-meeting-detail team_meeting_object.id %}">{% trans "Back to Meeting Details" %}</a>
+{% endblock %}
+
+{% block extrahead %}{{ block.super }}
+{{form.media}}
+{% endblock %}
+
+{% block extrafooter %}
+<script type="text/javascript"><!--
+$(document).ready(function(){
+    $('span[rel*=help]').colorTip({color:'orange'});
+});
+--></script>
+{% endblock %}
+
+{% block content %}
+<article class="main-content">
+<h2>{% trans "Add new Agenda Item for " %}{{ team_meeting_object.name}}</h2>
+<form action="." method="post">
+    	<div class="form" style="width:auto;"> 
+	    {{ form.as_template }}
+	    <div> 
+		{% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %}		
+		<input type="submit" name="submit" value="{% trans "Submit" %}" class="submit-button" /> 
+	    </div>
+    	</div>
+</form>
+</article>
+
+{% endblock %}

=== added file 'loco_directory/templates/meetings/agenda_item_update.html'
--- loco_directory/templates/meetings/agenda_item_update.html	1970-01-01 00:00:00 +0000
+++ loco_directory/templates/meetings/agenda_item_update.html	2011-01-22 16:08:54 +0000
@@ -0,0 +1,36 @@
+{% extends "base.html" %}
+{% load i18n admin_modify adminmedia %}
+
+{% block title %}{% trans "Update Agenda Item" %} | {% trans "Ubuntu LoCo Team Directory" %} {% endblock %}
+
+{% block sub_nav_links %}
+<a class="sub-nav-item" href="{% url team-meeting-detail team_meeting_object.id %}">{% trans "Back to Meeting Details" %}</a>
+{% endblock %}
+
+{% block extrahead %}{{ block.super }}
+{{form.media}}
+{% endblock %}
+
+{% block extrafooter %}
+<script type="text/javascript"><!--
+$(document).ready(function(){
+    $('span[rel*=help]').colorTip({color:'orange'});
+});
+--></script>
+{% endblock %}
+
+{% block content %}
+<article class="main-content">
+<h2>{% trans "Update Agenda Item" %}</h2>
+<form action="." method="post">
+    	<div class="form" style="width:auto;"> 
+	    {{ form.as_template }}
+	    <div> 
+		{% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %}		
+		<input type="submit" name="submit" value="{% trans "Submit" %}" class="submit-button" /> 
+	    </div>
+    	</div>
+</form>
+</article>
+
+{% endblock %}

=== modified file 'loco_directory/templates/meetings/team_meeting_detail_agenda.inc.html'
--- loco_directory/templates/meetings/team_meeting_detail_agenda.inc.html	2010-12-16 13:01:35 +0000
+++ loco_directory/templates/meetings/team_meeting_detail_agenda.inc.html	2011-01-22 16:08:54 +0000
@@ -1,10 +1,24 @@
 {% load i18n %}
+{% load recurse %}
+    <p>
+        <a href="{% url agenda-item-new team_meeting_object.id %}">{% trans 'Add Agenda Item' %}</a>
+    </p>
 {% if team_meeting_object.agenda %}
-<ol class="agenda-list">
-{% for item in team_meeting_object.agenda.as_tree %}
-{{item.as_ol|safe}}
-{% endfor %}
-</ol>
+    {% recurse item.children.all with team_meeting_object.agenda.top as item %}
+        <ol class="agenda-list">
+        {% loop %}
+            <li class="agenda-item">
+                <a class="agenda-title" href="{% url agenda-item-update team_meeting_object.id item.id %}">{{ item.title }}</a>
+                - <a class="agenda-sig" href="https://launchpad.net/~{{ item.owner.username}}">{{ item.owner.realname }}</a>
+                @ {{ item.created_date }}
+            {% if item.description %}
+                <div class="agenda-description">{{ item.description|linebreaks }}</div>
+            {% endif %}
+            </li>
+            {% child %}
+        {% endloop %}
+        </ol>
+    {% endrecurse %}
 {% else %}
 <p>{% trans "There are currently no items on the agenda." %}</p>
 {% endif %}

_______________________________________________
Mailing list: https://launchpad.net/~loco-directory-dev
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~loco-directory-dev
More help   : https://help.launchpad.net/ListHelp

Reply via email to