test_pandas_data_accessor.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. # Copyright 2021-2024 Avaiga Private Limited
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  4. # the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  9. # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  10. # specific language governing permissions and limitations under the License.
  11. import inspect
  12. import os
  13. from datetime import datetime
  14. from importlib import util
  15. import pandas
  16. from flask import g
  17. from taipy.gui import Gui
  18. from taipy.gui.data.data_format import _DataFormat
  19. from taipy.gui.data.decimator import ScatterDecimator
  20. from taipy.gui.data.pandas_data_accessor import _PandasDataAccessor
  21. def test_simple_data(gui: Gui, helpers, small_dataframe):
  22. accessor = _PandasDataAccessor(gui)
  23. pd = pandas.DataFrame(data=small_dataframe)
  24. ret_data = accessor.get_data("x", pd, {"start": 0, "end": -1}, _DataFormat.JSON)
  25. assert ret_data
  26. value = ret_data["value"]
  27. assert value
  28. assert value["rowcount"] == 3
  29. data = value["data"]
  30. assert len(data) == 3
  31. def test_simple_data_with_arrow(gui: Gui, helpers, small_dataframe):
  32. if util.find_spec("pyarrow"):
  33. accessor = _PandasDataAccessor(gui)
  34. pd = pandas.DataFrame(data=small_dataframe)
  35. ret_data = accessor.get_data("x", pd, {"start": 0, "end": -1}, _DataFormat.APACHE_ARROW)
  36. assert ret_data
  37. value = ret_data["value"]
  38. assert value
  39. assert value["rowcount"] == 3
  40. data = value["data"]
  41. assert isinstance(data, bytes)
  42. def test_get_all_simple_data(gui: Gui, helpers, small_dataframe):
  43. accessor = _PandasDataAccessor(gui)
  44. pd = pandas.DataFrame(data=small_dataframe)
  45. ret_data = accessor.get_data("x", pd, {"alldata": True}, _DataFormat.JSON)
  46. assert ret_data
  47. assert ret_data["alldata"] is True
  48. value = ret_data["value"]
  49. assert value
  50. data = value["data"]
  51. assert data == small_dataframe
  52. def test_slice(gui: Gui, helpers, small_dataframe):
  53. accessor = _PandasDataAccessor(gui)
  54. pd = pandas.DataFrame(data=small_dataframe)
  55. value = accessor.get_data("x", pd, {"start": 0, "end": 1}, _DataFormat.JSON)["value"]
  56. assert value["rowcount"] == 3
  57. data = value["data"]
  58. assert len(data) == 2
  59. value = accessor.get_data("x", pd, {"start": "0", "end": "1"}, _DataFormat.JSON)["value"]
  60. data = value["data"]
  61. assert len(data) == 2
  62. def test_sort(gui: Gui, helpers, small_dataframe):
  63. accessor = _PandasDataAccessor(gui)
  64. pd = pandas.DataFrame(data=small_dataframe)
  65. query = {"columns": ["name", "value"], "start": 0, "end": -1, "orderby": "name", "sort": "desc"}
  66. data = accessor.get_data("x", pd, query, _DataFormat.JSON)["value"]["data"]
  67. assert data[0]["name"] == "C"
  68. def test_aggregate(gui: Gui, helpers, small_dataframe):
  69. accessor = _PandasDataAccessor(gui)
  70. pd = pandas.DataFrame(data=small_dataframe)
  71. pd = pandas.concat(
  72. [pd, pandas.DataFrame(data={"name": ["A"], "value": [4]})], axis=0, join="outer", ignore_index=True
  73. )
  74. query = {"columns": ["name", "value"], "start": 0, "end": -1, "aggregates": ["name"], "applies": {"value": "sum"}}
  75. value = accessor.get_data("x", pd, query, _DataFormat.JSON)["value"]
  76. assert value["rowcount"] == 3
  77. data = value["data"]
  78. assert next(v.get("value") for v in data if v.get("name") == "A") == 5
  79. def test_filters(gui: Gui, helpers, small_dataframe):
  80. accessor = _PandasDataAccessor(gui)
  81. pd = pandas.DataFrame(data=small_dataframe)
  82. pd = pandas.concat(
  83. [pd, pandas.DataFrame(data={"name": ["A"], "value": [4]})], axis=0, join="outer", ignore_index=True
  84. )
  85. query = {
  86. "columns": ["name", "value"],
  87. "start": 0,
  88. "end": -1,
  89. "filters": [{"col": "name", "action": "!=", "value": ""}],
  90. }
  91. value = accessor.get_data("x", pd, query, _DataFormat.JSON)
  92. assert len(value["value"]["data"]) == 4
  93. query = {
  94. "columns": ["name", "value"],
  95. "start": 0,
  96. "end": -1,
  97. "filters": [{"col": "name", "action": "==", "value": ""}],
  98. }
  99. value = accessor.get_data("x", pd, query, _DataFormat.JSON)
  100. assert len(value["value"]["data"]) == 0
  101. query = {
  102. "columns": ["name", "value"],
  103. "start": 0,
  104. "end": -1,
  105. "filters": [{"col": "name", "action": "==", "value": "A"}],
  106. }
  107. value = accessor.get_data("x", pd, query, _DataFormat.JSON)
  108. assert len(value["value"]["data"]) == 2
  109. query = {
  110. "columns": ["name", "value"],
  111. "start": 0,
  112. "end": -1,
  113. "filters": [{"col": "name", "action": "==", "value": "A"}, {"col": "value", "action": "==", "value": 2}],
  114. }
  115. value = accessor.get_data("x", pd, query, _DataFormat.JSON)
  116. assert len(value["value"]["data"]) == 0
  117. query = {
  118. "columns": ["name", "value"],
  119. "start": 0,
  120. "end": -1,
  121. "filters": [{"col": "name", "action": "!=", "value": "A"}, {"col": "value", "action": "==", "value": 2}],
  122. }
  123. value = accessor.get_data("x", pd, query, _DataFormat.JSON)
  124. assert len(value["value"]["data"]) == 1
  125. assert value["value"]["data"][0]["_tp_index"] == 1
  126. def test_filter_by_date(gui: Gui, helpers, small_dataframe):
  127. accessor = _PandasDataAccessor(gui)
  128. pd = pandas.DataFrame(data=small_dataframe)
  129. pd["a date"] = [
  130. datetime.fromisocalendar(2022, 28, 1),
  131. datetime.fromisocalendar(2022, 28, 2),
  132. datetime.fromisocalendar(2022, 28, 3),
  133. ]
  134. query = {
  135. "columns": ["name", "value"],
  136. "start": 0,
  137. "end": -1,
  138. "filters": [{"col": "a date", "action": ">", "value": datetime.fromisocalendar(2022, 28, 3).isoformat() + "Z"}],
  139. }
  140. value = accessor.get_data("x", pd, query, _DataFormat.JSON)
  141. assert len(value["value"]["data"]) == 0
  142. query = {
  143. "columns": ["name", "value"],
  144. "start": 0,
  145. "end": -1,
  146. "filters": [{"col": "a date", "action": ">", "value": datetime.fromisocalendar(2022, 28, 2).isoformat() + "Z"}],
  147. }
  148. value = accessor.get_data("x", pd, query, _DataFormat.JSON)
  149. assert len(value["value"]["data"]) == 1
  150. query = {
  151. "columns": ["name", "value"],
  152. "start": 0,
  153. "end": -1,
  154. "filters": [{"col": "a date", "action": "<", "value": datetime.fromisocalendar(2022, 28, 3).isoformat() + "Z"}],
  155. }
  156. value = accessor.get_data("x", pd, query, _DataFormat.JSON)
  157. assert len(value["value"]["data"]) == 2
  158. query = {
  159. "columns": ["name", "value"],
  160. "start": 0,
  161. "end": -1,
  162. "filters": [
  163. {"col": "a date", "action": "<", "value": datetime.fromisocalendar(2022, 28, 2).isoformat() + "Z"},
  164. {"col": "a date", "action": ">", "value": datetime.fromisocalendar(2022, 28, 2).isoformat() + "Z"},
  165. ],
  166. }
  167. value = accessor.get_data("x", pd, query, _DataFormat.JSON)
  168. assert len(value["value"]["data"]) == 0
  169. query = {
  170. "columns": ["name", "value"],
  171. "start": 0,
  172. "end": -1,
  173. "filters": [
  174. {"col": "a date", "action": "<", "value": datetime.fromisocalendar(2022, 28, 3).isoformat() + "Z"},
  175. {"col": "a date", "action": ">", "value": datetime.fromisocalendar(2022, 28, 1).isoformat() + "Z"},
  176. ],
  177. }
  178. value = accessor.get_data("x", pd, query, _DataFormat.JSON)
  179. assert len(value["value"]["data"]) == 1
  180. def test_decimator(gui: Gui, helpers, small_dataframe):
  181. a_decimator = ScatterDecimator() # noqa: F841
  182. accessor = _PandasDataAccessor(gui)
  183. pd = pandas.DataFrame(data=small_dataframe)
  184. # set gui frame
  185. gui._set_frame(inspect.currentframe())
  186. gui.add_page("test", "<|Hello {a_decimator}|button|id={btn_id}|>")
  187. gui.run(run_server=False)
  188. flask_client = gui._server.test_client()
  189. cid = helpers.create_scope_and_get_sid(gui)
  190. # Get the jsx once so that the page will be evaluated -> variable will be registered
  191. flask_client.get(f"/taipy-jsx/test?client_id={cid}")
  192. with gui.get_flask_app().test_request_context(f"/taipy-jsx/test/?client_id={cid}", data={"client_id": cid}):
  193. g.client_id = cid
  194. ret_data = accessor.get_data(
  195. "x",
  196. pd,
  197. {
  198. "start": 0,
  199. "end": -1,
  200. "alldata": True,
  201. "decimatorPayload": {
  202. "decimators": [{"decimator": "a_decimator", "chartMode": "markers"}],
  203. "width": 100,
  204. },
  205. },
  206. _DataFormat.JSON,
  207. )
  208. assert ret_data
  209. value = ret_data["value"]
  210. assert value
  211. data = value["data"]
  212. assert len(data) == 2
  213. def test_edit(gui, small_dataframe):
  214. accessor = _PandasDataAccessor(gui)
  215. pd = pandas.DataFrame(small_dataframe)
  216. ln = len(pd)
  217. assert pd["value"].iloc[0] != 10
  218. ret_data = accessor.on_edit(pd, {"index": 0, "col": "value", "value": 10})
  219. assert isinstance(ret_data, pandas.DataFrame)
  220. assert len(ret_data) == ln
  221. assert ret_data["value"].iloc[0] == 10
  222. def test_delete(gui, small_dataframe):
  223. accessor = _PandasDataAccessor(gui)
  224. pd = pandas.DataFrame(small_dataframe)
  225. ln = len(pd)
  226. ret_data = accessor.on_delete(pd, {"index": 0})
  227. assert isinstance(ret_data, pandas.DataFrame)
  228. assert len(ret_data) == ln - 1
  229. def test_add(gui, small_dataframe):
  230. accessor = _PandasDataAccessor(gui)
  231. pd = pandas.DataFrame(small_dataframe)
  232. ln = len(pd)
  233. ret_data = accessor.on_add(pd, {"index": 0})
  234. assert isinstance(ret_data, pandas.DataFrame)
  235. assert len(ret_data) == ln + 1
  236. assert ret_data["value"].iloc[0] == 0
  237. assert ret_data["name"].iloc[0] == ""
  238. ret_data = accessor.on_add(pd, {"index": 2})
  239. assert isinstance(ret_data, pandas.DataFrame)
  240. assert len(ret_data) == ln + 1
  241. assert ret_data["value"].iloc[2] == 0
  242. assert ret_data["name"].iloc[2] == ""
  243. ret_data = accessor.on_add(pd, {"index": 0}, ["New", 100])
  244. assert isinstance(ret_data, pandas.DataFrame)
  245. assert len(ret_data) == ln + 1
  246. assert ret_data["value"].iloc[0] == 100
  247. assert ret_data["name"].iloc[0] == "New"
  248. ret_data = accessor.on_add(pd, {"index": 2}, ["New", 100])
  249. assert isinstance(ret_data, pandas.DataFrame)
  250. assert len(ret_data) == ln + 1
  251. assert ret_data["value"].iloc[2] == 100
  252. assert ret_data["name"].iloc[2] == "New"
  253. def test_csv(gui, small_dataframe):
  254. accessor = _PandasDataAccessor(gui)
  255. pd = pandas.DataFrame(small_dataframe)
  256. path = accessor.to_csv("", pd)
  257. assert path is not None
  258. assert os.path.getsize(path) > 0