# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['notalib',
 'notalib.django',
 'notalib.django.clickhouse',
 'notalib.django_xauth',
 'notalib.django_xauth.migrations',
 'notalib.pandas']

package_data = \
{'': ['*']}

install_requires = \
['arrow>=0.14.0,<2.0.0']

setup_kwargs = {
    'name': 'notalib',
    'version': '2.0.0',
    'description': 'A collection of utility functions & classes',
    'long_description': '# notalib [![Django CI](https://github.com/m1kc/notalib/actions/workflows/django.yml/badge.svg)](https://github.com/m1kc/notalib/actions/workflows/django.yml) [![Coverage Status](https://coveralls.io/repos/github/m1kc/notalib/badge.svg?branch=master)](https://coveralls.io/github/m1kc/notalib?branch=master)\n\nCollection of small Python utility functions and classes. Each one was created because I needed it and it didn\'t exist or I didn\'t like the existing implementations. 100% of code is used in real-world projects.\n\n(And one day, the documentation is going to be actually good. In the meanwhile, don\'t hesitate to ask if something\'s not clear.)\n\n## Install\n\n```sh\npip install notalib\n```\n\nOr with [poetry](https://python-poetry.org/):\n\n```sh\npoetry add notalib\n```\n\n## Maintenance & bugfixes\n\nWhile I try to fix bugs, add new features, and review any PRs when I have time, there\'re no promises and no set timeframes, even if a bug is critical. That\'s a project I do in my free time, free of charge.\n\nIf that\'s not enough for you or you have an urgent request, there are paid maintenance options (bugfixing, features, expedite PR review, 24h security responses). Contact me for prices: m1kc@yandex.ru\n\nAlso feel free to just send me money:\n\n* MasterCard: 5559 4925 7484 0297\n* PayPal: [paypal.me/thisism1kc](https://paypal.me/thisism1kc)\n\nDonations are always appreciated, even if you send 10$.\n\n## Constants included\n\n* `notalib.utf.BOM`: contains string `b\'\\xEF\\xBB\\xBF\'` (UTF-8 little endian byte order mark).\n\n## Utils included\n\n#### notalib.array.as_chunks :fire:\n\nIterates over your array in chunks of at most N elements.\n\n```python\nfrom notalib.array import as_chunks\n\narr = [1,2,3,4,5]\nfor chunk in as_chunks(arr, 2):\n    print(chunk)\n# [1,2]\n# [3,4]\n# [5]\n```\n\n#### notalib.array.ensure_iterable :fire:\n\nKeeps iterable things like lists intact, turns single values into single-element lists. Useful for functions that can accept both.\n\n```python\nensure_iterable([1,2,3])  # --> [1,2,3]\nensure_iterable((1,2,3))  # --> (1,2,3)\nensure_iterable(1)        # --> [1]\nensure_iterable(\'smth\')   # --> [\'smth\']\n\ndef my_function(one_or_multiple_args):\n    for arg in ensure_iterable(one_or_multiple_args):\n        ...\n        \nmy_function([\'log\', \'smog\'])\nmy_function(\'dog\')\n```\n\n#### notalib.combinator.Combinator :fire:\n#### notalib.date.parse_month\n#### notalib.date.parse_date\n#### notalib.date.normalize_date :fire:\n#### <s>notalib.date.get_week_number</s>\n\n_Removed in 2.0.0. Use `get_week` instead. If you want the "old" week numbering, use get_week with `WeekNumbering.MATCH_YEAR` and add 1 to week number._\n\n#### notalib.date.get_week\n\nReturns named tuple with week number for the given date. Accepts Python dates and Arrow timestamps.\n\nOptional argument `mode` tells what to do if the week started in previous year:\n\n* WeekNumbering.NORMAL (default): consider it the last week of the previous year\n* WeekNumbering.MATCH_YEAR: consider it 0-th week of current year\n\n```python\nfrom notalib.date import get_week, WeekNumbering\nfrom datetime import date\n\ndate1, date2 = date(2022, 12, 31), date(2023, 1, 1)\nget_week(date1, WeekNumbering.NORMAL)\n# Week(week=52, year=2022)\n\nget_week(date1, WeekNumbering.MATCH_YEAR)\n# Week(week=52, year=2022)\n\nget_week(date2, WeekNumbering.NORMAL)\n# Week(week=52, year=2022)\n\nget_week(date2, WeekNumbering.MATCH_YEAR)\n# Week(week=0, year=2023)\n```\n\n#### notalib.dict.find_field\n#### notalib.dict.find_value\n#### notalib.dict.normalize_dict :fire:\n#### notalib.dict.filter_dict\n\nFilters a dictionary, removing any keys except for the ones you choose.\n\n```python\nfrom notalib.dict import filter_dict\n\nsrc = {\n\t\'Some\': "BODY",\n\t\'once\': "told me",\n\t\'the world\': "is gonna roll me",\n}\nfilter_dict(src, ["Some", "once"])\n# {\'Some\': \'BODY\', \'once\': \'told me\'}\nfilter_dict(src, [])\n# {}\n```\n\n#### notalib.format.format_long_list\n#### notalib.hypertext.strip_tags :fire:\n#### notalib.hypertext.TablePrinter :fire:\n\nPrints an HTML table, row by row, from the given data, using attrs or dictionary keys as columns.\n\nTwo ways to use it:\n\n* Call header() / entry() / footer() manually\n\n```python\nfrom notalib.hypertext import TablePrinter\nt = TablePrinter([\'a\', \'b\'])\nt.header()\n# \'<table><thead><tr><th>a</th><th>b</th></tr></thead><tbody>\'\nt.entry({\'a\': 1, \'b\': 2})\n# \'<tr><td>1</td><td>2</td></tr>\\n\'\nt.entry({\'a\': 11, \'b\': 22})\n# \'<tr><td>11</td><td>22</td></tr>\\n\'\nt.footer()\n# \'</tbody></table>\'\n```\n\n* Pass an iterable to iterator_over()\n\t\n```python\nfrom notalib.hypertext import TablePrinter\nt = TablePrinter([\'a\', \'b\'])\nlist(t.iterator_over([ {\'a\': 11, \'b\': 22} ]))\n# [\'<table><thead><tr><th>a</th><th>b</th></tr></thead><tbody>\',\n#  \'<tr><td>11</td><td>22</td></tr>\\n\',\n#  \'</tbody></table>\']\n```\n\n#### notalib.polosa.polosa :fire: :fire: :fire: :fire: :fire:\n\n```\n18023/2000000   294.8/sec   Processing transaction ID#84378473 (2020-01-04)\n```\n\nThe CLI progress indicator you\'ve always dreamt of: shows current and total if available, measures current speed, can show your comments for each element, makes sure not to slow down your terminal with frequent updates. [See this short demo](https://asciinema.org/a/UI1aOqjQC1KXx303kaVGrxjQp).\n\n_Cheat sheet_\n\n```python\n## Basic usage\nwith polosa() as p:\n    p.tick()\n# 467344   201.2/sec\n\n## Specify total number of elements:\nwith polosa(total=1337) as p:\n# 26/1337   1.2/sec\n\n## Print something useful about every element:\np.tick(caption=my_order.time_created)\n# 1723910/2000000   319231.2/sec   2020-01-01 15:37:00\n```\n\n#### notalib.range.Range\n#### notalib.time.Timing :fire:\n\nMeasures time spent on executing your code. Killer feature: it can be used as a reusable context.\n\n```python\ntiming = Timing()\n...\nwith timing:\n    do_something()\n# That\'s it, do something with the measurement\nlog(f\'Operation took {timing.result} sec\')\n```\n\nIf you just want to print measurements into console, there\'s a shorthand:\n\n```python\ntiming = Timing(auto_print=True)\n...\nwith timing:\n    do_something()\n```\n\n#### notalib.trendsetter.Trendsetter :fire:\n\n## Pandas-related\n\n#### notalib.pandas.pandasplus.row_to_dict\n#### notalib.pandas.pandasplus.replace_null_objects\n\nReplaces all types of null values in a DataFrame with the given value.\n\n```python\ndf = pd.DataFrame({\'A\': [pd.NA, pd.NaT, \'SomeVal\', None]})\nnew_df = replace_null_objects(df, "Hello, notalib!")\nnew_df\n#                  A\n# 0  Hello, notalib!\n# 1  Hello, notalib!\n# 2          SomeVal\n# 3  Hello, notalib!\n```\n\n## Django-related\n\n#### notalib.django.auth.StaticBackend\n#### notalib.django.auth.SettingsBackend\n\n#### notalib.django.xauth\n\nEndpoints for easier authentication in APIs. Requires Django REST framework.\n\nProvides endpoints:\n\n* `/xauth/check` — returns code 200 if client is authenticated (or global permissions are set to AllowAny), 403 if not\n* `/xauth/auth-post` — authenticates a client; accepts two POST parameters `username` and `password`; returns code 200 on success and 403 on failure\n\nHow to use:\n\n1. Make sure Django REST framework is installed.\n2. Add `\'notalib.django_xauth\'` to INSTALLED_APPS.\n3. Run `manage.py migrate django_xauth` (doesn\'t actually change your DB).\n4. Add something like this to your urls.py: `path(\'xauth/\', include(\'notalib.django_xauth.urls\')),`\n\n#### notalib.django.colorlog.ColorFormatter\n#### notalib.django.filterset :fire:\n#### notalib.django.formplus.MonthField\n#### notalib.django.formplus.ChoiceWithDefault\n#### notalib.django.formplus.IntegerArrayField\n#### notalib.django.formplus.StringArrayField\n#### notalib.django.formplus.MonthArrayField\n#### notalib.django.request_time_middleware.RequestTimeLoggingMiddleware\n#### notalib.django.stream.stream_json\n\nStream all elements of iterable object as JSON array using the StreamingHttpResponse class. Unlike DRF\'s Response class, it can handle arrays of any size.\n\n```python\nclass SomeViewSet(...):\n    ...\n    \n    def list(self, request, *args, **kwargs):\n        ...\n        return stream_json(data)\n```\n\n## Django/Clickhouse\n\n_Stage: alpha_\n\nRequired packages: `clickhouse-sqlalchemy`\n\nRequired two django.settings variables:\n\n* **CLICKHOUSE_URL** — database URL\n* **CLICKHOUSE_PROFILE** — dump all queries and their timing to console, true/false\n\n#### notalib.django.clickhouse.base.get_connection\n#### notalib.django.clickhouse.base.get_database_name\n#### notalib.django.clickhouse.base.Query\n\nA wrapper for SQLAlchemy\'s `select` with some useful postprocessing options.\n\n* `execute` — no postprocessing\n* `execute_val` — returns single value\n* `execute_list` — returns single column as list\n* `execute_kv` — returns dict, first column becomes keys, second column becomes values\n* `execute_na` — returns number of affected rows\n\nUsage example:\n\n```python\nq = Query(\n    select([ SomeTable.c.notalib ])\n)\nq.execute_list()\n# ["Example", "OOOOO", "my", "defence", ...]\n```\n\n#### notalib.django.clickhouse.mutations.get_mutations_in_progress_count\n\nReturns the number of mutations in progress for the specified table.\n\n```python\nget_mutations_in_progress_count("SOME_DATABASE", "SOME_TABLE_IN_DATABASE")\n# 5\n```\n\n#### notalib.django.clickhouse.wait.wait_result :fire:\n\nWaits until all mutations for the given table are complete.\n\n```python\n# blah blah blah, ALTER TABLE ... UPDATE ...\nwait_result("SOME_DATABASE", "SOME_TABLE_IN_DATABASE")\n# UPDATE complete, continue\n```\n',
    'author': 'm1kc (Max Musatov)',
    'author_email': 'm1kc@yandex.ru',
    'maintainer': 'None',
    'maintainer_email': 'None',
    'url': 'https://github.com/m1kc/notalib',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.7.3,<4.0.0',
}


setup(**setup_kwargs)
