Merge pull request #584 from emanuelschuetze/participants-1

Participants fields: add 'title', remove 'type', use 'groups'
This commit is contained in:
Oskar Hahn 2013-04-09 13:25:18 -07:00
commit 632c9476ed
10 changed files with 162 additions and 105 deletions

View File

@ -1,12 +1,15 @@
"Vorname";"Nachname";"Geschlecht";"Gliederungsebene";"Typ";"Amt";"Kommentar" "Titel";"Vorname";"Nachname";"Geschlecht";"E-Mail";"Gruppen-ID";"Gliederungsebene";"Amt";"Über mich";"Kommentar";"Aktiviert"
"Angramain";"Aranea";"female";;"delegate";; ;"Angramain";"Aranea";"female";;;;;;;1
"Bastian";"Bux";"male";;"observer";"2. Vorsitzender"; ;"Bastian";"Bux";"male";;;;"2. Vorsitzender";;;1
"Emma";"Dampf";"female";"Ortsverband Berlin-Mitte";"delegate";"AG Frauen"; ;"Emma";"Dampf";"female";;3;"Ortsverband Berlin-Mitte";"AG Frauen";;;0
"David";"Delegierter";"male";"Ortsverband Berlin-Mitte";"delegate";"Haushaltsausschuss";"Demo-Account" "Dr.";"David";"Delegierter";"male";"david@example.com";3;"Ortsverband Berlin-Mitte";"Haushaltsausschuss";"Zu meiner Person:
"Marta";"Grankvist";"female";"Ortsverband Köln";"delegate";"1. Vorsitzende"; A
"Atréju";"Grün";"male";"Ortsverband Freiburg";"staff";"Versammlungsleitung"; B
"Li Si";"Mandala";"female";;"staff";; C";"Demo-Account";1
"Malin";"Melchersson";"male";"Gastredner e.V.";"guest";; ;"Marta";"Grankvist";"female";;3,4;"Ortsverband Köln";"1. Vorsitzende";;;0
"Molly";"Tender";"female";"Ortsverband Berlin-Mitte";"delegate";; "Prof. Dr.";"Atréju";"Grün";"male";;4;"Ortsverband Freiburg";"Versammlungsleitung";;;1
"Dr. Karl";"Tur Tur";"male";;"observer";; ;"Li Si";"Mandala";"female";;4;;;;;1
"Volker";"Versammlungsleitung";"male";"Ortsverband Hamburg";"delegate";"Versammlungsleitung";"Demo-Account" ;"Malin";"Melchersson";"male";;;"Gastredner e.V.";;;;1
;"Molly";"Tender";"female";;3;"Ortsverband Berlin-Mitte";;;;1
"Dr. med.";"Karl";"Tur Tur";"male";;;;;;;1
"Dipl.-Ing.";"Volker";"Versammlungsleitung";"male";"volker@example.com";3,4;"Ortsverband Hamburg";"Versammlungsleitung";;"Demo-Account";1

1 Titel Vorname Nachname Geschlecht Typ E-Mail Gruppen-ID Gliederungsebene Amt Über mich Kommentar Aktiviert
2 Angramain Aranea female delegate 1
3 Bastian Bux male observer 2. Vorsitzender 1
4 Emma Dampf female delegate 3 Ortsverband Berlin-Mitte AG Frauen 0
5 Dr. David Delegierter male delegate david@example.com 3 Ortsverband Berlin-Mitte Haushaltsausschuss Zu meiner Person: A B C Demo-Account 1
6 Marta Grankvist female delegate 3,4 Ortsverband Köln 1. Vorsitzende 0
7 Prof. Dr. Atréju Grün male staff 4 Ortsverband Freiburg Versammlungsleitung 1
8 Li Si Mandala female staff 4 1
9 Malin Melchersson male guest Gastredner e.V. 1
10 Molly Tender female delegate 3 Ortsverband Berlin-Mitte 1
11 Dr. med. Dr. Karl Karl Tur Tur male observer 1
12 Dipl.-Ing. Volker Versammlungsleitung male delegate volker@example.com 3,4 Ortsverband Hamburg Versammlungsleitung Demo-Account 1
13
14
15

