tracktolib 0.35.0__py3-none-any.whl → 0.37.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.
tracktolib/pg/query.py CHANGED
@@ -3,6 +3,7 @@ from dataclasses import dataclass, field
3
3
  from typing import (
4
4
  TypeVar, Iterable, Callable, Generic, Iterator, TypeAlias,
5
5
  overload, Any, Literal)
6
+ from ..pg_utils import get_conflict_query
6
7
 
7
8
  try:
8
9
  import asyncpg
@@ -29,27 +30,11 @@ def _get_on_conflict_query(query: str,
29
30
  keys: Iterable[K],
30
31
  update_keys: Iterable[K] | None,
31
32
  ignore_keys: Iterable[K] | None,
32
- constraint: str | None,
33
- on_conflict: str | None) -> str:
34
- if on_conflict:
35
- return f'{query} {on_conflict}'
36
-
37
- if constraint:
38
- query = f'{query} ON CONFLICT ON CONSTRAINT {constraint}'
39
- elif update_keys:
40
- update_keys_str = ', '.join(sorted(update_keys))
41
- query = f'{query} ON CONFLICT ({update_keys_str})'
42
- else:
43
- raise NotImplementedError('update_keys or constraint must be set')
44
-
45
- _ignore_keys = [*(update_keys or []), *(ignore_keys or [])]
46
- fields = ', '.join(f'{x} = COALESCE(EXCLUDED.{x}, t.{x})'
47
- for x in keys
48
- if x not in _ignore_keys)
49
- if not fields:
50
- raise ValueError('No fields set')
51
-
52
- return f'{query} DO UPDATE SET {fields}'
33
+ constraint: K | None,
34
+ on_conflict: K | None) -> str:
35
+ _on_conflict = get_conflict_query(keys=keys, update_keys=update_keys, ignore_keys=ignore_keys,
36
+ constraint=constraint, on_conflict=on_conflict)
37
+ return f'{query} {_on_conflict}'
53
38
 
54
39
 
55
40
  ReturningFn = Callable[[Iterable[K] | None, K | None], None]
tracktolib/pg/utils.py CHANGED
@@ -3,8 +3,9 @@ import datetime as dt
3
3
  import functools
4
4
  import logging
5
5
  from pathlib import Path
6
- from typing import AsyncIterator
6
+ from typing import AsyncIterator, Iterable, Sequence
7
7
  from typing_extensions import LiteralString
8
+ from ..pg_utils import get_conflict_query
8
9
 
9
10
  try:
10
11
  import asyncpg
@@ -89,14 +90,27 @@ async def upsert_csv(conn: asyncpg.Connection,
89
90
  *,
90
91
  chunk_size: int = 5_000,
91
92
  show_progress: bool = False,
92
- nb_lines: int | None = None):
93
+ nb_lines: int | None = None,
94
+ on_conflict_keys: Iterable[LiteralString] | None = None,
95
+ delimiter: str = ',',
96
+ col_names: Sequence[str] | None = None,
97
+ skip_header: bool = False):
93
98
  infos = await get_table_infos(conn, schema, table)
94
99
 
100
+ on_conflict_str = 'ON CONFLICT DO NOTHING'
101
+ if on_conflict_keys is not None:
102
+ on_conflict_str = get_conflict_query(keys=infos.keys(),
103
+ update_keys=on_conflict_keys)
104
+
95
105
  with csv_path.open('r') as f:
96
- reader = csv.DictReader(f)
97
- _columns = [x.lower() for x in (reader.fieldnames or [])]
106
+ reader = csv.DictReader(f, delimiter=delimiter, fieldnames=col_names)
107
+ if skip_header:
108
+ next(reader)
109
+ _columns = col_names if col_names else [x.lower() for x in (reader.fieldnames or [])]
98
110
  async with conn.transaction():
99
- _tmp_table, _tmp_query, _insert_query = get_tmp_table_query(schema, table)
111
+ _tmp_table, _tmp_query, _insert_query = get_tmp_table_query(schema, table,
112
+ columns=infos.keys(),
113
+ on_conflict=on_conflict_str)
100
114
  logger.info(f'Creating tmp table: {_tmp_table!r}')
101
115
  await conn.execute(_tmp_query)
102
116
  logger.info(f'Inserting data from {csv_path!r} to {_tmp_table!r}')
tracktolib/pg_utils.py CHANGED
@@ -1,18 +1,58 @@
1
1
  from typing_extensions import LiteralString
2
+ from typing import Iterable
3
+ from typing import cast
2
4
 
3
5
 
4
6
  def get_tmp_table_query(schema: LiteralString,
5
- table: LiteralString):
7
+ table: LiteralString,
8
+ columns: Iterable[LiteralString] | None = None,
9
+ on_conflict: LiteralString = 'ON CONFLICT DO NOTHING'):
6
10
  tmp_table_name = f'{schema}_{table}_tmp'
7
11
  create_tmp_table_query = f"""
8
12
  CREATE TEMP TABLE {tmp_table_name}
9
13
  (LIKE {schema}.{table} INCLUDING DEFAULTS)
10
14
  ON COMMIT DROP;
11
15
  """
12
- insert_query = f"""
13
- INSERT INTO {schema}.{table}
14
- SELECT *
15
- FROM {tmp_table_name}
16
- ON CONFLICT DO NOTHING;
17
- """
16
+
17
+ if columns:
18
+ _columns = ','.join(columns)
19
+ insert_query = f"""
20
+ INSERT INTO {schema}.{table} as t({_columns})
21
+ SELECT *
22
+ FROM {tmp_table_name}
23
+ {on_conflict};
24
+ """
25
+ else:
26
+ insert_query = f"""
27
+ INSERT INTO {schema}.{table}
28
+ SELECT *
29
+ FROM {tmp_table_name}
30
+ {on_conflict};
31
+ """
18
32
  return tmp_table_name, create_tmp_table_query, insert_query
