onetick-py 1.169.0__py3-none-any.whl → 1.171.0__py3-none-any.whl

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.
onetick/py/__init__.py CHANGED
@@ -214,9 +214,15 @@ from onetick.py.core.per_tick_script import (
214
214
  )
215
215
  from onetick.py.callback import CallbackBase
216
216
  from onetick.py.sql import SqlQuery
217
- from onetick.py.run import run
217
+ from onetick.py.run import run, run_async
218
218
  from onetick.py.math import rand, now
219
- from onetick.py.misc import bit_and, bit_or, bit_at, bit_xor, bit_not, hash_code, get_symbology_mapping
219
+ from onetick.py.misc import (
220
+ bit_and, bit_or, bit_at, bit_xor, bit_not,
221
+ hash_code,
222
+ get_symbology_mapping,
223
+ get_onetick_version,
224
+ get_username,
225
+ )
220
226
  from onetick.py.core.column import Column
221
227
  from onetick.py.core.column_operations.base import Operation, Expr as expr, Raw as raw, OnetickParameter as param
222
228
  from onetick.py.core.per_tick_script import remote, Once, once, logf, throw_exception
onetick/py/_version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # This file was generated automatically. DO NOT CHANGE.
2
- VERSION = '1.169.0'
2
+ VERSION = '1.171.0'
@@ -956,7 +956,7 @@ def generic(*args, **kwargs):
956
956
 
957
957
  >>> data = otp.Ticks({'A': [1, 2, 1]})
958
958
  >>> def count_values(source, value):
959
- ... values, _ = source[source['A'] == value]
959
+ ... values = source.where(source['A'] == value)
960
960
  ... return values.agg({'count': otp.agg.count()})
961
961
  >>> data = otp.agg.generic(count_values).apply(data, value=1)
962
962
  >>> otp.run(data)
@@ -146,6 +146,8 @@ class _OrderBookAggregation(_Aggregation, ABC):
146
146
  self.max_initialization_days = max_initialization_days
147
147
  self.book_uncross_method = book_uncross_method
148
148
  self.dq_events_that_clear_book = ','.join(dq_events_that_clear_book) if dq_events_that_clear_book else None
149
+ self.bound_symbols = None
150
+
149
151
  super().__init__(_Column('TIMESTAMP'), *args, **kwargs)
150
152
 
151
153
  def _param_validation(self):
@@ -170,6 +172,19 @@ class _OrderBookAggregation(_Aggregation, ABC):
170
172
  raise TypeError(f"Aggregation `{self.NAME}` need these columns: "
171
173
  f"BUY_SELL_FLAG, PRICE, SIZE and (UPDATE_TIME or DELETED_TIME)")
172
174
 
175
+ def to_ep(self, *args, **kwargs):
176
+ ob_ep = super().to_ep(*args, **kwargs)
177
+ if self.bound_symbols:
178
+ ob_ep = ob_ep.symbols(self.bound_symbols)
179
+
180
+ return ob_ep
181
+
182
+ def set_bound_symbols(self, bound_symbols=None):
183
+ if isinstance(bound_symbols, str):
184
+ bound_symbols = [bound_symbols]
185
+
186
+ self.bound_symbols = bound_symbols
187
+
173
188
 
174
189
  class ObSnapshot(_OrderBookAggregation):
175
190
  NAME = 'OB_SNAPSHOT'