View File

@ -1,12 +1,15 @@
"First Name";"Last Name";"Gender";"Structure Level";"Type";"Committee";"Comment" "First Name";"Last Name";"Gender";"Email";"Group id";"Structure Level";"Committee";"About me";"Comment";"Is active"
"Angramain";"Aranea";"female";;"delegate";; "Angramain";"Aranea";"female";;;;;;;1
"Bastian";"Bux";"male";;"observer";"2. Vorsitzender"; "Bastian";"Bux";"male";;;;"2. Vorsitzender";;;1
"Emma";"Dampf";"female";"Ortsverband Berlin-Mitte";"delegate";"AG Frauen"; "Emma";"Dampf";"female";;3;"Ortsverband Berlin-Mitte";"AG Frauen";;;0
"David";"Delegierter";"male";"Ortsverband Berlin-Mitte";"delegate";"Haushaltsausschuss";"Demo-Account" "David";"Delegierter";"male";"david@example.com";3;"Ortsverband Berlin-Mitte";"Haushaltsausschuss";"Zu meiner Person:
"Marta";"Grankvist";"female";"Ortsverband Köln";"delegate";"1. Vorsitzende"; A
"Atréju";"Grün";"male";"Ortsverband Freiburg";"staff";"Versammlungsleitung"; B
"Li Si";"Mandala";"female";;"staff";; C";"Demo-Account";1
"Malin";"Melchersson";"male";"Gastredner e.V.";"guest";; "Marta";"Grankvist";"female";;3,4;"Ortsverband Köln";"1. Vorsitzende";;;0
"Molly";"Tender";"female";"Ortsverband Berlin-Mitte";"delegate";; "Atréju";"Grün";"male";;4;"Ortsverband Freiburg";"Versammlungsleitung";;;1
"Dr. Karl";"Tur Tur";"male";;"observer";; "Li Si";"Mandala";"female";;4;;;;;1
"Volker";"Versammlungsleitung";"male";"Ortsverband Hamburg";"delegate";"Versammlungsleitung";"Demo-Account" "Malin";"Melchersson";"male";;;"Gastredner e.V.";;;;1
"Molly";"Tender";"female";;3;"Ortsverband Berlin-Mitte";;;;1
"Karl";"Tur Tur";"male";;;;;;;1
"Volker";"Versammlungsleitung";"male";"volker@example.com";3,4;"Ortsverband Hamburg";"Versammlungsleitung";;"Demo-Account";1

1 First Name Last Name Gender Email Type Group id Structure Level Committee About me Comment Is active
2 Angramain Aranea female delegate 1
3 Bastian Bux male observer 2. Vorsitzender 1
4 Emma Dampf female delegate 3 Ortsverband Berlin-Mitte AG Frauen 0
5 David Delegierter male david@example.com delegate 3 Ortsverband Berlin-Mitte Haushaltsausschuss Zu meiner Person: A B C Demo-Account 1
6 Marta Grankvist female delegate 3,4 Ortsverband Köln 1. Vorsitzende 0
7 Atréju Grün male staff 4 Ortsverband Freiburg Versammlungsleitung 1
8 Li Si Mandala female staff 4 1
9 Malin Melchersson male guest Gastredner e.V. 1
10 Molly Tender female delegate 3 Ortsverband Berlin-Mitte 1
11 Dr. Karl Karl Tur Tur male observer 1
12 Volker Versammlungsleitung male volker@example.com delegate 3,4 Ortsverband Hamburg Versammlungsleitung Demo-Account 1
13
14
15

View File