33
+
34
+
35
+ def get_conflict_query(keys: Iterable[str],
36
+ update_keys: Iterable[str] | None = None,
37
+ ignore_keys: Iterable[str] | None = None,
38
+ constraint: str | None = None,
39
+ on_conflict: str | None = None) -> LiteralString:
40
+ if on_conflict:
41
+ return cast(LiteralString, on_conflict)
42
+
43
+ if constraint:
44
+ query = f'ON CONFLICT ON CONSTRAINT {constraint}'
45
+ elif update_keys:
46
+ update_keys_str = ', '.join(sorted(update_keys))
47
+ query = f'ON CONFLICT ({update_keys_str})'
48
+ else:
49
+ raise NotImplementedError('update_keys or constraint must be set')
50
+
51
+ _ignore_keys = [*(update_keys or []), *(ignore_keys or [])]
52
+ fields = ', '.join(f'{x} = COALESCE(EXCLUDED.{x}, t.{x})'
53
+ for x in keys
54
+ if x not in _ignore_keys)
55
+ if not fields:
56
+ raise ValueError('No fields set')
57
+
58
+ return cast(LiteralString, f'{query} DO UPDATE SET {fields}')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tracktolib
3
- Version: 0.35.0
3
+ Version: 0.37.0
4
4
  Summary: Utility library for python
5
5
  Home-page: https://github.com/tracktor/tracktolib
6
6
  License: MIT
@@ -4,16 +4,16 @@ tracktolib/api.py,sha256=2hAtq7Cjx7GqiqO2ATJfiCK3rM66kDfLKMBVZ_I8ttU,6743
4
4
  tracktolib/http.py,sha256=GHWvD9rHTF1oAnG6Ptna_oeMDcwCxYYpPxLOME-oZ3Y,1574
5
5
  tracktolib/logs.py,sha256=nbI9LO2NaXDAv9aoUkad54V_x8B8zga-MwqUfYb-pkk,2523
6
6
  tracktolib/pg/__init__.py,sha256=mK_EAUN9wTLQC38n3iaBFTP2YzaEo7S17AyFiKHgu6k,241
7
- tracktolib/pg/query.py,sha256=6IfRqID_RIqnhW4cyZKsZNMA5t9QtXWay3Hllu3xolw,15398
8
- tracktolib/pg/utils.py,sha256=A7YVFKRwolxTP7S4T6XtR2vyj4oq3jwOITBJZtdDEys,4228
7
+ tracktolib/pg/query.py,sha256=E42F2pq_kv465XO2HQo52BhKhORsZ-mdvsGsqKdnbVg,14969
8
+ tracktolib/pg/utils.py,sha256=Yfd-6G5KfHbwIReYrymzdozFs8flQaP-R9K5CP42t_4,5059
9
9
  tracktolib/pg_sync.py,sha256=M_9DMVXWozhxD8KGVWKxLblOG4K-npbK5SKoO-Rz0yM,5203
10
- tracktolib/pg_utils.py,sha256=rWYCkuOk_Qq2ShNvbudEvcgFNKTu2vJd6PsUmElVu9Q,545
10
+ tracktolib/pg_utils.py,sha256=e5QdJ5clFtap949U57sSjplaWi6LNv-Rs2cuz4NRIxk,2012
11
11
  tracktolib/s3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  tracktolib/s3/minio.py,sha256=gkpI2bvVwfXaAFY7M9e0Mlv82x0PjuriS8LdRmEJtMo,1258
13
13
  tracktolib/s3/s3.py,sha256=Kfmz0uLl5yGQxH4XvAfIe1yhOieTXS9Ck85nhj_aMnQ,4011
14
14
  tracktolib/tests.py,sha256=GycCVRUOdzazxqBXKBWWzhrlkV8yyD7NaActz_A2WXU,590
15
15
  tracktolib/utils.py,sha256=kU35LCJaFDNg0IR9ekAi4uEJ3unZCC3VPzqgkctt06Q,5437
16
- tracktolib-0.35.0.dist-info/LICENSE,sha256=uUanH0X7SeZEPdsRTHegMSMTiIHMurt9H0jSwEwKE1Y,1081
17
- tracktolib-0.35.0.dist-info/WHEEL,sha256=vxFmldFsRN_Hx10GDvsdv1wroKq8r5Lzvjp6GZ4OO8c,88
18
- tracktolib-0.35.0.dist-info/METADATA,sha256=6nszCklyCJfTvWH4_HYIVOkIqQL4nVwiSsQPtVgbGZk,3630
19
- tracktolib-0.35.0.dist-info/RECORD,,
16
+ tracktolib-0.37.0.dist-info/LICENSE,sha256=uUanH0X7SeZEPdsRTHegMSMTiIHMurt9H0jSwEwKE1Y,1081
17
+ tracktolib-0.37.0.dist-info/WHEEL,sha256=vxFmldFsRN_Hx10GDvsdv1wroKq8r5Lzvjp6GZ4OO8c,88
18
+ tracktolib-0.37.0.dist-info/METADATA,sha256=Fu_O-M1gdC3VCihdzJN3PVTRx9VjkO5TacWTLfNQC4c,3630
19
+ tracktolib-0.37.0.dist-info/RECORD,,