PyOdoo 0.5.9__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/PKG-INFO +3 -1
  2. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/PyOdoo.egg-info/PKG-INFO +3 -1
  3. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/PyOdoo.egg-info/SOURCES.txt +5 -1
  4. PyOdoo-0.6.0/PyOdoo.egg-info/requires.txt +1 -0
  5. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/README.md +2 -0
  6. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/constants.py +1 -1
  7. PyOdoo-0.6.0/pyodoo/samples/sql_excel_query.py +81 -0
  8. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/v12/__init__.py +1 -0
  9. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/v12/model.py +14 -8
  10. PyOdoo-0.6.0/pyodoo/v12/sql_excel_query.py +310 -0
  11. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/setup.cfg +2 -0
  12. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/tests/test_contacts.py +26 -5
  13. PyOdoo-0.6.0/tests/test_sql_excel_query.py +233 -0
  14. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/LICENSE +0 -0
  15. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/PyOdoo.egg-info/dependency_links.txt +0 -0
  16. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/PyOdoo.egg-info/top_level.txt +0 -0
  17. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/__init__.py +0 -0
  18. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/active_status_choice.py +0 -0
  19. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/boolean_operator.py +0 -0
  20. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/compare_type.py +0 -0
  21. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/filter.py +0 -0
  22. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/message_subtype.py +0 -0
  23. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/samples/__init__.py +0 -0
  24. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/samples/awaitable.py +0 -0
  25. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/samples/contacts.py +0 -0
  26. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/samples/contacts_async.py +0 -0
  27. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyodoo/v12/api.py +0 -0
  28. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/pyproject.toml +0 -0
  29. {PyOdoo-0.5.9 → PyOdoo-0.6.0}/tests/test_compare_types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyOdoo
3
- Version: 0.5.9
3
+ Version: 0.6.0
4
4
  Summary: API for Odoo
5
5
  Home-page: http://www.muflone.com/pyodoo/
6
6
  Author: Fabio Castelli
@@ -44,6 +44,8 @@ its XML RPC API.
44
44
  # System Requirements
45
45
 
46
46
  * Python 3.x