@ -67,21 +67,38 @@ def import_users(csv_file):
dialect=dialect)): dialect=dialect)):
if line_no: if line_no:
try: try:
(first_name, last_name, gender, structure_level, type, committee, comment) = line[:7] (title, first_name, last_name, gender, email, groups,
structure_level, committee, about_me, comment, is_active) = line[:11]
except ValueError: except ValueError:
error_messages.append(_('Ignoring malformed line %d in import file.') % (line_no + 1)) error_messages.append(_('Ignoring malformed line %d in import file.') % (line_no + 1))
continue continue
user = User() user = User()
user.title = title
user.last_name = last_name user.last_name = last_name
user.first_name = first_name user.first_name = first_name
user.username = gen_username(first_name, last_name) user.username = gen_username(first_name, last_name)
user.gender = gender user.gender = gender
user.email = email
user.structure_level = structure_level user.structure_level = structure_level
user.type = type
user.committee = committee user.committee = committee
user.about_me = about_me
user.comment = comment user.comment = comment
if is_active == '1':
user.is_active = True
else:
user.is_active = False
user.default_password = gen_password() user.default_password = gen_password()
user.save() user.save()
for groupid in groups:
try:
if groupid != ",":
Group.objects.get(pk=groupid).user_set.add(user)
except ValueError:
error_messages.append(_('Ignoring malformed group id in line %d.') % (line_no + 1))
continue
except Group.DoesNotExist:
error_messages.append(_('Group id %s does not exists (line %d).') % (groupid, line_no + 1))
continue
user.reset_password() user.reset_password()
count_success += 1 count_success += 1
except csv.Error: except csv.Error:

View File

@ -36,16 +36,17 @@ class UserCreateForm(forms.ModelForm, CssClassMixin):
class Meta: class Meta:
model = User model = User
fields = ('first_name', 'last_name', 'is_active', 'groups', 'structure_level', fields = ('title', 'first_name', 'last_name', 'gender', 'email',
'gender', 'type', 'committee', 'about_me', 'comment', 'default_password') 'groups', 'structure_level', 'committee', 'about_me', 'comment',
'is_active', 'default_password')
class UserUpdateForm(UserCreateForm): class UserUpdateForm(UserCreateForm):
class Meta: class Meta:
model = User model = User
fields = ('username', 'first_name', 'last_name', 'is_active', 'groups', fields = ('username', 'title', 'first_name', 'last_name', 'gender', 'email',
'structure_level', 'gender', 'type', 'committee', 'about_me', 'comment', 'groups', 'structure_level', 'committee', 'about_me', 'comment',
'default_password') 'is_active', 'default_password')
class GroupForm(forms.ModelForm, CssClassMixin): class GroupForm(forms.ModelForm, CssClassMixin):
@ -106,7 +107,8 @@ class UsersettingsForm(forms.ModelForm, CssClassMixin):
class Meta: class Meta:
model = User model = User
fields = ('username', 'first_name', 'last_name', 'gender', 'email', 'committee', 'about_me') fields = ('username', 'title', 'first_name', 'last_name', 'gender', 'email',
'committee', 'about_me')
class UserImportForm(forms.Form, CssClassMixin): class UserImportForm(forms.Form, CssClassMixin):

View File

@ -30,25 +30,19 @@ class User(PersonMixin, Person, SlideMixin, DjangoUser):
('male', _('Male')), ('male', _('Male')),
('female', _('Female')), ('female', _('Female')),
) )
TYPE_CHOICES = (
('delegate', _('Delegate')),
('observer', _('Observer')),
('staff', _('Staff')),
('guest', _('Guest')),
)
django_user = models.OneToOneField(DjangoUser, editable=False, parent_link=True) django_user = models.OneToOneField(DjangoUser, editable=False, parent_link=True)
structure_level = models.CharField( structure_level = models.CharField(
max_length=100, blank=True, default='', verbose_name=_("Structure level"), max_length=255, blank=True, default='', verbose_name=_("Structure level"),
help_text=_('Will be shown after the name.')) help_text=_('Will be shown after the name.'))
title = models.CharField(
max_length=50, blank=True, default='', verbose_name=_("Titel"),
help_text=_('Will be shown before the name.'))
gender = models.CharField( gender = models.CharField(
max_length=50, choices=GENDER_CHOICES, blank=True, max_length=50, choices=GENDER_CHOICES, blank=True,
verbose_name=_("Gender"), help_text=_('Only for filtering the participant list.')) verbose_name=_("Gender"), help_text=_('Only for filtering the participant list.'))
type = models.CharField(
max_length=100, choices=TYPE_CHOICES, blank=True,
verbose_name=_("Typ"), help_text=_('Only for filtering the participant list.'))
committee = models.CharField( committee = models.CharField(
max_length=100, blank=True, default='', verbose_name=_("Committee"), max_length=255, blank=True, default='', verbose_name=_("Committee"),
help_text=_('Only for filtering the participant list.')) help_text=_('Only for filtering the participant list.'))
about_me = models.TextField( about_me = models.TextField(
blank=True, default='', verbose_name=_('About me'), blank=True, default='', verbose_name=_('About me'),
@ -62,7 +56,11 @@ class User(PersonMixin, Person, SlideMixin, DjangoUser):
@property @property
def clean_name(self): def clean_name(self):
return self.get_full_name() or self.username if self.title:
name = "%s %s" % (self.title, self.get_full_name())
else:
name = self.get_full_name()
return name or self.username
def get_name_suffix(self): def get_name_suffix(self):
return self.structure_level return self.structure_level

View File

@ -13,15 +13,21 @@
</h1> </h1>
<p>{% trans 'Select a CSV file to import participants!' %}</p> <p>{% trans 'Select a CSV file to import participants!' %}</p>
<p>{% trans 'Required comma separated values' %}: <p>{% trans 'Please note' %}:</p>
<code>({% trans 'first_name, last_name, gender, structure level, type, committee, comment' %})</code> <ul>
<br> <li>
{% trans 'Required CSV file encoding: UTF-8 (Unicode).' %} {% trans 'Required comma separated values' %}:<br>
</p> <code>({% trans 'title, first name, last name, gender, email, group id, structure level, committee, about me, comment, is active' %})</code>
</li>
<p><a href="https://github.com/OpenSlides/OpenSlides/wiki/CSV-Import" target="_blank">{% trans 'A CSV example file is available in OpenSlides Wiki.' %}</a> <li>
</p> {% trans 'Default groups' %}:
{% trans 'Anonymous' %} (<code>1</code>), {% trans 'Registered' %} (<code>2</code>),
{% trans 'Delegate' %} (<code>3</code>), {% trans 'Staff' %} (<code>4</code>)
</li>
<li>{% trans 'Required CSV file encoding: UTF-8 (Unicode).' %}</li>
<li><a href="https://github.com/OpenSlides/OpenSlides/wiki/CSV-Import" target="_blank">{% trans 'Use the CSV example file from OpenSlides Wiki.' %}</a></li>
</ul>
<form enctype="multipart/form-data" action="" method="post">{% csrf_token %} <form enctype="multipart/form-data" action="" method="post">{% csrf_token %}
{% include "form.html" %} {% include "form.html" %}

View File

@ -59,10 +59,11 @@
<thead> <thead>
<tr> <tr>
<th>{% trans "Present" %}</th> <th>{% trans "Present" %}</th>
<th class="optional">{% trans "Title" %}</th>
<th>{% trans "First Name" %}</th> <th>{% trans "First Name" %}</th>
<th>{% trans "Last Name" %}</th> <th>{% trans "Last Name" %}</th>
<th class="optional">{% trans "Structure level" %}</th> <th class="optional">{% trans "Structure level" %}</th>
<th class="optional">{% trans "Type" %}</th> <th class="optional">{% trans "Group" %}</th>
<th class="optional">{% trans "Committee" %}</th> <th class="optional">{% trans "Committee" %}</th>
{% if perms.participant.can_manage_participant %} {% if perms.participant.can_manage_participant %}
<th class="optional">{% trans "Comment" %}</th> <th class="optional">{% trans "Comment" %}</th>
@ -88,10 +89,18 @@
title="{% if user.is_active %}{% trans 'present' %}{% else %}{% trans 'absent' %}{% endif %}"></i> title="{% if user.is_active %}{% trans 'present' %}{% else %}{% trans 'absent' %}{% endif %}"></i>
{% endif %} {% endif %}
</td> </td>
<td class="optional">{{ user.title }}</td>
<td><a href="{% url 'user_view' user.id %}">{{ user.first_name }}</a></td> <td><a href="{% url 'user_view' user.id %}">{{ user.first_name }}</a></td>
<td><a href="{% url 'user_view' user.id %}">{{ user.last_name }}</a></td> <td><a href="{% url 'user_view' user.id %}">{{ user.last_name }}</a></td>
<td class="optional">{{ user.structure_level }}</td> <td class="optional">{{ user.structure_level }}</td>
<td class="optional">{{ user.get_type_display }}</td> <td class="optional">
{% for group in user.groups.all %}
{% if group.name != 'Registered' %}
{{ group}}
{% if not forloop.last %}<br>{% endif %}
{% endif %}
{% endfor %}
</td>
<td class="optional">{{ user.committee }}</td> <td class="optional">{{ user.committee }}</td>
{% if perms.participant.can_manage_participant %} {% if perms.participant.can_manage_participant %}
<td class="optional">{{ user.comment|first_line }}</td> <td class="optional">{{ user.comment|first_line }}</td>

View File

@ -7,55 +7,50 @@
{% block content %} {% block content %}
<h1>{{ shown_user }} <h1>{{ shown_user.clean_name }}
<small class="pull-right"> <small class="pull-right">
<a href="{% url 'user_overview' %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to overview" %}</a> <a href="{% url 'user_overview' %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to overview" %}</a>
</small> </small>
</h1> </h1>
<p>{{ shown_user.email }}</p>
<h4>{% trans "Groups" %}</h4>
<p> <fieldset>
<legend>{% trans "Personal data" %}</legend>
<label>{% trans "Gender" %}</label>
{{ shown_user.get_gender_display }}
<label>{% trans "Email" %}</label>
{{ shown_user.email }}
<label>{% trans "About me" %}</label>
{{ shown_user.about_me|linebreaks }}
</fieldset>
<fieldset>
<legend>{% trans "Event data" %}</legend>
<label>{% trans "Structure level" %}</label>
{{ shown_user.structure_level }}
<label>{% trans "Committee" %}</label>
{{ shown_user.committee }}
<label>{% trans "Groups" %}</label>
{% if shown_user.groups.all %} {% if shown_user.groups.all %}
{{ shown_user.groups.all|join:", " }} {{ shown_user.groups.all|join:", " }}
{% else %} {% else %}
{% trans "The participant is not member of any group." %} {% trans "The participant is not member of any group." %}
{% endif %} {% endif %}
</p> </fieldset>
{% if shown_user.get_gender_display %}
<h4>{% trans "Gender" %}</h4>
<p>{{ shown_user.get_gender_display }}</p>
{% endif %}
{% if shown_user.get_type_display %}
<h4>{% trans "Type" %}</h4>
<p>{{ shown_user.get_type_display }}</p>
{% endif %}
{% if shown_user.committee %}
<h4>{% trans "Committee" %}</h4>
<p>{{ shown_user.committee }}</p>
{% endif %}
{% if shown_user.about_me %}
<h4>{% trans "About me" %}</h4>
<p>{{ shown_user.about_me }}</p>
{% endif %}
{% if perms.participant.can_manage_participant %} {% if perms.participant.can_manage_participant %}
{% if shown_user.comment %} <fieldset>
<h4>{% trans "Comment" %}</h4> <legend>{% trans "Administrative data" %}</legend>
<p>{{ shown_user.comment }}</p> <label>{% trans "User name" %}</label>
{% endif %} {{ shown_user.username }}
<label>{% trans "Comment" %}</label>
<h4>{% trans "Last Login" %}</h4> {{ shown_user.comment|linebreaks }}
<label>{% trans "Last Login" %}</label>
{% if shown_user.last_login > shown_user.date_joined %} {% if shown_user.last_login > shown_user.date_joined %}
<p>{{ shown_user.last_login }}</p> {{ shown_user.last_login }}
{% else %} {% else %}
<p>{% trans "The participant has not logged in yet." %}</p> {% trans "The participant has not logged in yet." %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -86,16 +86,6 @@ class UserDetailView(DetailView, PermissionMixin):
context_object_name = 'shown_user' context_object_name = 'shown_user'
class GroupDetailView(DetailView, PermissionMixin):
"""
Classed based view to show a specific group in the interface.
"""
permission_required = 'participant.can_manage_participant'
model = Group
template_name = 'participant/group_detail.html'
context_object_name = 'group'
class UserCreateView(CreateView): class UserCreateView(CreateView):
""" """
Create a new participant. Create a new participant.
@ -184,8 +174,8 @@ class ParticipantsListPDF(PDFView):
document_title = ugettext_lazy('List of Participants') document_title = ugettext_lazy('List of Participants')
def append_to_pdf(self, story): def append_to_pdf(self, story):
data = [['#', _('Last Name'), _('First Name'), _('Group'), _('Type'), data = [['#', _('Title'), _('Last Name'), _('First Name'),
_('Committee')]] _('Structure level'), _('Group'), _('Committee')]]
if config['participant_sort_users_by_first_name']: if config['participant_sort_users_by_first_name']:
sort = 'first_name' sort = 'first_name'
else: else:
@ -193,12 +183,17 @@ class ParticipantsListPDF(PDFView):
counter = 0 counter = 0
for user in User.objects.all().order_by(sort): for user in User.objects.all().order_by(sort):
counter += 1 counter += 1
groups = ''
for group in user.groups.all():
if unicode(group) != "Registered":
groups += "%s<br/>" % unicode(group)
data.append([ data.append([
counter, counter,
Paragraph(user.title, stylesheet['Tablecell']),
Paragraph(user.last_name, stylesheet['Tablecell']), Paragraph(user.last_name, stylesheet['Tablecell']),
Paragraph(user.first_name, stylesheet['Tablecell']), Paragraph(user.first_name, stylesheet['Tablecell']),
Paragraph(user.structure_level, stylesheet['Tablecell']), Paragraph(user.structure_level, stylesheet['Tablecell']),
Paragraph(user.get_type_display(), stylesheet['Tablecell']), Paragraph(groups, stylesheet['Tablecell']),
Paragraph(user.committee, stylesheet['Tablecell'])]) Paragraph(user.committee, stylesheet['Tablecell'])])
t = LongTable(data, style=[ t = LongTable(data, style=[
('VALIGN', (0, 0), (-1, -1), 'TOP'), ('VALIGN', (0, 0), (-1, -1), 'TOP'),
@ -292,7 +287,7 @@ class UserImportView(FormView):
permission_required = 'participant.can_manage_participant' permission_required = 'participant.can_manage_participant'
template_name = 'participant/import.html' template_name = 'participant/import.html'
form_class = UserImportForm form_class = UserImportForm
success_url_name = 'user_import' success_url_name = 'user_overview'
def form_valid(self, form): def form_valid(self, form):
# check for valid encoding (will raise UnicodeDecodeError if not) # check for valid encoding (will raise UnicodeDecodeError if not)
@ -339,6 +334,16 @@ class GroupOverview(ListView):
model = Group model = Group
class GroupDetailView(DetailView, PermissionMixin):
"""
Classed based view to show a specific group in the interface.
"""
permission_required = 'participant.can_manage_participant'
model = Group
template_name = 'participant/group_detail.html'
context_object_name = 'group'
class GroupCreateView(CreateView): class GroupCreateView(CreateView):
""" """
Create a new group. Create a new group.

View File

@ -117,7 +117,19 @@ tr.total td {
.optional { .optional {
display: auto; display: auto;
} }
fieldset {
margin-bottom: 10px;
}
fieldset legend {
margin-bottom: 5px;
}
fieldset label {
font-weight: bold;
margin: 10px 0 0 0;
}
fieldset label:after {
content: ":";
}
/** Forms **/ /** Forms **/
input, textarea { input, textarea {
@ -144,7 +156,14 @@ form .required label:after {
legend + .control-group { legend + .control-group {
margin-top: 0px !important; margin-top: 0px !important;
} }
#id_permissions {
height: 310px;
width: auto;
}
#id_submitter, #id_supporter, #id_users {
height: 110px;
width: auto;
}
/** Left sitebar navigation **/ /** Left sitebar navigation **/
.leftmenu ul { .leftmenu ul {