onetick/py/cache.py CHANGED
@@ -113,7 +113,7 @@ def create_cache(
113
113
  The cache will be cleared every X seconds, triggering new query executions when data is requested.
114
114
  tick_type: str
115
115
  Tick type.
116
- symbol: str, list of str, list of otq.Symbol, :py:class:`onetick.py.Source`, pd.DataFrame, optional
116
+ symbol: str, list of str, list of otq.Symbol, :py:class:`onetick.py.Source`, :pandas:`pandas.DataFrame`, optional
117
117
  ``symbols`` parameter of ``otp.run()``.
118
118
  db: str
119
119
  Database.
@@ -259,7 +259,7 @@ def delete_cache(
259
259
  :func:`<onetick.py.create_cache>`.
260
260
  tick_type: str
261
261
  Tick type.
262
- symbol: str, list of str, list of otq.Symbol, :py:class:`onetick.py.Source`, pd.DataFrame, optional
262
+ symbol: str, list of str, list of otq.Symbol, :py:class:`onetick.py.Source`, :pandas:`pandas.DataFrame`, optional
263
263
  ``symbols`` parameter of ``otp.run()``.
264
264
  db: str
265
265
  Database.
@@ -329,7 +329,7 @@ def modify_cache_config(
329
329
  New value of configuration parameter. Will be converted to string.
330
330
  tick_type: str
331
331
  Tick type.
332
- symbol: str, list of str, list of otq.Symbol, :py:class:`onetick.py.Source`, pd.DataFrame, optional
332
+ symbol: str, list of str, list of otq.Symbol, :py:class:`onetick.py.Source`, :pandas:`pandas.DataFrame`, optional
333
333
  ``symbols`` parameter of ``otp.run()``.
334
334
  db: str
335
335
  Database.
@@ -18,7 +18,7 @@ class LogCallback(CallbackBase):
18
18
 
19
19
  class ManualDataframeCallback(CallbackBase):
20
20
  """
21
- This callback class can be used to generate the same pandas.DataFrame result as in otp.run.
21
+ This callback class can be used to generate the same :pandas:`pandas.DataFrame` result as in otp.run.
22
22
  Unlike otp.run, here result is constructed manually, one tick at a time.
23
23
  This may lead to lower memory usage in some cases.
24
24
  See task PY-863 for details.
@@ -32,6 +32,28 @@ def parse_datetime(s):
32
32
  )
33
33
 
34
34
 
35
+ def parse_bool(value) -> Optional[bool]:
36
+ str_value = str(value).lower()
37
+ if str_value in ('1', 'true', 'yes'):
38
+ return True
39
+ elif str_value in ('0', 'false', 'no'):
40
+ return False
41
+ else:
42
+ return None
43
+
44
+
45
+ def parse_true(value) -> bool:
46
+ return parse_bool(value) is True
47
+
48
+
49
+ def parse_bool_or_string(value) -> Union[bool, str]:
50
+ parsed_value = parse_bool(value)
51
+ if parsed_value is not None:
52
+ return parsed_value
53
+ else:
54
+ return str(value)
55
+
56
+
35
57
  def _env_func_concurrency(value: str) -> Optional[int]:
36
58
  if not value:
37
59
  return None
@@ -197,7 +219,7 @@ class OtpShowStackInfoProperty(OtpProperty):
197
219
  """
198
220
  @staticmethod
199
221
  def parser(value):
200
- return str(value).lower() in ('1', 'true', 'yes')
222
+ return parse_true(value)
201
223
 
202
224
  def __init__(self, *args, **kwargs):
203
225
  super().__init__(*args, **kwargs)
@@ -492,7 +514,7 @@ class Config:
492
514
  base_default=False,
493
515
  allowed_types=[bool],
494
516
  env_var_name='OTP_PRESORT_FORCE_DEFAULT_CONCURRENCY',
495
- env_var_func=OtpShowStackInfoProperty.parser,
517
+ env_var_func=parse_true,
496
518
  )
497
519
 
498
520
  # default batch size is set to 0, so the number of symbols in batch is not limited
@@ -623,10 +645,14 @@ class Config:
623
645
  )
624
646
 
625
647
  trusted_certificates_file = OtpProperty(
626
- description='Path to the file with list of trusted Certificate Authority certificates for WebAPI requests.',
648
+ description='Either a boolean, in which case it controls whether we verify the server TLS certificate '
649
+ 'or a string with the path to the file with list of '
650
+ 'trusted Certificate Authority certificates for WebAPI requests. '
651
+ 'Default behaviour implies verification is enabled.',
627
652
  base_default=None,
628
- allowed_types=str,
653
+ allowed_types=(bool, str),
629
654
  env_var_name='OTP_SSL_CERT_FILE',
655
+ env_var_func=parse_bool_or_string,
630
656
  )
631
657
 
632
658
  max_expected_ticks_per_symbol = OtpProperty(
@@ -641,7 +667,7 @@ class Config:
641
667
  base_default=False,
642
668
  allowed_types=(str, bool, int),
643
669
  env_var_name='OTP_SHOW_STACK_INFO',
644
- env_var_func=OtpShowStackInfoProperty.parser,
670
+ env_var_func=parse_true,
645
671
  )
646
672
 
647
673
  log_symbol = OtpProperty(
@@ -651,7 +677,7 @@ class Config:
651
677
  base_default=False,
652
678
  allowed_types=(str, bool, int),
653
679
  env_var_name='OTP_LOG_SYMBOL',
654
- env_var_func=OtpShowStackInfoProperty.parser,
680
+ env_var_func=parse_true,
655
681
  )
656
682
 
657
683
  ignore_ticks_in_unentitled_time_range = OtpProperty(
@@ -659,7 +685,7 @@ class Config:
659
685
  base_default=False,
660
686
  env_var_name='OTP_IGNORE_TICKS_IN_UNENTITLED_TIME_RANGE',
661
687
  allowed_types=(str, bool, int),
662
- env_var_func=OtpShowStackInfoProperty.parser,
688
+ env_var_func=parse_true,
663
689
  )
664
690
 
665
691
  main_query_generated_filename = OtpProperty(
@@ -685,7 +711,7 @@ class Config:
685
711
  base_default=False,
686
712
  env_var_name='OTP_OTQ_DEBUG_MODE',
687
713
  allowed_types=(str, bool, int),
688
- env_var_func=OtpShowStackInfoProperty.parser,
714
+ env_var_func=parse_true,
689
715
  )
690
716
 
691
717
  allow_lowercase_in_saved_fields = OtpProperty(
@@ -702,7 +728,7 @@ class Config:
702
728
  base_default=True,
703
729
  env_var_name='OTP_CLEAN_UP_TMP_FILES',
704
730
  allowed_types=(str, bool, int),
705
- env_var_func=OtpShowStackInfoProperty.parser,
731
+ env_var_func=parse_true,
706
732
  )
707
733
 
708
734
  default_schema_policy = OtpProperty(
@@ -165,7 +165,7 @@ class _StateBase(ABC):
165
165
  data.state_vars['VAR'] = otp.state.tick_list()
166
166
  def fun(min_value):
167
167
  t = otp.Ticks(X=[123, 234])
168
- t, _ = t[t['X'] > min_value]
168
+ t = t.where(t['X'] > min_value)
169
169
  return t
170
170
  data = data.state_vars['VAR'].modify_from_query(fun, params={'min_value': 200})
171
171
  data = data.state_vars['VAR'].dump()
@@ -344,8 +344,9 @@ class _TickSequence(_StateBase):
344
344
  def __init__(self, name, obj_ref, default_value, scope, schema=None, **kwargs):
345
345
  if kwargs:
346
346
  raise ValueError(f"Unknown parameters for '{self.__class__.__name__}': {list(kwargs)}")
347
- if default_value is not None and not isinstance(default_value, _QueryEvalWrapper):
348
- raise ValueError('only otp.eval can be used as initial value for tick sequences')
347
+ from onetick.py.core.source import Source
348
+ if default_value is not None and not isinstance(default_value, (_QueryEvalWrapper, Source)):
349
+ raise ValueError('only otp.eval and otp.Source objects can be used as initial value for tick sequences')
349
350
  if default_value is not None and schema is not None:
350
351
  # TODO: check that the two schemas align or possibly that they are exactly the same
351
352
  pass
@@ -396,9 +397,13 @@ class _TickSequence(_StateBase):
396
397
  self._schema = None
397
398
  if not self._schema:
398
399
  if self.default_value is not None:
399
- # If tick sequence is initialized from eval,
400
- # then we get schema from the Source in eval.
401
- self._schema = self.default_value.query.schema.copy()
400
+ from onetick.py.core.source import Source
401
+ if isinstance(self.default_value, _QueryEvalWrapper):
402
+ # If tick sequence is initialized from eval,
403
+ # then we get schema from the Source in eval.
404
+ self._schema = self.default_value.query.schema.copy()
405
+ elif isinstance(self.default_value, Source):
406
+ self._schema = self.default_value.schema.copy()
402
407
  else:
403
408
  # If tick sequence is initialized as empty,
404
409
  # then it's schema will be derived from the schema of the parent object (e.g. source)
@@ -81,7 +81,13 @@ class StateVars:
81
81
  str_type = type2str(base_type)
82
82
 
83
83
  expression = f'{str_type} {name}'
84
+
85
+ import onetick.py as otp
84
86
  if value.default_value is not None:
85
- # TODO: PY-952: use to_eval_string(self._owner._tmp_otq) here if otp.eval is passed
86
- expression += f' = {value2str(value.default_value)}'
87
+ if isinstance(value.default_value, otp.Source):
88
+ value_expression = otp.eval(value.default_value).to_eval_string(self._owner._tmp_otq)
89
+ else:
90
+ # TODO: PY-952: use to_eval_string(self._owner._tmp_otq) here if otp.eval is passed
91
+ value_expression = value2str(value.default_value)
92
+ expression += f' = {value_expression}'
87
93
  self._owner.sink(otq.DeclareStateVariables(variables=expression, scope=value.scope))
@@ -394,7 +394,7 @@ def join_with_collection(
394
394
  >>>
395
395
  >>> trd_qte = trd_qte.state_vars['LAST_QUOTE_PER_EXCHANGE'].update(where=trd_qte['TICK_TYPE'] == 'QTE',
396
396
  ... value_fields=['ASK_PRICE', 'BID_PRICE'])
397
- >>> trd, _ = trd_qte[trd_qte['TICK_TYPE'] == 'TRD']
397
+ >>> trd = trd_qte.where(trd_qte['TICK_TYPE'] == 'TRD')
398
398
  >>> trd.drop(['ASK_PRICE', 'BID_PRICE', 'EXCHANGE'], inplace=True)
399
399
  >>> trd = trd.join_with_collection('LAST_QUOTE_PER_EXCHANGE')
400
400
  >>> otp.run(trd)[['PRICE', 'SIZE', 'EXCHANGE', 'ASK_PRICE', 'BID_PRICE']]
@@ -514,7 +514,7 @@ def cache(
514
514
  the result data not found in the cache. Otherwise, the cache remains unchanged.
515
515
  tick_type: str
516
516
  Tick type.
517
- symbol: str, list of str, list of otq.Symbol, :py:class:`onetick.py.Source`, pd.DataFrame, optional
517
+ symbol: str, list of str, list of otq.Symbol, :py:class:`onetick.py.Source`, :pandas:`pandas.DataFrame`, optional
518
518
  ``symbols`` parameter of ``otp.run()``.
519
519
  db: str
520
520
  Database.
@@ -227,7 +227,7 @@ class _StrAccessor(_Accessor):
227
227
 
228
228
  >>> q = otp.DataSource('US_COMP', tick_type='TRD', symbols=['SPY']) # doctest: +SKIP
229
229
  >>> q = q[['PRICE', 'SIZE', 'COND', 'EXCHANGE']] # doctest: +SKIP
230
- >>> q, _ = q[q['COND'].str.match('^[^O6TUHILNRWZ47QMBCGPV]*$')] # doctest: +SKIP
230
+ >>> q = q.where(q['COND'].str.match('^[^O6TUHILNRWZ47QMBCGPV]*$')) # doctest: +SKIP
231
231
  >>> otp.run(q, start=otp.dt(2023, 5, 15, 9, 30), end=otp.dt(2023, 5, 15, 9, 30, 1)) # doctest: +SKIP
232
232
  Time PRICE SIZE COND EXCHANGE
233
233
  0 2023-05-15 09:30:00.000776704 412.220 247 Z
@@ -1225,7 +1225,7 @@ class _StrAccessor(_Accessor):
1225
1225
  This function can be used to filter out ticks:
1226
1226
 
1227
1227
  >>> data = otp.Ticks(X=['a', 'ab', 'b_', 'b%'])
1228
- >>> data, _ = data[data['X'].str.like('a%')]
1228
+ >>> data = data.where(data['X'].str.like('a%'))
1229
1229
  >>> otp.run(data)
1230
1230
  Time X
1231
1231
  0 2003-12-01 00:00:00.000 a
@@ -1335,7 +1335,7 @@ class _StrAccessor(_Accessor):
1335
1335
  :skipif: not is_ilike_supported()
1336
1336
 
1337
1337
  data = otp.Ticks(X=['a', 'ab', 'Ab', 'b_'])
1338
- data, _ = data[data['X'].str.ilike('a%')]
1338
+ data = data.where(data['X'].str.ilike('a%'))
1339
1339
  df = otp.run(data)
1340
1340
  print(df)
1341
1341
 
@@ -725,7 +725,7 @@ class Operation:
725
725
  Examples
726
726
  --------
727
727
  >>> t = otp.Ticks(A=range(4))
728
- >>> t, _ = t[~(t['A'] > 1)]
728
+ >>> t = t.where(~(t['A'] > 1))
729
729
  >>> otp.run(t)[['A']]
730
730
  A
731
731
  0 0
@@ -741,7 +741,7 @@ class Operation:
741
741
  Examples
742
742
  --------
743
743
  >>> t = otp.Ticks(A=range(4))
744
- >>> t, _ = t[(t['A'] == 1)]
744
+ >>> t = t.where((t['A'] == 1))
745
745
  >>> otp.run(t)[['A']]
746
746
  A
747
747
  0 1
@@ -756,7 +756,7 @@ class Operation:
756
756
  Examples
757
757
  --------
758
758
  >>> t = otp.Ticks(A=range(4))
759
- >>> t, _ = t[(t['A'] != 1)]
759
+ >>> t = t.where((t['A'] != 1))
760
760
  >>> otp.run(t)[['A']]
761
761
  A
762
762
  0 0
@@ -773,7 +773,7 @@ class Operation:
773
773
  Examples
774
774
  --------
775
775
  >>> t = otp.Ticks(A=range(4))
776
- >>> t, _ = t[(t['A'] == 1) | (t['A'] == 2)]
776
+ >>> t = t.where((t['A'] == 1) | (t['A'] == 2))
777
777
  >>> otp.run(t)[['A']]
778
778
  A
779
779
  0 1
@@ -789,7 +789,7 @@ class Operation:
789
789
  Examples
790
790
  --------
791
791
  >>> t = otp.Ticks(A=[1, 1], B=[1, 2])
792
- >>> t, _ = t[(t['A'] == 1) & (t['B'] == 1)]
792
+ >>> t = t.where((t['A'] == 1) & (t['B'] == 1))
793
793
  >>> otp.run(t)[['A', 'B']]
794
794
  A B
795
795
  0 1 1
@@ -804,7 +804,7 @@ class Operation:
804
804
  Examples
805
805
  --------
806
806
  >>> t = otp.Ticks(A=range(4))
807
- >>> t, _ = t[t['A'] <= 2]
807
+ >>> t = t.where(t['A'] <= 2)
808
808
  >>> otp.run(t)[['A']]
809
809
  A
810
810
  0 0
@@ -821,7 +821,7 @@ class Operation:
821
821
  Examples
822
822
  --------
823
823
  >>> t = otp.Ticks(A=range(4))
824
- >>> t, _ = t[t['A'] < 2]
824
+ >>> t = t.where(t['A'] < 2)
825
825
  >>> otp.run(t)[['A']]
826
826
  A
827
827
  0 0
@@ -837,7 +837,7 @@ class Operation:
837
837
  Examples
838
838
  --------
839
839
  >>> t = otp.Ticks(A=range(4))
840
- >>> t, _ = t[t['A'] >= 2]
840
+ >>> t = t.where(t['A'] >= 2)
841
841
  >>> otp.run(t)[['A']]
842
842
  A
843
843
  0 2
@@ -853,7 +853,7 @@ class Operation:
853
853
  Examples
854
854
  --------
855
855
  >>> t = otp.Ticks(A=range(4))
856
- >>> t, _ = t[t['A'] > 2]
856
+ >>> t = t.where(t['A'] > 2)
857
857
  >>> otp.run(t)[['A']]
858
858
  A
859
859
  0 3
@@ -171,7 +171,7 @@ def eval(query, symbol=None, start=None, end=None,
171
171
  >>> data = otp.Ticks(X=[1, 2, 3])
172
172
  >>> # note that in this case column WHERE must be specified,
173
173
  >>> # because evaluated query returns tick with more than one field
174
- >>> data, _ = data[otp.eval(get_filter, a=0, b=2)['WHERE']]
174
+ >>> data = data.where(otp.eval(get_filter, a=0, b=2)['WHERE'])
175
175
  >>> otp.run(data)
176
176
  Time X
177
177
  0 2003-12-01 1
@@ -183,12 +183,12 @@ def eval(query, symbol=None, start=None, end=None,
183
183
  ... 'TICK_TYPE': ['TRD', 'QTE'],
184
184
  ... 'WHERE': ['PRICE>=1.4', 'ASK_PRICE>=1.4']
185
185
  ... })
186
- ... res, _ = res[res['TICK_TYPE'] == tick_type]
186
+ ... res = res.where(res['TICK_TYPE'] == tick_type)
187
187
  ... return res.drop(['TICK_TYPE'])
188
188
  >>> t = otp.DataSource('US_COMP::TRD')
189
189
  >>> # note that in this case column WHERE do not need to be specified,
190
190
  >>> # because evaluated query returns tick with only one field
191
- >>> t, _ = t[otp.eval(filter_by_tt, tick_type=t['_TICK_TYPE'])]
191
+ >>> t = t.where(otp.eval(filter_by_tt, tick_type=t['_TICK_TYPE']))
192
192
  >>> otp.run(t, start=otp.dt(2022, 3, 1), end=otp.dt(2022, 3, 2))
193
193
  Time PRICE SIZE
194
194
  0 2022-03-01 00:00:00.001 1.4 10
@@ -1095,6 +1095,8 @@ class CaseExpressionParser(ExpressionParser):
1095
1095
  try:
1096
1096
  return super().call(expr)
1097
1097
  except Exception:
1098
+ if orig_err is not None:
1099
+ raise err from orig_err
1098
1100
  raise ValueError(
1099
1101
  f"Can't convert function '{astunparse(expr)}' to CASE() expression."
1100
1102
  ) from err
@@ -1,3 +1,4 @@
1
+ import itertools
1
2
  import warnings
2
3
  from typing import Union, Iterable, Tuple, Optional, Any, Literal
3
4
  from datetime import date as dt_date, datetime, timedelta
@@ -7,6 +8,7 @@ from dateutil.tz import gettz
7
8
 
8
9
  import onetick.py as otp
9
10
  from onetick.py import configuration, utils
11
+ from onetick.py import types as ott
10
12
  from onetick.py.compatibility import is_native_plus_zstd_supported, is_show_db_list_show_description_supported
11
13
  from onetick.py.core import db_constants
12
14
  from onetick.py.otq import otq
@@ -909,10 +911,12 @@ class DB:
909
911
 
910
912
 
911
913
  def databases(
912
- context=utils.default, derived: bool = False, readable_only: bool = True, fetch_description: bool = False,
913
- ) -> dict[str, DB]:
914
+ context=utils.default, derived: bool = False, readable_only: bool = True,
915
+ fetch_description: Optional[bool] = None,
916
+ as_table: bool = False,
917
+ ) -> Union[dict[str, DB], pd.DataFrame]:
914
918
  """
915
- Gets all available databases in the ``context``
919
+ Gets all available databases in the ``context``.
916
920
 
917
921
  Parameters
918
922
  ----------
@@ -931,6 +935,9 @@ def databases(
931
935
  fetch_description: bool
932
936
  If set to True, retrieves descriptions for databases and puts them into ``description`` property of
933
937
  :py:class:`~onetick.py.DB` objects in a returned dict.
938
+ as_table: bool
939
+ If False (default), this function returns a dictionary of database names and database objects.
940
+ If True, returns a :pandas:`pandas.DataFrame` table where each row contains the info for each database.
934
941
 
935
942
  See also
936
943
  --------
@@ -940,50 +947,66 @@ def databases(
940
947
 
941
948
  Returns
942
949
  -------
943
- dict
944
- Dict where keys are database names and values are :class:`DB <onetick.py.db._inspection.DB>` objects
945
- with ``context`` specified.
946
- """
947
- if fetch_description and not is_show_db_list_show_description_supported():
948
- fetch_description = False
950
+ Dict where keys are database names and values are :class:`DB <onetick.py.db._inspection.DB>` objects
951
+ or :pandas:`pandas.DataFrame` object depending on ``as_table`` parameter.
949
952
 
950
- if readable_only:
951
- node = (
952
- otq.AccessInfo(info_type='DATABASES', show_for_all_users=False, deep_scan=True).tick_type('ANY')
953
- >> otq.Passthrough('DB_NAME,READ_ACCESS')
954
- >> otq.WhereClause(where='READ_ACCESS = 1')
955
- )
953
+ Examples
954
+ --------
956
955
 
957
- if fetch_description:
958
- join = otq.Join(
959
- left_source='LEFT', join_type='LEFT_OUTER', join_criteria='LEFT.DB_NAME = RIGHT.DATABASE_NAME'
960
- )
961
-
962
- _ = node.set_node_name('LEFT') >> join
963
- _ = (
964
- otq.ShowDbList(show_description=fetch_description).tick_type('ANY')
965
- >> otq.Passthrough('DATABASE_NAME,DESCRIPTION').set_node_name('RIGHT')
966
- >> join
967
- )
968
-
969
- node = (
970
- join >> otq.Passthrough('LEFT.DB_NAME,RIGHT.DESCRIPTION')
971
- >> otq.RenameFields("LEFT.DB_NAME=DB_NAME,RIGHT.DESCRIPTION=DESCRIPTION")
972
- )
973
- else:
974
- db_list_kwargs = {}
975
- output_fields = ['DATABASE_NAME']
976
- if fetch_description:
977
- db_list_kwargs['show_description'] = fetch_description
978
- output_fields.append('DESCRIPTION')
956
+ Get the dictionary of database names and objects:
979
957
 
980
- node = (
981
- otq.ShowDbList(**db_list_kwargs).tick_type('ANY')
982
- >> otq.Passthrough(','.join(output_fields))
983
- )
958
+ >>> otp.databases() # doctest: +SKIP
959
+ {'ABU_DHABI': <onetick.py.db._inspection.DB at 0x7f9413a5e8e0>,
960
+ 'ABU_DHABI_BARS': <onetick.py.db._inspection.DB at 0x7f9413a5ef40>,
961
+ 'ABU_DHABI_DAILY': <onetick.py.db._inspection.DB at 0x7f9413a5eac0>,
962
+ 'ALPHA': <onetick.py.db._inspection.DB at 0x7f9413a5e940>,
963
+ 'ALPHA_X': <onetick.py.db._inspection.DB at 0x7f9413a5e490>,
964
+ ...
965
+ }
966
+
967
+ Get a table with database info:
984
968
 
985
- if not fetch_description:
986
- node = node >> otq.AddField('DESCRIPTION', '""')
969
+ >>> otp.databases(as_table=True) # doctest: +SKIP
970
+ Time DB_NAME READ_ACCESS WRITE_ACCESS ...
971
+ 0 2003-01-01 ABU_DHABI 1 0 ...
972
+ 1 2003-01-01 ABU_DHABI_BARS 1 1 ...
973
+ 2 2003-01-01 ABU_DHABI_DAILY 1 1 ...
974
+ 3 2003-01-01 ALPHA 1 1 ...
975
+ 4 2003-01-01 ALPHA_X 1 1 ...
976
+ ... ... ... ... ... ...
977
+ """
978
+ show_db_list_kwargs = {}
979
+ if fetch_description is not None and is_show_db_list_show_description_supported() and (
980
+ 'show_description' in otq.ShowDbList.Parameters.list_parameters()
981
+ ):
982
+ show_db_list_kwargs['show_description'] = fetch_description
983
+
984
+ node = otq.AccessInfo(info_type='DATABASES', show_for_all_users=False, deep_scan=True).tick_type('ANY')
985
+ # for some reason ACCESS_INFO sometimes return several ticks
986
+ # for the same database with different SERVER_ADDRESS values
987
+ # so we get only the first tick
988
+ node = (
989
+ node >> otq.NumTicks(is_running_aggr=True, group_by='DB_NAME',
990
+ all_fields_for_sliding=False, output_field_name='NUM_TICKS')
991
+ >> otq.WhereClause(where='NUM_TICKS = 1')
992
+ >> otq.Passthrough('NUM_TICKS', drop_fields=True)
993
+ )
994
+ if readable_only:
995
+ node = node >> otq.WhereClause(where='READ_ACCESS = 1')
996
+
997
+ left = node.set_node_name('LEFT')
998
+ right = otq.ShowDbList(**show_db_list_kwargs).tick_type('ANY').set_node_name('RIGHT')
999
+ join = otq.Join(
1000
+ left_source='LEFT', join_type='INNER', join_criteria='LEFT.DB_NAME = RIGHT.DATABASE_NAME',
1001
+ add_source_prefix=False,
1002
+ )
1003
+ left >> join << right # pylint: disable=pointless-statement
1004
+ node = join >> otq.Passthrough('LEFT.TIMESTAMP,RIGHT.TIMESTAMP,DATABASE_NAME', drop_fields=True)
1005
+
1006
+ # times bigger than datetime.max are not representable in python
1007
+ max_dt = ott.value2str(datetime.max)
1008
+ node = node >> otq.UpdateFields(set=f'INTERVAL_START={max_dt}', where=f'INTERVAL_START > {max_dt}')
1009
+ node = node >> otq.UpdateFields(set=f'INTERVAL_END={max_dt}', where=f'INTERVAL_END > {max_dt}')
987
1010
 
988
1011
  dbs = otp.run(node,
989
1012
  symbols='LOCAL::',
@@ -992,15 +1015,20 @@ def databases(
992
1015
  end=db_constants.DEFAULT_END_DATE,
993
1016
  context=context)
994
1017
 
1018
+ if as_table:
1019
+ return dbs
1020
+
995
1021
  # WebAPI returns empty DataFrame (no columns) if there are no databases
996
1022
  if len(dbs) == 0:
997
1023
  return {}
998
1024
 
999
- db_list = list(dbs['DB_NAME'] if readable_only else dbs['DATABASE_NAME'])
1000
- merged_db_list = list(zip(db_list, dbs['DESCRIPTION']))
1025
+ db_list = list(dbs['DB_NAME'])
1026
+ db_description_list = dbs['DESCRIPTION'] if 'DESCRIPTION' in dbs else itertools.repeat('')
1027
+ merged_db_list = list(zip(db_list, db_description_list))
1001
1028
 
1002
1029
  db_dict = {
1003
- db_name: DB(db_name, description=db_description, context=context) for db_name, db_description in merged_db_list
1030
+ db_name: DB(db_name, description=db_description, context=context)
1031
+ for db_name, db_description in merged_db_list
1004
1032
  }
1005
1033
 
1006
1034
  if derived:
@@ -1018,6 +1046,7 @@ def derived_databases(
1018
1046
  selection_criteria='all',
1019
1047
  db=None,
1020
1048
  db_discovery_scope='query_host_only',
1049
+ as_table: bool = False,
1021
1050
  ) -> dict[str, DB]:
1022
1051
  """
1023
1052
  Gets available derived databases.
@@ -1052,6 +1081,9 @@ def derived_databases(
1052
1081
  an attempt will be performed to get derived databases from all reachable hosts.
1053
1082
  When *query_host_only* is specified,
1054
1083
  only derived databases from the host on which the query is performed will be returned.
1084
+ as_table: bool
1085
+ If False (default), this function returns a dictionary of database names and database objects.
1086
+ If True, returns a :pandas:`pandas.DataFrame` table where each row contains the info for each database.
1055
1087
 
1056
1088
  See also
1057
1089
  --------
@@ -1059,9 +1091,8 @@ def derived_databases(
1059
1091
 
1060
1092
  Returns
1061
1093
  -------
1062
- dict
1063
- Dict where keys are database names and values are :class:`DB <onetick.py.db._inspection.DB>` objects
1064
- with ``context`` specified.
1094
+ Dict where keys are database names and values are :class:`DB <onetick.py.db._inspection.DB>` objects
1095
+ or :pandas:`pandas.DataFrame` object depending on ``as_table`` parameter.
1065
1096
  """
1066
1097
  if start and end:
1067
1098
  time_range = otq.ShowDerivedDbList.TimeRange.QUERY_TIME_INTERVAL
@@ -1089,6 +1120,8 @@ def derived_databases(
1089
1120
  ep = ep.tick_type('ANY')
1090
1121
  db = db or 'LOCAL'
1091
1122
  dbs = otp.run(ep, symbols=f'{db}::', start=start, end=end, context=context)
1123
+ if as_table:
1124
+ return dbs
1092
1125
  if len(dbs) == 0:
1093
1126
  return {}
1094
1127
  db_list = list(dbs['DERIVED_DB_NAME'])