Fix and update motion csv import.
This commit is contained in:
parent
485518975a
commit
0f17f74fee
@ -1,7 +1,7 @@
|
|||||||
Identifier,Titel,Text,Begründung,Antragsteller ID
|
Bezeichner,Titel,Text,Begründung,Antragsteller,Sachgebiet
|
||||||
1,Entlastung des Vorstandes,"Die Versammlung möge beschließen, den Vorstand für seine letzte Legislaturperiode zu entlasten.","Bericht erfolgt mündlich.",user:5
|
1,Entlastung des Vorstandes,"Die Versammlung möge beschließen, den Vorstand für seine letzte Legislaturperiode zu entlasten.","Bericht erfolgt mündlich.",Max Mustermann,Vorstandsangelegenheiten
|
||||||
2,"Satzungsänderung §2, Abs.3","Die Versammlung möge beschließen, die Satzung in § 2 Abs. 3 wie folgt zu ändern:
|
S 2,"Satzungsänderung § 2 Abs. 3","Die Versammlung möge beschließen, die Satzung in § 2 Abs. 3 wie folgt zu ändern:
|
||||||
|
|
||||||
Es wird nach dem Wort ""Zweck"" der Satz ""..."" eingefügt.","Die Änderung der Satzung ist aufgrund der letzten Erfahrungen eine sinnvolle Maßnahme, weil ...",user:2
|
Es wird nach dem Wort ""Zweck"" der Satz ""..."" eingefügt.","Die Änderung der Satzung ist aufgrund der letzten Erfahrungen eine sinnvolle Maßnahme, weil ...",Fritz Fleiner,Satzung
|
||||||
3,"Einführung von elektronischen Abstimmungen mit OpenSlides","Die Versammlung möge beschließen, öffentliche Abstimmungen künftig elektronisch mit dem OpenSlides Plugin ""VoteCollector"" durchzuführen.", Elektronische Abstimmungen beschleunigen den Ablauf. OpenSlides wird bereits bei uns eingesetzt und bietet ein zusätzliches Plugin, um mit Keypads für jeden Teilnehmer elektronisch abzustimmen. Die Ergebnisse werden direkt in OpenSlides gespeichert. Details gibts über den professional Support auf openslides.org.",user:3
|
3,"Einführung von elektronischen Abstimmungen mit OpenSlides","Die Versammlung möge beschließen, öffentliche Abstimmungen künftig elektronisch mit dem OpenSlides Plugin ""VoteCollector"" durchzuführen.","Elektronische Abstimmungen beschleunigen den Ablauf. OpenSlides wird bereits bei uns eingesetzt und bietet ein zusätzliches Plugin, um mit Keypads für jeden Teilnehmer elektronisch abzustimmen. Die Ergebnisse werden direkt in OpenSlides gespeichert. Details gibts über den professional Support auf http://openslides.org.",,
|
||||||
,"Resolution","Die Versammlung möge beschließen, die Resolution zum Thema OpenSlides vom Ortsverband-Mitte zu verabschieden.",,user:3
|
,"Resolution","Die Versammlung möge beschließen, die Resolution zum Thema OpenSlides vom Ortsverband-Mitte zu verabschieden.",,Dr. Hilde Müller,Resolution
|
||||||
|
Can't render this file because it contains an unexpected character in line 6 and column 527.
|
@ -1,7 +1,2 @@
|
|||||||
Number;Title;Text;Reason;Submitter (First Name);Submitter (Last Name);Submitter is Group
|
Identifier,Title,Text,Reason,Submitter,Category
|
||||||
1;Entlastung des Vorstandes;Die Versammlung möge beschließen, den Vorstand für seine letzte Legislaturperiode zu entlasten.;Bericht erfolgt mündlich.;Volker;Versammlungsleitung;0
|
H 1,New proposal,"The assembly may decide, that everyone should eat more apples, esp. ""Golden Delicious"".",Apples are very tasty.,John Smith,Health
|
||||||
2;Satzungsänderung §2, Abs.3;"Die Versammlung möge beschließen, die Satzung in § 2 Abs. 3 wie folgt zu ändern:
|
|
||||||
|
|
||||||
Es wird nach dem Wort ""Zweck"" der Satz ""..."" eingefügt.";Die Änderung der Satzung ist aufgrund der letzten Erfahrungen eine sinnvolle Maßnahme, weil ...;David;Delegierter;0
|
|
||||||
3;Einführung von elektronischen Abstimmungen mit OpenSlides;"Die Versammlung möge beschließen, öffentliche Abstimmungen künftig elektronisch mit dem OpenSlides Plugin ""VoteCollector"" durchzuführen.";Elektronische Abstimmungen beschleunigen den Ablauf. OpenSlides wird bereits bei uns eingesetzt und bietet ein zusätzliches Plugin, um mit Keypads für jeden Teilnehmer elektronisch abzustimmen. Die Ergebnisse werden direkt in OpenSlides gespeichert. Details gibts über den professional Support auf openslides.org.;Emma;Dampf;0
|
|
||||||
;Resolution;Die Versammlung möge beschließen, die Resolution zum Thema OpenSlides vom Ortsverband-Mitte zu verabschieden.;;;Vorstand;1
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
|||||||
openslides.motion.csv_import
|
openslides.motion.csv_import
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Functions to import motions from csv.
|
Functions to import motions from a csv file.
|
||||||
|
|
||||||
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
@ -15,44 +15,93 @@
|
|||||||
# http://docs.python.org/2/tutorial/modules.html#intra-package-references
|
# http://docs.python.org/2/tutorial/modules.html#intra-package-references
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from openslides.utils import csv_ext
|
from openslides.utils.person.api import Persons
|
||||||
from .models import Motion
|
|
||||||
|
from .models import Motion, Category
|
||||||
|
|
||||||
|
|
||||||
def import_motions(csv_file):
|
def import_motions(csv_file, default_submitter, override=False):
|
||||||
|
"""
|
||||||
|
Imports motions from a csv file.
|
||||||
|
|
||||||
|
The file must be encoded in utf8. The first line (header) is ignored.
|
||||||
|
If no or multiple submitters found, the default submitter is used. If
|
||||||
|
a motion with a given identifier already exists, the motion is overridden,
|
||||||
|
when the flag 'override' is true. If no or multiple categories found,
|
||||||
|
the category is set to None.
|
||||||
|
"""
|
||||||
error_messages = []
|
error_messages = []
|
||||||
|
warning_messages = []
|
||||||
count_success = 0
|
count_success = 0
|
||||||
csv_file.read().decode('utf-8')
|
|
||||||
|
# Check encoding
|
||||||
|
try:
|
||||||
|
csv_file.read().decode('utf8')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
return (0, [_('Encoding error in import file. Ensure using UTF-8.')], [])
|
||||||
csv_file.seek(0)
|
csv_file.seek(0)
|
||||||
|
|
||||||
with transaction.commit_on_success():
|
with transaction.commit_on_success():
|
||||||
for (line_no, line) in enumerate(csv.reader(csv_file)):
|
for (line_no, line) in enumerate(csv.reader(csv_file)):
|
||||||
if line_no < 1:
|
if line_no < 1:
|
||||||
# Do not read the header line
|
# Do not read the header line
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# TODO: test for wrong format
|
# Check format
|
||||||
try:
|
try:
|
||||||
(identifier, title, text, reason, person_id) = line[:5]
|
(identifier, title, text, reason, submitter, category) = line[:6]
|
||||||
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
|
||||||
|
|
||||||
|
# Check existing motions according to the identifier
|
||||||
if identifier:
|
if identifier:
|
||||||
try:
|
try:
|
||||||
motion = Motion.objects.get(identifier=identifier)
|
motion = Motion.objects.get(identifier=identifier)
|
||||||
except Motion.DoesNotExist:
|
except Motion.DoesNotExist:
|
||||||
motion = Motion()
|
motion = Motion(identifier=identifier)
|
||||||
|
else:
|
||||||
|
if not override:
|
||||||
|
error_messages.append(_('Line %d in import file: Ignoring existing motion.') % (line_no + 1))
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
motion = Motion()
|
motion = Motion()
|
||||||
|
|
||||||
|
# Insert data
|
||||||
motion.title = title
|
motion.title = title
|
||||||
motion.text = text
|
motion.text = text
|
||||||
motion.reason = reason
|
motion.reason = reason
|
||||||
|
if category:
|
||||||
|
try:
|
||||||
|
motion.category = Category.objects.get(name=category)
|
||||||
|
except Category.DoesNotExist:
|
||||||
|
error_messages.append(_('Line %d in import file: Category not found.') % (line_no + 1))
|
||||||
|
except Category.MultipleObjectsReturned:
|
||||||
|
error_messages.append(_('Line %d in import file: Multiple categories found.') % (line_no + 1))
|
||||||
motion.save()
|
motion.save()
|
||||||
# TODO: person does not exist
|
|
||||||
motion.add_submitter(person_id)
|
# Add submitter
|
||||||
|
person_found = False
|
||||||
|
if submitter:
|
||||||
|
for person in Persons():
|
||||||
|
if person.clean_name == submitter.decode('utf8'):
|
||||||
|
if person_found:
|
||||||
|
error_messages.append(_('Line %d in import file: Multiple persons found.') % (line_no + 1))
|
||||||
|
person_found = False
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
new_submitter = person
|
||||||
|
person_found = True
|
||||||
|
if not person_found:
|
||||||
|
warning_messages.append(_('Line %d in import file: Default submitter is used.') % (line_no + 1))
|
||||||
|
new_submitter = default_submitter
|
||||||
|
motion.clear_submitters()
|
||||||
|
motion.add_submitter(new_submitter)
|
||||||
|
|
||||||
count_success += 1
|
count_success += 1
|
||||||
return (count_success, error_messages)
|
|
||||||
|
return (count_success, error_messages, warning_messages)
|
||||||
|
@ -124,5 +124,31 @@ class MotionIdentifierMixin(forms.ModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class MotionImportForm(CssClassMixin, forms.Form):
|
class MotionImportForm(CssClassMixin, forms.Form):
|
||||||
csvfile = forms.FileField(widget=forms.FileInput(attrs={'size': '50'}),
|
"""
|
||||||
label=ugettext_lazy('CSV File'))
|
Form for motion import via csv file.
|
||||||
|
"""
|
||||||
|
csvfile = forms.FileField(
|
||||||
|
widget=forms.FileInput(attrs={'size': '50'}),
|
||||||
|
label=ugettext_lazy('CSV File'),
|
||||||
|
help_text=ugettext_lazy('The file should be encoded in UTF-8.'))
|
||||||
|
"""
|
||||||
|
CSV filt with import data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
override = forms.BooleanField(
|
||||||
|
required=False,
|
||||||
|
label=ugettext_lazy('Override existing motions with the same identifier'),
|
||||||
|
help_text=ugettext_lazy('If this is active, every motion with the same identifier as in your csv file will be overridden.'))
|
||||||
|
"""
|
||||||
|
Flag to decide whether existing motions (according to the identifier)
|
||||||
|
should be overridden.
|
||||||
|
"""
|
||||||
|
|
||||||
|
default_submitter = PersonFormField(
|
||||||
|
required=True,
|
||||||
|
label=ugettext_lazy('Default submitter'),
|
||||||
|
help_text=ugettext_lazy('This person is used as submitter for any line of your csv file which does not contain valid submitter data.'))
|
||||||
|
"""
|
||||||
|
Person which is used as submitter, if the line of the csv file does
|
||||||
|
not contain valid submitter data.
|
||||||
|
"""
|
||||||
|
@ -386,6 +386,9 @@ class Motion(SlideMixin, models.Model):
|
|||||||
def add_submitter(self, person):
|
def add_submitter(self, person):
|
||||||
MotionSubmitter.objects.create(motion=self, person=person)
|
MotionSubmitter.objects.create(motion=self, person=person)
|
||||||
|
|
||||||
|
def clear_submitters(self):
|
||||||
|
MotionSubmitter.objects.filter(motion=self).delete()
|
||||||
|
|
||||||
def is_supporter(self, person):
|
def is_supporter(self, person):
|
||||||
"""
|
"""
|
||||||
Return True, if person is a supporter of this motion. Else: False.
|
Return True, if person is a supporter of this motion. Else: False.
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
{% extends "base.html" %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}{{ block.super }} – {% trans "Import motions" %} {% endblock %}
|
{% block title %}{{ block.super }} – {% trans 'Import motions' %} {% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>
|
<h1>
|
||||||
{% trans 'Import motions' %}
|
{% trans 'Import motions' %}
|
||||||
<small class="pull-right">
|
<small class="pull-right">
|
||||||
<a href="{% url 'motion_list' %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans "Back to overview" %}</a>
|
<a href="{% url 'motion_list' %}" class="btn btn-mini"><i class="icon-chevron-left"></i> {% trans 'Back to overview' %}</a>
|
||||||
</small>
|
</small>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p>{% trans 'Select a CSV file to import motions!' %}</p>
|
<p>{% trans 'Select a CSV file to import motions' %}.</p>
|
||||||
|
|
||||||
<p>{% trans 'Please note' %}:</p>
|
<p>{% trans 'Please note' %}:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
{% trans 'Required comma separated values' %}:<br>
|
{% trans 'Required comma separated values' %}:<br />
|
||||||
<code>({% trans 'identifier, title, text, reason, submitter_id' %})</code>
|
<code>({% trans 'identifier, title, text, reason, submitter (clean name), category' %})</code>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{% trans 'identifier and reason are optional and may be empty' %}.
|
{% trans 'Identifier, reason, submitter and category are optional and may be empty' %}.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{% trans 'Required CSV file encoding: UTF-8 (Unicode).' %}
|
{% trans 'Required CSV file encoding: UTF-8.' %}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://github.com/OpenSlides/OpenSlides/wiki/CSV-Import">{% trans 'Use the CSV example file from OpenSlides Wiki.' %}</a>
|
<a href="https://github.com/OpenSlides/OpenSlides/wiki/CSV-Import">{% trans 'Use the CSV example file from OpenSlides Wiki.' %}</a>
|
||||||
@ -32,7 +32,7 @@
|
|||||||
</ul>
|
</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' %}
|
||||||
<p>
|
<p>
|
||||||
<button class="btn btn-primary" type="submit">
|
<button class="btn btn-primary" type="submit">
|
||||||
<span class="icon import">{% trans 'Import' %}</span>
|
<span class="icon import">{% trans 'Import' %}</span>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.motion.can_manage_motion %}
|
{% if perms.motion.can_manage_motion %}
|
||||||
<a href="{% url 'motion_category_list' %}" class="btn btn-mini"><i class="icon-th-large"></i> {% trans 'Categories' %}</a>
|
<a href="{% url 'motion_category_list' %}" class="btn btn-mini"><i class="icon-th-large"></i> {% trans 'Categories' %}</a>
|
||||||
{# <a href="{% url 'motion_import' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Import motions' %}"><i class="icon-import"></i> {% trans 'Import' %}</a>#}
|
<a href="{% url 'motion_csv_import' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Import motions' %}"><i class="icon-import"></i> {% trans 'Import' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{% url 'motion_list_pdf' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Print all motions as PDF' %}"><i class="icon-print"></i> PDF</a>
|
<a href="{% url 'motion_list_pdf' %}" class="btn btn-mini" rel="tooltip" data-original-title="{% trans 'Print all motions as PDF' %}"><i class="icon-print"></i> PDF</a>
|
||||||
</small>
|
</small>
|
||||||
|
@ -672,16 +672,31 @@ class MotionCSVImportView(FormView):
|
|||||||
"""
|
"""
|
||||||
Import motions via csv.
|
Import motions via csv.
|
||||||
"""
|
"""
|
||||||
permission_required = 'motions.can_manage_participant'
|
permission_required = 'motion.can_manage_motion'
|
||||||
template_name = 'motion/motion_form_csv_import.html'
|
template_name = 'motion/motion_form_csv_import.html'
|
||||||
form_class = MotionImportForm
|
form_class = MotionImportForm
|
||||||
success_url_name = 'motion_list'
|
success_url_name = 'motion_list'
|
||||||
|
|
||||||
|
def get_initial(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Sets the request user as initial for the default submitter.
|
||||||
|
"""
|
||||||
|
return_value = super(MotionCSVImportView, self).get_initial(*args, **kwargs)
|
||||||
|
return_value.update({'default_submitter': self.request.user.person_id})
|
||||||
|
return return_value
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
# check for valid encoding (will raise UnicodeDecodeError if not)
|
"""
|
||||||
count_success, error_messages = import_motions(self.request.FILES['csvfile'])
|
Processes the import function.
|
||||||
|
"""
|
||||||
|
count_success, error_messages, warning_messages = import_motions(
|
||||||
|
self.request.FILES['csvfile'],
|
||||||
|
default_submitter=self.request.user,
|
||||||
|
override=form.cleaned_data['override'])
|
||||||
for message in error_messages:
|
for message in error_messages:
|
||||||
messages.error(self.request, message)
|
messages.error(self.request, message)
|
||||||
|
for message in warning_messages:
|
||||||
|
messages.warning(self.request, message)
|
||||||
if count_success:
|
if count_success:
|
||||||
messages.success(
|
messages.success(
|
||||||
self.request,
|
self.request,
|
||||||
|
@ -8,28 +8,94 @@
|
|||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import StringIO
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.utils.test import TestCase
|
from openslides.motion.models import Motion, Category
|
||||||
from openslides.participant.models import User
|
|
||||||
from openslides.motion.models import Motion
|
|
||||||
from openslides.motion.csv_import import import_motions
|
from openslides.motion.csv_import import import_motions
|
||||||
|
from openslides.participant.models import User
|
||||||
|
from openslides.utils.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
class CSVImport(TestCase):
|
class CSVImport(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Admin
|
# Admin
|
||||||
self.admin = User.objects.create_superuser('admin', 'admin@admin.admin', 'admin')
|
self.admin = User.objects.create_superuser('Admin_ieY0Eereimeimeizuosh', 'admin@admin.admin', 'eHiK1aiRahxaix0Iequ2')
|
||||||
self.admin_client = Client()
|
self.admin_client = Client()
|
||||||
self.admin_client.login(username='admin', password='admin')
|
self.admin_client.login(username='Admin_ieY0Eereimeimeizuosh', password='eHiK1aiRahxaix0Iequ2')
|
||||||
|
|
||||||
|
# Normal user
|
||||||
|
self.normal_user = User.objects.create_user('User_CiuNgo4giqueeChie5oi', 'user@user.user', 'eihi1Eequaek4eagaiKu')
|
||||||
|
self.normal_client = Client()
|
||||||
|
self.normal_client.login(username='User_CiuNgo4giqueeChie5oi', password='eihi1Eequaek4eagaiKu')
|
||||||
|
|
||||||
|
# Category
|
||||||
|
self.category1 = Category.objects.create(name='Satzung', prefix='S')
|
||||||
|
self.category2 = Category.objects.create(name='Resolution', prefix='R1')
|
||||||
|
self.category3 = Category.objects.create(name='Resolution', prefix='R2')
|
||||||
|
|
||||||
|
def test_example_file_de(self):
|
||||||
|
special_user = User.objects.create_user(username='Fritz_Fleiner',
|
||||||
|
email='fritz@fritz.fritz',
|
||||||
|
password='iegheeChaje7guthie4a',
|
||||||
|
first_name='Fritz',
|
||||||
|
last_name='Fleiner')
|
||||||
|
|
||||||
|
for i in range(2):
|
||||||
|
username = 'Hilde_Müller_%d' % i
|
||||||
|
User.objects.create_user(username=username,
|
||||||
|
email='hilde@hilde.hilde',
|
||||||
|
password='default',
|
||||||
|
first_name='Hilde',
|
||||||
|
last_name='Müller',
|
||||||
|
title='Dr.')
|
||||||
|
|
||||||
def test_example_file(self):
|
|
||||||
csv_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'extras', 'csv-examples')
|
csv_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'extras', 'csv-examples')
|
||||||
|
self.assertEqual(Motion.objects.count(), 0)
|
||||||
with open(csv_dir + '/motions-demo_de.csv') as f:
|
with open(csv_dir + '/motions-demo_de.csv') as f:
|
||||||
import_motions(f)
|
count_success, error_messages, warning_messages = import_motions(csv_file=f, default_submitter=self.normal_user.person_id)
|
||||||
self.assertEqual(Motion.objects.count(), 4)
|
self.assertEqual(Motion.objects.count(), 4)
|
||||||
|
self.assertEqual(count_success, 4)
|
||||||
|
|
||||||
|
motion1 = Motion.objects.get(pk=1)
|
||||||
|
self.assertEqual(motion1.identifier, '1')
|
||||||
|
self.assertEqual(motion1.title, u'Entlastung des Vorstandes')
|
||||||
|
self.assertEqual(motion1.text, u'Die Versammlung möge beschließen, den Vorstand für seine letzte Legislaturperiode zu entlasten.')
|
||||||
|
self.assertEqual(motion1.reason, u'Bericht erfolgt mündlich.')
|
||||||
|
self.assertEqual(len(motion1.submitter.all()), 1)
|
||||||
|
self.assertEqual(motion1.submitter.all()[0].person, self.normal_user)
|
||||||
|
self.assertTrue(motion1.category is None)
|
||||||
|
self.assertTrue('Line 2 in import file: Default submitter is used.' in warning_messages)
|
||||||
|
self.assertTrue('Line 2 in import file: Category not found.' in error_messages)
|
||||||
|
|
||||||
|
motion2 = Motion.objects.get(pk=2)
|
||||||
|
self.assertEqual(motion2.identifier, 'S 2')
|
||||||
|
self.assertEqual(motion2.title, u'Satzungsänderung § 2 Abs. 3')
|
||||||
|
self.assertEqual(motion2.text, u'Die Versammlung möge beschließen, die Satzung in § 2 Abs. 3 wie folgt zu ändern:\n\n'
|
||||||
|
u'Es wird nach dem Wort "Zweck" der Satz "..." eingefügt.')
|
||||||
|
self.assertEqual(motion2.reason, u'Die Änderung der Satzung ist aufgrund der letzten Erfahrungen eine sinnvolle Maßnahme, weil ...')
|
||||||
|
self.assertEqual(len(motion2.submitter.all()), 1)
|
||||||
|
self.assertEqual(motion2.submitter.all()[0].person, special_user)
|
||||||
|
self.assertEqual(motion2.category, self.category1)
|
||||||
|
|
||||||
|
self.assertTrue('Line 5 in import file: Multiple persons found.' in error_messages)
|
||||||
|
self.assertTrue('Line 5 in import file: Multiple categories found.' in error_messages)
|
||||||
|
|
||||||
|
def test_malformed_file(self):
|
||||||
|
csv_file = StringIO.StringIO()
|
||||||
|
csv_file.write('Header\nMalformed data,\n,Title,Text,,,\n')
|
||||||
|
count_success, error_messages, warning_messages = import_motions(csv_file=csv_file, default_submitter=self.normal_user.person_id)
|
||||||
|
self.assertEqual(count_success, 1)
|
||||||
|
self.assertTrue('Ignoring malformed line 2 in import file.' in error_messages)
|
||||||
|
|
||||||
|
def test_wrong_encoding(self):
|
||||||
|
csv_file = StringIO.StringIO()
|
||||||
|
text = u'Müller'.encode('iso-8859-15')
|
||||||
|
csv_file.write(text)
|
||||||
|
csv_file.seek(0)
|
||||||
|
count_success, error_messages, warning_messages = import_motions(csv_file=csv_file, default_submitter=self.normal_user.person_id)
|
||||||
|
self.assertEqual(count_success, 0)
|
||||||
|
self.assertTrue('Encoding error in import file. Ensure using UTF-8.' in error_messages)
|
||||||
|
Loading…
Reference in New Issue
Block a user