47
+ * xlrd 2.0.x (https://pypi.org/project/xlrd/)
48
+ * requests 2.31.x (https://pypi.org/project/requests/)
47
49
 
48
50
  # Usage
49
51
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyOdoo
3
- Version: 0.5.9
3
+ Version: 0.6.0
4
4
  Summary: API for Odoo
5
5
  Home-page: http://www.muflone.com/pyodoo/
6
6
  Author: Fabio Castelli
@@ -44,6 +44,8 @@ its XML RPC API.
44
44
  # System Requirements
45
45
 
46
46
  * Python 3.x
47
+ * xlrd 2.0.x (https://pypi.org/project/xlrd/)
48
+ * requests 2.31.x (https://pypi.org/project/requests/)
47
49
 
48
50
  # Usage
49
51
 
@@ -5,6 +5,7 @@ setup.cfg
5
5
  PyOdoo.egg-info/PKG-INFO
6
6
  PyOdoo.egg-info/SOURCES.txt
7
7
  PyOdoo.egg-info/dependency_links.txt
8
+ PyOdoo.egg-info/requires.txt
8
9
  PyOdoo.egg-info/top_level.txt
9
10
  pyodoo/__init__.py
10
11
  pyodoo/active_status_choice.py
@@ -17,8 +18,11 @@ pyodoo/samples/__init__.py
17
18
  pyodoo/samples/awaitable.py
18
19
  pyodoo/samples/contacts.py
19
20
  pyodoo/samples/contacts_async.py
21
+ pyodoo/samples/sql_excel_query.py
20
22
  pyodoo/v12/__init__.py
21
23
  pyodoo/v12/api.py
22
24
  pyodoo/v12/model.py
25
+ pyodoo/v12/sql_excel_query.py
23
26
  tests/test_compare_types.py
24
- tests/test_contacts.py
27
+ tests/test_contacts.py
28
+ tests/test_sql_excel_query.py
@@ -0,0 +1 @@
1
+ xlrd==2.0.1
@@ -24,6 +24,8 @@ its XML RPC API.
24
24
  # System Requirements
25
25
 
26
26
  * Python 3.x
27
+ * xlrd 2.0.x (https://pypi.org/project/xlrd/)
28
+ * requests 2.31.x (https://pypi.org/project/requests/)
27
29
 
28
30
  # Usage
29
31
 
@@ -19,7 +19,7 @@
19
19
  ##
20
20
 
21
21
  APP_NAME = 'PyOdoo'
22
- APP_VERSION = '0.5.9'
22
+ APP_VERSION = '0.6.0'
23
23
  APP_DESCRIPTION = 'API for Odoo'
24
24
  APP_DOMAIN = 'pyodoo'
25
25
  APP_ID = f'{APP_DOMAIN}.muflone.com'
@@ -0,0 +1,81 @@
1
+ ##
2
+ # Project: PyOdoo
3
+ # Description: API for Odoo
4
+ # Author: Fabio Castelli (Muflone) <muflone@muflone.com>
5
+ # Copyright: 2021-2023 Fabio Castelli
6
+ # License: GPL-3+
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
19
+ ##
20
+
21
+ import os
22
+
23
+ from pyodoo.constants import APP_NAME, APP_VERSION
24
+ from pyodoo.v12 import SqlExcelQuery
25
+
26
+
27
+ # Check SQL Excel Query availability
28
+ print(SqlExcelQuery.is_available(endpoint=os.environ['ODOO_ENDPOINT'],
29
+ database=os.environ['ODOO_DATABASE'],
30
+ username=os.environ['ODOO_USERNAME'],
31
+ password=os.environ['ODOO_PASSWORD'],
32
+ language=None))
33
+ # Instance SQL Excel Query object
34
+ query = SqlExcelQuery(name='test query 1',
35
+ category=f'{APP_NAME} {APP_VERSION}',
36
+ endpoint=os.environ['ODOO_ENDPOINT'],
37
+ database=os.environ['ODOO_DATABASE'],
38
+ username=os.environ['ODOO_USERNAME'],
39
+ password=os.environ['ODOO_PASSWORD'],
40
+ language=None)
41
+ # Change default language
42
+ query.language = 'en_GB'
43
+
44
+ # Get Query ID
45
+ print(query.id)
46
+
47
+ # Set Query SQL text
48
+ print(query.set_sql(text='SELECT\n *\nFROM res_partner\nLIMIT 100'))
49
+
50
+ # Set and get Query state
51
+ query.set_draft()
52
+ print(query.get_state())
53
+ query.validate()
54
+ print(query.get_state())
55
+
56
+ # Set Query active status
57
+ print(query.set_active(active=False))
58
+ print(query.set_active(active=True))
59
+
60
+ # Execute the SQL query
61
+ query.execute()
62
+
63
+ # Get Excel file
64
+ data = query.get_file()
65
+
66
+ # Process the Excel file and extract its data
67
+ data = query.get_data()
68
+ print(data)
69
+
70
+ # Remove Excel file from SQL Excel Query
71
+ query.clear()
72
+
73
+ # Add query tags
74
+ query.add_tag(tag_id=4)
75
+
76
+ # Get the query tags
77
+ data = query.get_tags()
78
+ print(data)
79
+
80
+ # Delete SQL Excel Query object
81
+ print(query.delete())
@@ -20,3 +20,4 @@
20
20
 
21
21
  from .api import Api # noqa: F401
22
22
  from .model import Model # noqa: F401
23
+ from .sql_excel_query import SqlExcelQuery # noqa: F401
@@ -92,20 +92,26 @@ class Model(object):
92
92
 
93
93
  def get_model(self,
94
94
  model_name: str,
95
- authenticate: bool = False) -> 'Model':
95
+ authenticate: bool = False,
96
+ use_existing_uid: bool = False) -> 'Model':
96
97
  """
97
98
  Get a Model object for another model name
98
99
  :param model_name: Model name
99
100
  :param authenticate: Automatically authenticate user
101
+ :param use_existing_uid: Use the existing UID if not authenticated
100
102
  :return: Model object
101
103
  """
102
- return Model(model_name=model_name,
103
- endpoint=self.api.endpoint,
104
- database=self.api.database,
105
- username=self.api.username,
106
- password=self.api.password,
107
- language=self.api.language,
108
- authenticate=authenticate)
104
+ model = Model(model_name=model_name,
105
+ endpoint=self.api.endpoint,
106
+ database=self.api.database,
107
+ username=self.api.username,
108
+ password=self.api.password,
109
+ language=self.api.language,
110
+ authenticate=authenticate)
111
+ if not authenticate and use_existing_uid:
112
+ # Use the existing UID
113
+ model.api.uid = self.api.uid
114
+ return model
109
115
 
110
116
  def get_model_data_reference(self,
111
117
  module_name: str,
@@ -0,0 +1,310 @@
1
+ ##
2
+ # Project: PyOdoo
3
+ # Description: API for Odoo
4
+ # Author: Fabio Castelli (Muflone) <muflone@muflone.com>
5
+ # Copyright: 2021-2023 Fabio Castelli
6
+ # License: GPL-3+
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
19
+ ##
20
+
21
+ import base64
22
+ import io
23
+ from typing import Any, Optional
24
+
25
+ import pyodoo
26
+ from pyodoo.v12.model import Model
27
+
28
+ import xlrd
29
+
30
+
31
+ class SqlExcelQuery(object):
32
+ """
33
+ SQL Excel query to extract data from tables
34
+ """
35
+ def __init__(self,
36
+ name: str,
37
+ category: str,
38
+ endpoint: str,
39
+ database: str,
40
+ username: str,
41
+ password: str,
42
+ language: str):
43
+ # Model object
44
+ self._query_name = name
45
+ self.model = Model(model_name='sql.excel.pdf',
46
+ endpoint=endpoint,
47
+ database=database,
48
+ username=username,
49
+ password=password,
50
+ language=language,
51
+ authenticate=True)
52
+ # Get or create the category ID
53
+ model_category = self.model.get_model(model_name='sql.category',
54
+ authenticate=False,
55
+ use_existing_uid=True)
56
+ results = model_category.search(
57
+ filters=[pyodoo.Filter(field='name',
58
+ compare_type=pyodoo.CompareType.EQUAL,
59
+ value=category)],
60
+ limit=1)
61
+ if results:
62
+ category_id = results[0]
63
+ else:
64
+ category_id = model_category.create(values={'name': category})
65
+ # Get or create the query object
66
+ results = self.model.search(
67
+ filters=[pyodoo.Filter(field='name',
68
+ compare_type=pyodoo.CompareType.EQUAL,
69
+ value=self.name)],
70
+ is_active=pyodoo.ActiveStatusChoice.BOTH,
71
+ limit=1)
72
+ if results:
73
+ self._query_id = results[0]
74
+ else:
75
+ # Query object doesn't exist, create it right now
76
+ self._query_id = self.model.create(
77
+ values={'name': self.name,
78
+ 'category_id': category_id,
79
+ 'sql_query': ''})
80
+
81
+ @staticmethod
82
+ def is_available(endpoint: str,
83
+ database: str,
84
+ username: str,
85
+ password: str,
86
+ language: str) -> bool:
87
+ """
88
+ Check if the sql.excel.pdf model exists
89
+
90
+ :param endpoint: Odoo instance endpoint
91
+ :param database: Odoo database
92
+ :param username: Odoo username
93
+ :param password: User password
94
+ :param language: Language for localizations
95
+
96
+ :return: True if the model sql.excel.pdf exists
97
+ """
98
+ model = Model(model_name='ir.model',
99
+ endpoint=endpoint,
100
+ database=database,
101
+ username=username,
102
+ password=password,
103
+ language=language,
104
+ authenticate=True)
105
+ results = model.search(
106
+ filters=[pyodoo.Filter(field='model',
107
+ compare_type=pyodoo.CompareType.EQUAL,
108
+ value='sql.excel.pdf')],
109
+ limit=1)
110
+ return bool(results)
111
+
112
+ @property
113
+ def name(self) -> str:
114
+ """
115
+ Get the current query name
116
+
117
+ :return: Query name
118
+ """
119
+ return self._query_name
120
+
121
+ @property
122
+ def language(self) -> str:
123
+ """
124
+ Get the current default language
125
+
126
+ :return: Language code
127
+ """
128
+ return self.model.language
129
+
130
+ @language.setter
131
+ def language(self,
132
+ language: str):
133
+ """
134
+ Set the current default language
135
+
136
+ :param language: Language code to set
137
+ """
138
+ self.model.language = language
139
+
140
+ @property
141
+ def id(self) -> int:
142
+ """
143
+ Get the query object ID
144
+ :return: query object ID
145
+ """
146
+ return self._query_id
147
+
148
+ def get_sql(self) -> Optional[str]:
149
+ """
150
+ Get the query SQL text
151
+
152
+ :return: SQL text for the query
153
+ """
154
+ results = self.model.get(entity_id=self._query_id,
155
+ fields=('id',
156
+ 'sql_query'))
157
+ return results['sql_query'] if results else None
158
+
159
+ def set_sql(self, text: str) -> bool:
160
+ """
161
+ Set the query SQL text
162
+
163
+ :param text: SQL text for query
164
+ :return: True if the query was updated
165
+ """
166
+ return self.model.update(entity_id=self._query_id,
167
+ values={'sql_query': text})
168
+
169
+ def set_draft(self) -> None:
170
+ """
171
+ Set the query object as draft
172
+ """
173
+ self.model.execute(method_name='action_reset_draft',
174
+ args=[self._query_id],
175
+ kwargs={},
176
+ ignore_none_errors=True)
177
+
178
+ def validate(self) -> None:
179
+ """
180
+ Validate the query object
181
+ """
182
+ self.model.execute(method_name='action_validate',
183
+ args=[self._query_id],
184
+ kwargs={},
185
+ ignore_none_errors=False)
186
+
187
+ def get_state(self) -> Optional[str]:
188
+ """
189
+ Get the query object state
190
+
191
+ :return: the query object state
192
+ """
193
+ results = self.model.get(entity_id=self._query_id,
194
+ fields=('state', ))
195
+ return results['state'] if results else None
196
+
197
+ def get_active(self) -> Optional[str]:
198
+ """
199
+ Get the query object active state
200
+
201
+ :return: the query object active state
202
+ """
203
+ results = self.model.get(entity_id=self._query_id,
204
+ fields=('active', ))
205
+ return results['active'] if results else None
206
+
207
+ def set_active(self, active: bool) -> bool:
208
+ """
209
+ Set the query object as active/inactive
210
+
211
+ :param active: active status
212
+ :return: True if the query was updated
213
+ """
214
+ return self.model.update(entity_id=self._query_id,
215
+ values={'active': active})
216
+
217
+ def execute(self) -> None:
218
+ """
219
+ Execute the query to create the Excel file in the `file` field
220
+
221
+ :return: None
222
+ """
223
+ self.model.execute(method_name='print_xls_report',
224
+ args=[self._query_id],
225
+ kwargs={},
226
+ ignore_none_errors=True)
227
+ return None
228
+
229
+ def clear(self) -> None:
230
+ """
231
+ Clear the produced file
232
+ """
233
+ self.model.update(entity_id=self._query_id,
234
+ values={'file': False})
235
+
236
+ def delete(self) -> bool:
237
+ """
238
+ Delete the query object
239
+
240
+ :return: True if the query was removed
241
+ """
242
+ return self.model.delete(entity_id=self._query_id)
243
+
244
+ def get_file(self) -> Optional[str]:
245
+ """
246
+ Get the latest produced Excel file
247
+
248
+ :return: Excel binary file content
249
+ """
250
+ results = self.model.get(entity_id=self._query_id,
251
+ fields=('file', ))
252
+ if results and results['file']:
253
+ # Decode the base64 content
254
+ results = base64.b64decode(s=results['file'])
255
+ else:
256
+ results = None
257
+ return results
258
+
259
+ def get_data(self) -> Optional[list[dict[str, Any]]]:
260
+ """
261
+ Get the latest produced Excel file and extract its tabular data
262
+
263
+ :return: list of dictionaries with data
264
+ """
265
+ with io.BytesIO() as file:
266
+ file.write(self.get_file())
267
+ with xlrd.open_workbook(file_contents=file.getvalue()) as workbook:
268
+ worksheet = workbook.sheet_by_index(0)
269
+ rows = worksheet.get_rows()
270
+ # Skip the first three rows
271
+ next(rows)
272
+ next(rows)
273
+ next(rows)
274
+ # Get the column headers
275
+ headers = [item.value
276
+ for item in next(rows)]
277
+ # Get the data
278
+ results = []
279
+ for row in rows:
280
+ results.append(dict(zip(headers,
281
+ [item.value for item in row])))
282
+ return results
283
+
284
+ def add_tag(self, tag_id: int) -> bool:
285
+ """
286
+ Add an existing tag to the query
287
+
288
+ :param tag_id: Tag ID to add
289
+ :return: True if the tag was added
290
+ """
291
+ return self.model.many_to_many_add(entity_id=self._query_id,
292
+ field='tag_ids',
293
+ related_id=tag_id)
294
+
295
+ def get_tags(self) -> Optional[list[dict[str, Any]]]:
296
+ """
297
+ Get the query tags
298
+
299
+ :return: list with the tags
300
+ """
301
+ if results := self.model.get(entity_id=self._query_id,
302
+ fields=('id',
303
+ 'tag_ids')):
304
+ model = self.model.get_model(model_name='sql.tags',
305
+ authenticate=False,
306
+ use_existing_uid=True)
307
+ results = model.get_many(entity_ids=results['tag_ids'],
308
+ fields=('id',
309
+ 'name'))
310
+ return results or None
@@ -25,6 +25,8 @@ packages =
25
25
  pyodoo
26
26
  pyodoo.v12
27
27
  include_package_data = False
28
+ install_requires =
29
+ xlrd==2.0.1
28
30
 
29
31
  [options.package_data]
30
32
  pyodoo = samples/*
@@ -784,14 +784,35 @@ class TestCaseContacts(unittest.TestCase):
784
784
  self.assertIn('string', field_name)
785
785
  self.assertIn('type', field_name)
786
786
 
787
- def test_41_get_model(self) -> None:
787
+ def test_41_get_model_authenticate(self) -> None:
788
788
  """
789
789
  Get a new Model object
790
790
  """
791
791
  model_name = 'res.users'
792
- results = self.model.get_model(model_name=model_name,
793
- authenticate=False)
792
+ model = self.model.get_model(model_name=model_name,
793
+ authenticate=True,
794
+ use_existing_uid=False)
795
+ results = model.count(filters=[])
794
796
  # Check if we have results
797
+ self.assertIsNotNone(model)
798
+ self.assertIsInstance(model, Model)
799
+ self.assertEqual(model.model_name, model_name)
795
800
  self.assertIsNotNone(results)
796
- self.assertIsInstance(results, Model)
797
- self.assertEqual(results.model_name, model_name)
801
+ self.assertGreater(results, 0)
802
+
803
+ def test_42_get_model_no_authenticate(self) -> None:
804
+ """
805
+ Get a new Model object using the existing UID
806
+ """
807
+ self.model.authenticate()
808
+ model_name = 'res.users'
809
+ model = self.model.get_model(model_name=model_name,
810
+ authenticate=False,
811
+ use_existing_uid=True)
812
+ results = model.count(filters=[])
813
+ # Check if we have results
814
+ self.assertIsNotNone(model)
815
+ self.assertIsInstance(model, Model)
816
+ self.assertEqual(model.model_name, model_name)
817
+ self.assertIsNotNone(results)
818
+ self.assertGreater(results, 0)
@@ -0,0 +1,233 @@
1
+ ##
2
+ # Project: PyOdoo
3
+ # Description: API for Odoo
4
+ # Author: Fabio Castelli (Muflone) <muflone@muflone.com>
5
+ # Copyright: 2021-2023 Fabio Castelli
6
+ # License: GPL-3+
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
19
+ ##
20
+
21
+ import unittest
22
+ import uuid
23
+
24
+ from pyodoo.constants import APP_NAME, APP_VERSION
25
+ from pyodoo.v12 import SqlExcelQuery
26
+
27
+ import utility
28
+
29
+
30
+ class TestCaseSqlExcelQuery(unittest.TestCase):
31
+ @classmethod
32
+ def setUpClass(cls):
33
+ """
34
+ Query object preparation
35
+ """
36
+ info = utility.get_authentication_from_demo()
37
+ if SqlExcelQuery.is_available(endpoint=info['host'],
38
+ database=info['database'],
39
+ username=info['user'],
40
+ password=info['password'],
41
+ language='en_US'):
42
+ # The model sql.excel.pdf is available
43
+ cls.query_name = uuid.uuid4().hex
44
+ cls.query = SqlExcelQuery(name=cls.query_name,
45
+ category=f'{APP_NAME} {APP_VERSION}',
46
+ endpoint=info['host'],
47
+ database=info['database'],
48
+ username=info['user'],
49
+ password=info['password'],
50
+ language='en_US')
51
+ else:
52
+ # The model sql.excel.pdf is not available
53
+ cls.query = None
54
+
55
+ def setUp(self):
56
+ """
57
+ Check if the test could be run
58
+ """
59
+ if not self.query:
60
+ self.skipTest('Model sql.excel.pdf is not available')
61
+
62
+ def test_01_get_name(self) -> None:
63
+ """
64
+ Check the query name
65
+ """
66
+ results = self.query.name
67
+ # Check if the name is not None
68
+ self.assertIsNotNone(results)
69
+ # Check if the name is the same
70
+ self.assertEqual(results, self.query_name)
71
+
72
+ def test_02_get_language(self) -> None:
73
+ """
74
+ Check the query language
75
+ """
76
+ results = self.query.language
77
+ # Check if the language is not None
78
+ self.assertIsNotNone(results)
79
+ # Check if the language is the same
80
+ self.assertEqual(results, 'en_US')
81
+
82
+ def test_03_get_id(self) -> None:
83
+ """
84
+ Check the query ID
85
+ """
86
+ results = self.query.id
87
+ # Check if the ID is not None
88
+ self.assertIsNotNone(results)
89
+ # Check if the ID is > 0
90
+ self.assertGreater(results, 0)
91
+
92
+ def test_04_get_set_sql(self) -> None:
93
+ """
94
+ Get the query SQL text
95
+ """
96
+ results = self.query.get_sql()
97
+ # Check if the query SQL text is not None
98
+ self.assertIsNotNone(results)
99
+ # Check if the query SQL text length is = 0
100
+ self.assertEqual(len(results), 0)
101
+ # Set new query SQL text
102
+ sql_text = ('SELECT customer, supplier, COUNT(*) AS count '
103
+ 'FROM res_partner '
104
+ 'GROUP BY customer, supplier')
105
+ results = self.query.set_sql(text=sql_text)
106
+ # Check if the response is True
107
+ self.assertEqual(results, True)
108
+ # Check if the query SQL text is not None
109
+ results = self.query.get_sql()
110
+ self.assertIsNotNone(results)
111
+ # Check if the query SQL text is the expected
112
+ self.assertEqual(results, sql_text)
113
+
114
+ def test_05_get_state(self) -> None:
115
+ """
116
+ Get the query state
117
+ """
118
+ results = self.query.get_state()
119
+ # Check if the query state is not None
120
+ self.assertIsNotNone(results)
121
+ # Check if the query state is draft
122
+ self.assertEqual(results, 'draft')
123
+
124
+ def test_06_validate(self) -> None:
125
+ """
126
+ Set the query state to validate
127
+ """
128
+ self.query.validate()
129
+ # Check if the query state is not None
130
+ results = self.query.get_state()
131
+ self.assertIsNotNone(results)
132
+ # Check if the query state is validate
133
+ self.assertEqual(results, 'validate')
134
+
135
+ def test_07_set_draft(self) -> None:
136
+ """
137
+ Set the query state to draft
138
+ """
139
+ self.query.set_draft()
140
+ # Check if the query state is not None
141
+ results = self.query.get_state()
142
+ self.assertIsNotNone(results)
143
+ # Check if the query state is draft
144
+ self.assertEqual(results, 'draft')
145
+
146
+ def test_08_get_set_active(self) -> None:
147
+ """
148
+ Get and set the query active state
149
+ """
150
+ # Set the query as not active
151
+ results = self.query.set_active(active=False)
152
+ self.assertTrue(results)
153
+ # Check if the query active state is False
154
+ results = self.query.get_active()
155
+ self.assertFalse(results)
156
+ # Set the query as active
157
+ results = self.query.set_active(active=True)
158
+ self.assertTrue(results)
159
+ # Check if the query active state is True
160
+ results = self.query.get_active()
161
+ self.assertTrue(results)
162
+
163
+ def test_09_execute(self) -> None:
164
+ """
165
+ Execute the query
166
+ """
167
+ results = self.query.execute()
168
+ # Check if the query was executed
169
+ self.assertIsNone(results)
170
+
171
+ def test_10_get_file(self) -> None:
172
+ """
173
+ Get the query execution results as file
174
+ """
175
+ results = self.query.get_file()
176
+ # Check if the file is not None
177
+ self.assertIsNotNone(results)
178
+ # Check if the file is not empty
179
+ self.assertGreater(len(results), 0)
180
+
181
+ def test_11_clear(self) -> None:
182
+ """
183
+ Clear the query execution results file
184
+ """
185
+ self.query.clear()
186
+ results = self.query.get_file()
187
+ # Check if the language is None
188
+ self.assertIsNone(results)
189
+
190
+ def test_12_get_data(self) -> None:
191
+ """
192
+ Get the query execution results as data
193
+ """
194
+ self.query.execute()
195
+ results = self.query.get_data()
196
+ # Check if the results data are not None
197
+ self.assertIsNotNone(results)
198
+ # Check if the results have one or more results
199
+ self.assertGreaterEqual(len(results), 1)
200
+ # Check if the results data have the expected fields
201
+ self.assertIn('S.No', results[0])
202
+ self.assertIn('customer', results[0])
203
+ self.assertIn('supplier', results[0])
204
+ self.assertIn('count', results[0])
205
+ self.assertEqual(len(results[0]), 4)
206
+
207
+ def test_13_add_tags(self) -> None:
208
+ """
209
+ Add and get tags from the Query
210
+ """
211
+ model = self.query.model.get_model(model_name='sql.tags',
212
+ authenticate=False,
213
+ use_existing_uid=True)
214
+ tag_ids = model.search(filters=[])
215
+ for tag_id in tag_ids:
216
+ results = self.query.add_tag(tag_id=tag_id)
217
+ # Check if the addition was successful
218
+ self.assertTrue(results)
219
+ # Check the resulting tags
220
+ results = self.query.get_tags()
221
+ # Check the tags list
222
+ self.assertIsNotNone(results)
223
+ results = [item['id'] for item in results]
224
+ for tag_id in tag_ids:
225
+ self.assertIn(tag_id, results)
226
+
227
+ def test_14_delete(self) -> None:
228
+ """
229
+ Delete the query
230
+ """
231
+ results = self.query.delete()
232
+ # Check if the deletion was successful
233
+ self.assertTrue(results)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes