2012-08-13 13:42:19 +00:00
|
|
|
# -*- encoding: utf-8 -*-
|
2016-02-19 04:37:51 +00:00
|
|
|
|
|
|
|
import csv
|
|
|
|
import io
|
2012-08-13 13:42:19 +00:00
|
|
|
import unittest2
|
2016-02-19 04:37:51 +00:00
|
|
|
|
2012-08-10 07:35:13 +00:00
|
|
|
from openerp.tests.common import TransactionCase
|
|
|
|
|
2012-08-13 13:42:19 +00:00
|
|
|
from .. import models
|
|
|
|
|
2014-07-06 14:44:26 +00:00
|
|
|
ID_FIELD = {
|
|
|
|
'id': 'id',
|
|
|
|
'name': 'id',
|
|
|
|
'string': "External ID",
|
|
|
|
'required': False,
|
|
|
|
'fields': [],
|
|
|
|
}
|
|
|
|
|
2012-08-10 07:35:13 +00:00
|
|
|
def make_field(name='value', string='unknown', required=False, fields=[]):
|
|
|
|
return [
|
|
|
|
ID_FIELD,
|
|
|
|
{'id': name, 'name': name, 'string': string, 'required': required, 'fields': fields},
|
|
|
|
]
|
|
|
|
|
2014-07-06 14:44:26 +00:00
|
|
|
def sorted_fields(fields):
|
|
|
|
""" recursively sort field lists to ease comparison """
|
|
|
|
recursed = [dict(field, fields=sorted_fields(field['fields'])) for field in fields]
|
|
|
|
return sorted(recursed, key=lambda field: field['id'])
|
|
|
|
|
|
|
|
class BaseImportCase(TransactionCase):
|
|
|
|
def assertEqualFields(self, fields1, fields2):
|
|
|
|
self.assertEqual(sorted_fields(fields1), sorted_fields(fields2))
|
|
|
|
|
|
|
|
class test_basic_fields(BaseImportCase):
|
2012-08-10 07:35:13 +00:00
|
|
|
def get_fields(self, field):
|
|
|
|
return self.registry('base_import.import')\
|
|
|
|
.get_fields(self.cr, self.uid, 'base_import.tests.models.' + field)
|
|
|
|
|
|
|
|
def test_base(self):
|
|
|
|
""" A basic field is not required """
|
2014-07-06 14:44:26 +00:00
|
|
|
self.assertEqualFields(self.get_fields('char'), make_field())
|
2012-08-10 07:35:13 +00:00
|
|
|
|
|
|
|
def test_required(self):
|
|
|
|
""" Required fields should be flagged (so they can be fill-required) """
|
2014-07-06 14:44:26 +00:00
|
|
|
self.assertEqualFields(self.get_fields('char.required'), make_field(required=True))
|
2012-08-10 07:35:13 +00:00
|
|
|
|
|
|
|
def test_readonly(self):
|
|
|
|
""" Readonly fields should be filtered out"""
|
[FIX] models: display_name and name_get mismatch
- display_name uses name_get and not the other way around:
name_get should not call _compute_display_name, _compute_display_name should call name_get.
The previous behaviour was not backward-compatible with the old api.
All the models redefining name_get would have 2 different behaviors between name_get and display_name.
- Do not set an inverse function to display_name:
In most cases, writing on display_name writes on _rec_name (if any, not mandatory).
If the display_name computation is redefined, we need to redefine as well the inverse method to avoid unexpected behaviour
This required to also modify tests in base_import as readonly fields are avoided.
- Remove search method on display_name:
For the same reason as for the first point, it could be good that searching on display_name use name_search (and not the other way around).
However doing this would be very inefficiant (need to do the search, without limit, extract the ids of the name_get result just to generate
a subdomain ('id', 'in', [...]). As in most cases it would anyway mean to search on the _rec_name it's better to directly do so.
- Changing label to avoid mismatch:
In view displaying the list of fields or when a match is made on the label of a field (e.g. when importing csv file,
matching is made on both label and technical name), the fact that display_name field has '
Calling it 'Display Name' will avoid most errors.
- remove display_name definition from website_forum_doc,ir_model:
These fields are doing the same thing as the display_name of the new api, we can remove them.
We need to keep the one for res.partner as it's a stored field.
2014-07-16 08:35:52 +00:00
|
|
|
self.assertEqualFields(self.get_fields('char.readonly'), [ID_FIELD])
|
2012-08-10 07:35:13 +00:00
|
|
|
|
|
|
|
def test_readonly_states(self):
|
|
|
|
""" Readonly fields with states should not be filtered out"""
|
2014-07-06 14:44:26 +00:00
|
|
|
self.assertEqualFields(self.get_fields('char.states'), make_field())
|
2012-08-10 07:35:13 +00:00
|
|
|
|
|
|
|
def test_readonly_states_noreadonly(self):
|
|
|
|
""" Readonly fields with states having nothing to do with
|
|
|
|
readonly should still be filtered out"""
|
[FIX] models: display_name and name_get mismatch
- display_name uses name_get and not the other way around:
name_get should not call _compute_display_name, _compute_display_name should call name_get.
The previous behaviour was not backward-compatible with the old api.
All the models redefining name_get would have 2 different behaviors between name_get and display_name.
- Do not set an inverse function to display_name:
In most cases, writing on display_name writes on _rec_name (if any, not mandatory).
If the display_name computation is redefined, we need to redefine as well the inverse method to avoid unexpected behaviour
This required to also modify tests in base_import as readonly fields are avoided.
- Remove search method on display_name:
For the same reason as for the first point, it could be good that searching on display_name use name_search (and not the other way around).
However doing this would be very inefficiant (need to do the search, without limit, extract the ids of the name_get result just to generate
a subdomain ('id', 'in', [...]). As in most cases it would anyway mean to search on the _rec_name it's better to directly do so.
- Changing label to avoid mismatch:
In view displaying the list of fields or when a match is made on the label of a field (e.g. when importing csv file,
matching is made on both label and technical name), the fact that display_name field has '
Calling it 'Display Name' will avoid most errors.
- remove display_name definition from website_forum_doc,ir_model:
These fields are doing the same thing as the display_name of the new api, we can remove them.
We need to keep the one for res.partner as it's a stored field.
2014-07-16 08:35:52 +00:00
|
|
|
self.assertEqualFields(self.get_fields('char.noreadonly'), [ID_FIELD])
|
2012-08-10 07:35:13 +00:00
|
|
|
|
|
|
|
def test_readonly_states_stillreadonly(self):
|
|
|
|
""" Readonly fields with readonly states leaving them readonly
|
|
|
|
always... filtered out"""
|
[FIX] models: display_name and name_get mismatch
- display_name uses name_get and not the other way around:
name_get should not call _compute_display_name, _compute_display_name should call name_get.
The previous behaviour was not backward-compatible with the old api.
All the models redefining name_get would have 2 different behaviors between name_get and display_name.
- Do not set an inverse function to display_name:
In most cases, writing on display_name writes on _rec_name (if any, not mandatory).
If the display_name computation is redefined, we need to redefine as well the inverse method to avoid unexpected behaviour
This required to also modify tests in base_import as readonly fields are avoided.
- Remove search method on display_name:
For the same reason as for the first point, it could be good that searching on display_name use name_search (and not the other way around).
However doing this would be very inefficiant (need to do the search, without limit, extract the ids of the name_get result just to generate
a subdomain ('id', 'in', [...]). As in most cases it would anyway mean to search on the _rec_name it's better to directly do so.
- Changing label to avoid mismatch:
In view displaying the list of fields or when a match is made on the label of a field (e.g. when importing csv file,
matching is made on both label and technical name), the fact that display_name field has '
Calling it 'Display Name' will avoid most errors.
- remove display_name definition from website_forum_doc,ir_model:
These fields are doing the same thing as the display_name of the new api, we can remove them.
We need to keep the one for res.partner as it's a stored field.
2014-07-16 08:35:52 +00:00
|
|
|
self.assertEqualFields(self.get_fields('char.stillreadonly'), [ID_FIELD])
|
2012-08-10 07:35:13 +00:00
|
|
|
|
|
|
|
def test_m2o(self):
|
|
|
|
""" M2O fields should allow import of themselves (name_get),
|
|
|
|
their id and their xid"""
|
2014-07-06 14:44:26 +00:00
|
|
|
self.assertEqualFields(self.get_fields('m2o'), make_field(fields=[
|
2012-08-10 07:35:13 +00:00
|
|
|
{'id': 'value', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []},
|
|
|
|
{'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': False, 'fields': []},
|
|
|
|
]))
|
|
|
|
|
|
|
|
def test_m2o_required(self):
|
|
|
|
""" If an m2o field is required, its three sub-fields are
|
|
|
|
required as well (the client has to handle that: requiredness
|
|
|
|
is id-based)
|
|
|
|
"""
|
2014-07-06 14:44:26 +00:00
|
|
|
self.assertEqualFields(self.get_fields('m2o.required'), make_field(required=True, fields=[
|
2012-08-10 07:35:13 +00:00
|
|
|
{'id': 'value', 'name': 'id', 'string': 'External ID', 'required': True, 'fields': []},
|
|
|
|
{'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': True, 'fields': []},
|
|
|
|
]))
|
|
|
|
|
2014-07-06 14:44:26 +00:00
|
|
|
class test_o2m(BaseImportCase):
|
2012-08-10 07:35:13 +00:00
|
|
|
def get_fields(self, field):
|
|
|
|
return self.registry('base_import.import')\
|
|
|
|
.get_fields(self.cr, self.uid, 'base_import.tests.models.' + field)
|
|
|
|
|
|
|
|
def test_shallow(self):
|
2014-07-06 14:44:26 +00:00
|
|
|
self.assertEqualFields(self.get_fields('o2m'), make_field(fields=[
|
|
|
|
ID_FIELD,
|
2012-08-10 07:35:13 +00:00
|
|
|
# FIXME: should reverse field be ignored?
|
|
|
|
{'id': 'parent_id', 'name': 'parent_id', 'string': 'unknown', 'required': False, 'fields': [
|
|
|
|
{'id': 'parent_id', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []},
|
|
|
|
{'id': 'parent_id', 'name': '.id', 'string': 'Database ID', 'required': False, 'fields': []},
|
|
|
|
]},
|
|
|
|
{'id': 'value', 'name': 'value', 'string': 'unknown', 'required': False, 'fields': []},
|
|
|
|
]))
|
2012-08-13 13:42:19 +00:00
|
|
|
|
|
|
|
class test_match_headers_single(TransactionCase):
|
|
|
|
def test_match_by_name(self):
|
|
|
|
match = self.registry('base_import.import')._match_header(
|
|
|
|
'f0', [{'name': 'f0'}], {})
|
|
|
|
|
|
|
|
self.assertEqual(match, [{'name': 'f0'}])
|
|
|
|
|
|
|
|
def test_match_by_string(self):
|
|
|
|
match = self.registry('base_import.import')._match_header(
|
|
|
|
'some field', [{'name': 'bob', 'string': "Some Field"}], {})
|
|
|
|
|
|
|
|
self.assertEqual(match, [{'name': 'bob', 'string': "Some Field"}])
|
|
|
|
|
|
|
|
def test_nomatch(self):
|
|
|
|
match = self.registry('base_import.import')._match_header(
|
|
|
|
'should not be', [{'name': 'bob', 'string': "wheee"}], {})
|
|
|
|
|
|
|
|
self.assertEqual(match, [])
|
|
|
|
|
|
|
|
def test_recursive_match(self):
|
|
|
|
f = {
|
|
|
|
'name': 'f0',
|
|
|
|
'string': "My Field",
|
|
|
|
'fields': [
|
|
|
|
{'name': 'f0', 'string': "Sub field 0", 'fields': []},
|
|
|
|
{'name': 'f1', 'string': "Sub field 2", 'fields': []},
|
|
|
|
]
|
|
|
|
}
|
|
|
|
match = self.registry('base_import.import')._match_header(
|
|
|
|
'f0/f1', [f], {})
|
|
|
|
|
|
|
|
self.assertEqual(match, [f, f['fields'][1]])
|
|
|
|
|
|
|
|
def test_recursive_nomatch(self):
|
|
|
|
""" Match first level, fail to match second level
|
|
|
|
"""
|
|
|
|
f = {
|
|
|
|
'name': 'f0',
|
|
|
|
'string': "My Field",
|
|
|
|
'fields': [
|
|
|
|
{'name': 'f0', 'string': "Sub field 0", 'fields': []},
|
|
|
|
{'name': 'f1', 'string': "Sub field 2", 'fields': []},
|
|
|
|
]
|
|
|
|
}
|
|
|
|
match = self.registry('base_import.import')._match_header(
|
|
|
|
'f0/f2', [f], {})
|
|
|
|
|
|
|
|
self.assertEqual(match, [])
|
|
|
|
|
|
|
|
class test_match_headers_multiple(TransactionCase):
|
|
|
|
def test_noheaders(self):
|
2012-08-14 12:07:37 +00:00
|
|
|
self.assertEqual(
|
2012-08-13 13:42:19 +00:00
|
|
|
self.registry('base_import.import')._match_headers(
|
2012-08-14 12:07:37 +00:00
|
|
|
[], [], {}),
|
|
|
|
(None, None)
|
|
|
|
)
|
2012-08-13 13:42:19 +00:00
|
|
|
def test_nomatch(self):
|
|
|
|
self.assertEqual(
|
|
|
|
self.registry('base_import.import')._match_headers(
|
|
|
|
iter([
|
|
|
|
['foo', 'bar', 'baz', 'qux'],
|
|
|
|
['v1', 'v2', 'v3', 'v4'],
|
|
|
|
]),
|
|
|
|
[],
|
|
|
|
{'headers': True}),
|
2012-08-14 12:07:37 +00:00
|
|
|
(
|
|
|
|
['foo', 'bar', 'baz', 'qux'],
|
|
|
|
dict.fromkeys(range(4))
|
|
|
|
)
|
|
|
|
)
|
2012-08-13 13:42:19 +00:00
|
|
|
|
|
|
|
def test_mixed(self):
|
|
|
|
self.assertEqual(
|
|
|
|
self.registry('base_import.import')._match_headers(
|
|
|
|
iter(['foo bar baz qux/corge'.split()]),
|
|
|
|
[
|
|
|
|
{'name': 'bar', 'string': 'Bar'},
|
|
|
|
{'name': 'bob', 'string': 'Baz'},
|
|
|
|
{'name': 'qux', 'string': 'Qux', 'fields': [
|
|
|
|
{'name': 'corge', 'fields': []},
|
|
|
|
]}
|
|
|
|
],
|
|
|
|
{'headers': True}),
|
2012-08-14 12:07:37 +00:00
|
|
|
(['foo', 'bar', 'baz', 'qux/corge'], {
|
2012-08-13 13:42:19 +00:00
|
|
|
0: None,
|
|
|
|
1: ['bar'],
|
|
|
|
2: ['bob'],
|
|
|
|
3: ['qux', 'corge'],
|
|
|
|
})
|
2012-08-14 12:07:37 +00:00
|
|
|
)
|
2012-08-13 13:42:19 +00:00
|
|
|
|
|
|
|
class test_preview(TransactionCase):
|
|
|
|
def make_import(self):
|
|
|
|
Import = self.registry('base_import.import')
|
|
|
|
id = Import.create(self.cr, self.uid, {
|
|
|
|
'res_model': 'res.users',
|
2012-08-30 10:32:15 +00:00
|
|
|
'file': u"로그인,언어\nbob,1\n".encode('euc_kr'),
|
2012-08-13 13:42:19 +00:00
|
|
|
})
|
|
|
|
return Import, id
|
|
|
|
|
|
|
|
def test_encoding(self):
|
|
|
|
Import, id = self.make_import()
|
|
|
|
result = Import.parse_preview(self.cr, self.uid, id, {
|
2012-08-30 14:12:26 +00:00
|
|
|
'quoting': '"',
|
2012-08-13 13:42:19 +00:00
|
|
|
'separator': ',',
|
|
|
|
})
|
|
|
|
self.assertTrue('error' in result)
|
|
|
|
|
|
|
|
def test_csv_errors(self):
|
|
|
|
Import, id = self.make_import()
|
|
|
|
|
|
|
|
result = Import.parse_preview(self.cr, self.uid, id, {
|
2012-08-30 14:12:26 +00:00
|
|
|
'quoting': 'foo',
|
2012-08-13 13:42:19 +00:00
|
|
|
'separator': ',',
|
|
|
|
'encoding': 'euc_kr',
|
|
|
|
})
|
|
|
|
self.assertTrue('error' in result)
|
|
|
|
|
|
|
|
result = Import.parse_preview(self.cr, self.uid, id, {
|
2012-08-30 14:12:26 +00:00
|
|
|
'quoting': '"',
|
2012-08-13 13:42:19 +00:00
|
|
|
'separator': 'bob',
|
|
|
|
'encoding': 'euc_kr',
|
|
|
|
})
|
|
|
|
self.assertTrue('error' in result)
|
|
|
|
|
|
|
|
def test_success(self):
|
|
|
|
Import = self.registry('base_import.import')
|
|
|
|
id = Import.create(self.cr, self.uid, {
|
|
|
|
'res_model': 'base_import.tests.models.preview',
|
2012-08-30 10:32:15 +00:00
|
|
|
'file': 'name,Some Value,Counter\n'
|
2012-08-13 13:42:19 +00:00
|
|
|
'foo,1,2\n'
|
|
|
|
'bar,3,4\n'
|
2012-08-30 10:32:15 +00:00
|
|
|
'qux,5,6\n'
|
2012-08-13 13:42:19 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
result = Import.parse_preview(self.cr, self.uid, id, {
|
2012-08-30 14:12:26 +00:00
|
|
|
'quoting': '"',
|
2012-08-13 13:42:19 +00:00
|
|
|
'separator': ',',
|
|
|
|
'headers': True,
|
|
|
|
})
|
|
|
|
|
2012-08-14 12:07:37 +00:00
|
|
|
self.assertEqual(result['matches'], {0: ['name'], 1: ['somevalue'], 2: None})
|
|
|
|
self.assertEqual(result['headers'], ['name', 'Some Value', 'Counter'])
|
2012-08-13 13:42:19 +00:00
|
|
|
# Order depends on iteration order of fields_get
|
2012-08-14 12:07:37 +00:00
|
|
|
self.assertItemsEqual(result['fields'], [
|
2014-07-06 14:44:26 +00:00
|
|
|
ID_FIELD,
|
2012-08-14 12:07:37 +00:00
|
|
|
{'id': 'name', 'name': 'name', 'string': 'Name', 'required':False, 'fields': []},
|
|
|
|
{'id': 'somevalue', 'name': 'somevalue', 'string': 'Some Value', 'required':True, 'fields': []},
|
|
|
|
{'id': 'othervalue', 'name': 'othervalue', 'string': 'Other Variable', 'required':False, 'fields': []},
|
|
|
|
])
|
|
|
|
self.assertEqual(result['preview'], [
|
|
|
|
['foo', '1', '2'],
|
|
|
|
['bar', '3', '4'],
|
|
|
|
['qux', '5', '6'],
|
|
|
|
])
|
2012-08-13 13:42:19 +00:00
|
|
|
# Ensure we only have the response fields we expect
|
2012-08-14 12:07:37 +00:00
|
|
|
self.assertItemsEqual(result.keys(), ['matches', 'headers', 'fields', 'preview'])
|
2012-08-14 09:38:13 +00:00
|
|
|
|
|
|
|
class test_convert_import_data(TransactionCase):
|
|
|
|
""" Tests conversion of base_import.import input into data which
|
|
|
|
can be fed to Model.import_data
|
|
|
|
"""
|
|
|
|
def test_all(self):
|
|
|
|
Import = self.registry('base_import.import')
|
|
|
|
id = Import.create(self.cr, self.uid, {
|
|
|
|
'res_model': 'base_import.tests.models.preview',
|
2012-08-30 10:32:15 +00:00
|
|
|
'file': 'name,Some Value,Counter\n'
|
2012-08-14 09:38:13 +00:00
|
|
|
'foo,1,2\n'
|
|
|
|
'bar,3,4\n'
|
2012-08-30 10:32:15 +00:00
|
|
|
'qux,5,6\n'
|
2012-08-14 09:38:13 +00:00
|
|
|
})
|
|
|
|
record = Import.browse(self.cr, self.uid, id)
|
|
|
|
data, fields = Import._convert_import_data(
|
|
|
|
record, ['name', 'somevalue', 'othervalue'],
|
2012-08-30 14:12:26 +00:00
|
|
|
{'quoting': '"', 'separator': ',', 'headers': True,})
|
2012-08-14 09:38:13 +00:00
|
|
|
|
|
|
|
self.assertItemsEqual(fields, ['name', 'somevalue', 'othervalue'])
|
|
|
|
self.assertItemsEqual(data, [
|
|
|
|
('foo', '1', '2'),
|
|
|
|
('bar', '3', '4'),
|
|
|
|
('qux', '5', '6'),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_filtered(self):
|
|
|
|
""" If ``False`` is provided as field mapping for a column,
|
|
|
|
that column should be removed from importable data
|
|
|
|
"""
|
|
|
|
Import = self.registry('base_import.import')
|
|
|
|
id = Import.create(self.cr, self.uid, {
|
|
|
|
'res_model': 'base_import.tests.models.preview',
|
2012-08-30 10:32:15 +00:00
|
|
|
'file': 'name,Some Value,Counter\n'
|
2012-08-14 09:38:13 +00:00
|
|
|
'foo,1,2\n'
|
|
|
|
'bar,3,4\n'
|
2012-08-30 10:32:15 +00:00
|
|
|
'qux,5,6\n'
|
2012-08-14 09:38:13 +00:00
|
|
|
})
|
|
|
|
record = Import.browse(self.cr, self.uid, id)
|
|
|
|
data, fields = Import._convert_import_data(
|
|
|
|
record, ['name', False, 'othervalue'],
|
2012-08-30 14:12:26 +00:00
|
|
|
{'quoting': '"', 'separator': ',', 'headers': True,})
|
2012-08-14 09:38:13 +00:00
|
|
|
|
|
|
|
self.assertItemsEqual(fields, ['name', 'othervalue'])
|
|
|
|
self.assertItemsEqual(data, [
|
|
|
|
('foo', '2'),
|
|
|
|
('bar', '4'),
|
|
|
|
('qux', '6'),
|
|
|
|
])
|
|
|
|
|
|
|
|
def test_norow(self):
|
|
|
|
""" If a row is composed only of empty values (due to having
|
|
|
|
filtered out non-empty values from it), it should be removed
|
|
|
|
"""
|
|
|
|
Import = self.registry('base_import.import')
|
|
|
|
id = Import.create(self.cr, self.uid, {
|
|
|
|
'res_model': 'base_import.tests.models.preview',
|
2012-08-30 10:32:15 +00:00
|
|
|
'file': 'name,Some Value,Counter\n'
|
2012-08-14 09:38:13 +00:00
|
|
|
'foo,1,2\n'
|
|
|
|
',3,\n'
|
2012-08-30 10:32:15 +00:00
|
|
|
',5,6\n'
|
2012-08-14 09:38:13 +00:00
|
|
|
})
|
|
|
|
record = Import.browse(self.cr, self.uid, id)
|
|
|
|
data, fields = Import._convert_import_data(
|
|
|
|
record, ['name', False, 'othervalue'],
|
2012-08-30 14:12:26 +00:00
|
|
|
{'quoting': '"', 'separator': ',', 'headers': True,})
|
2012-08-14 09:38:13 +00:00
|
|
|
|
|
|
|
self.assertItemsEqual(fields, ['name', 'othervalue'])
|
|
|
|
self.assertItemsEqual(data, [
|
|
|
|
('foo', '2'),
|
|
|
|
('', '6'),
|
|
|
|
])
|
2012-08-14 14:29:33 +00:00
|
|
|
|
2015-04-01 10:59:20 +00:00
|
|
|
def test_empty_rows(self):
|
|
|
|
Import = self.registry('base_import.import')
|
|
|
|
id = Import.create(self.cr, self.uid, {
|
|
|
|
'res_model': 'base_import.tests.models.preview',
|
|
|
|
'file': 'name,Some Value\n'
|
|
|
|
'foo,1\n'
|
|
|
|
'\n'
|
|
|
|
'bar,2\n'
|
|
|
|
' \n'
|
|
|
|
'\t \n'
|
|
|
|
})
|
|
|
|
record = Import.browse(self.cr, self.uid, id)
|
|
|
|
data, fields = Import._convert_import_data(
|
|
|
|
record, ['name', 'somevalue'],
|
|
|
|
{'quoting': '"', 'separator': ',', 'headers': True,})
|
|
|
|
|
|
|
|
self.assertItemsEqual(fields, ['name', 'somevalue'])
|
|
|
|
self.assertItemsEqual(data, [
|
|
|
|
('foo', '1'),
|
|
|
|
('bar', '2'),
|
|
|
|
])
|
|
|
|
|
2012-08-14 14:29:33 +00:00
|
|
|
def test_nofield(self):
|
|
|
|
Import = self.registry('base_import.import')
|
|
|
|
|
|
|
|
id = Import.create(self.cr, self.uid, {
|
|
|
|
'res_model': 'base_import.tests.models.preview',
|
2012-08-30 10:32:15 +00:00
|
|
|
'file': 'name,Some Value,Counter\n'
|
|
|
|
'foo,1,2\n'
|
2012-08-14 14:29:33 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
record = Import.browse(self.cr, self.uid, id)
|
|
|
|
self.assertRaises(
|
|
|
|
ValueError,
|
|
|
|
Import._convert_import_data,
|
|
|
|
record, [],
|
2012-08-30 14:12:26 +00:00
|
|
|
{'quoting': '"', 'separator': ',', 'headers': True,})
|
2012-08-14 14:29:33 +00:00
|
|
|
|
|
|
|
def test_falsefields(self):
|
|
|
|
Import = self.registry('base_import.import')
|
|
|
|
|
|
|
|
id = Import.create(self.cr, self.uid, {
|
|
|
|
'res_model': 'base_import.tests.models.preview',
|
2012-08-30 10:32:15 +00:00
|
|
|
'file': 'name,Some Value,Counter\n'
|
|
|
|
'foo,1,2\n'
|
2012-08-14 14:29:33 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
record = Import.browse(self.cr, self.uid, id)
|
|
|
|
self.assertRaises(
|
|
|
|
ValueError,
|
|
|
|
Import._convert_import_data,
|
|
|
|
record, [False, False, False],
|
2012-08-30 14:12:26 +00:00
|
|
|
{'quoting': '"', 'separator': ',', 'headers': True,})
|
2015-02-27 12:19:02 +00:00
|
|
|
|
2016-02-19 04:37:51 +00:00
|
|
|
def test_newline_import(self):
|
|
|
|
"""
|
|
|
|
Ensure importing keep newlines
|
|
|
|
"""
|
|
|
|
Import = self.registry('base_import.import')
|
|
|
|
output = io.BytesIO()
|
|
|
|
writer = csv.writer(output, quoting=csv.QUOTE_ALL)
|
|
|
|
|
|
|
|
data_row = ("\tfoo\n\tbar", " \"hello\" \n\n 'world' ")
|
|
|
|
|
|
|
|
writer.writerow(["name", "Some Value"])
|
|
|
|
writer.writerow(data_row)
|
|
|
|
|
|
|
|
|
|
|
|
id = Import.create(self.cr, self.uid, {
|
|
|
|
'res_model': 'base_import.tests.models.preview',
|
|
|
|
'file': output.getvalue()
|
|
|
|
})
|
|
|
|
record = Import.browse(self.cr, self.uid, id)
|
|
|
|
data, _ = Import._convert_import_data(
|
|
|
|
record, ['name', 'somevalue'],
|
|
|
|
{'quoting': '"', 'separator': ',', 'headers': True,})
|
|
|
|
|
|
|
|
self.assertItemsEqual(data, [data_row])
|
|
|
|
|
2015-02-27 12:19:02 +00:00
|
|
|
class test_failures(TransactionCase):
|
|
|
|
def test_big_attachments(self):
|
|
|
|
"""
|
|
|
|
Ensure big fields (e.g. b64-encoded image data) can be imported and
|
|
|
|
we're not hitting limits of the default CSV parser config
|
|
|
|
"""
|
|
|
|
import csv, cStringIO
|
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
im = Image.new('RGB', (1920, 1080))
|
|
|
|
fout = cStringIO.StringIO()
|
|
|
|
|
|
|
|
writer = csv.writer(fout, dialect=None)
|
|
|
|
writer.writerows([
|
|
|
|
['name', 'db_datas'],
|
|
|
|
['foo', im.tobytes().encode('base64')]
|
|
|
|
])
|
|
|
|
|
|
|
|
Import = self.env['base_import.import']
|
|
|
|
imp = Import.create({
|
|
|
|
'res_model': 'ir.attachment',
|
|
|
|
'file': fout.getvalue()
|
|
|
|
})
|
|
|
|
[results] = imp.do(
|
|
|
|
['name', 'db_datas'],
|
|
|
|
{'headers': True, 'separator': ',', 'quoting': '"'})
|
|
|
|
self.assertFalse(
|
|
|
|
results, "results should be empty on successful import")
|