spinta 0.2.dev21__py3-none-any.whl → 0.2.dev23__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.
- spinta/auth.py +33 -26
- spinta/cli/admin.py +12 -8
- spinta/cli/helpers/admin/components.py +1 -0
- spinta/cli/helpers/admin/registry.py +4 -0
- spinta/cli/helpers/admin/scripts/enums.py +148 -0
- spinta/cli/helpers/message.py +6 -2
- spinta/cli/helpers/script/components.py +1 -1
- spinta/cli/helpers/script/core.py +5 -7
- spinta/cli/helpers/script/helpers.py +2 -2
- spinta/cli/helpers/upgrade/scripts/backends/postgresql/schemas.py +3 -2
- spinta/components.py +2 -1
- spinta/config.py +2 -1
- spinta/datasets/backends/dataframe/backends/json/commands/read.py +1 -1
- spinta/datasets/backends/dataframe/backends/soap/commands/read.py +13 -1
- spinta/datasets/backends/dataframe/commands/read.py +5 -0
- spinta/datasets/backends/dataframe/ufuncs/query/ufuncs.py +21 -6
- spinta/datasets/backends/helpers.py +25 -2
- spinta/exceptions.py +6 -0
- spinta/testing/client.py +0 -2
- spinta/testing/csv.py +7 -2
- spinta/types/config.py +5 -0
- spinta/types/partial/link.py +30 -15
- spinta/types/ref/link.py +3 -1
- {spinta-0.2.dev21.dist-info → spinta-0.2.dev23.dist-info}/METADATA +1 -1
- {spinta-0.2.dev21.dist-info → spinta-0.2.dev23.dist-info}/RECORD +28 -27
- {spinta-0.2.dev21.dist-info → spinta-0.2.dev23.dist-info}/WHEEL +0 -0
- {spinta-0.2.dev21.dist-info → spinta-0.2.dev23.dist-info}/entry_points.txt +0 -0
- {spinta-0.2.dev21.dist-info → spinta-0.2.dev23.dist-info}/licenses/LICENSE +0 -0
spinta/auth.py
CHANGED
|
@@ -58,6 +58,7 @@ from spinta.exceptions import (
|
|
|
58
58
|
InvalidClientsKeymapStructure,
|
|
59
59
|
InvalidScopes,
|
|
60
60
|
InvalidClientFileFormat,
|
|
61
|
+
ModelNotFound,
|
|
61
62
|
)
|
|
62
63
|
from spinta.utils import passwords
|
|
63
64
|
from spinta.utils.config import get_clients_path, get_keymap_path, get_id_path, get_helpers_path
|
|
@@ -858,40 +859,46 @@ def authorized(
|
|
|
858
859
|
scope_formatter: ScopeFormatterFunc = None,
|
|
859
860
|
):
|
|
860
861
|
config: Config = context.get("config")
|
|
862
|
+
|
|
863
|
+
# Disable access to nodes that have lower access level than config.access
|
|
864
|
+
if config.access > node.access:
|
|
865
|
+
if throw:
|
|
866
|
+
raise ModelNotFound(model=node.name)
|
|
867
|
+
return False
|
|
868
|
+
|
|
861
869
|
token = context.get("auth.token")
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
870
|
+
|
|
871
|
+
# Unauthenticated clients can only access nodes if spinta config.access is open.
|
|
872
|
+
unauthenticated = token.get_client_id() == get_default_auth_client_id(context)
|
|
873
|
+
|
|
874
|
+
if unauthenticated and config.access < Access.open:
|
|
866
875
|
if throw:
|
|
867
876
|
raise AuthorizedClientsOnly()
|
|
868
877
|
else:
|
|
869
878
|
return False
|
|
870
879
|
|
|
871
|
-
#
|
|
880
|
+
# Add explicit node scope
|
|
872
881
|
scopes = [node]
|
|
873
882
|
|
|
874
|
-
#
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
if ns:
|
|
894
|
-
scopes.extend(ns.parents())
|
|
883
|
+
# Add parent node scopes
|
|
884
|
+
ns = None
|
|
885
|
+
if isinstance(node, Property):
|
|
886
|
+
# Hidden nodes also require explicit scope.
|
|
887
|
+
# XXX: `hidden` parameter should only be used for API control, not
|
|
888
|
+
# access control. See docs.
|
|
889
|
+
if not node.hidden:
|
|
890
|
+
scopes.append(node.model)
|
|
891
|
+
scopes.append(node.model.ns)
|
|
892
|
+
ns = node.model.ns
|
|
893
|
+
elif isinstance(node, Model):
|
|
894
|
+
scopes.append(node.ns)
|
|
895
|
+
ns = node.ns
|
|
896
|
+
elif isinstance(node, Namespace):
|
|
897
|
+
ns = node
|
|
898
|
+
|
|
899
|
+
# Add all parent namespace scopes too.
|
|
900
|
+
if ns:
|
|
901
|
+
scopes.extend(ns.parents())
|
|
895
902
|
|
|
896
903
|
# Build scope names.
|
|
897
904
|
scope_formatter = scope_formatter or config.scope_formatter
|
spinta/cli/admin.py
CHANGED
|
@@ -2,15 +2,14 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import pathlib
|
|
5
|
-
import sys
|
|
6
5
|
from typing import Optional, List
|
|
7
6
|
|
|
8
7
|
from typer import Context as TyperContext, Argument
|
|
9
8
|
from typer import Option
|
|
10
|
-
from typer import echo
|
|
11
9
|
|
|
12
10
|
from spinta.cli.helpers.admin.components import ADMIN_SCRIPT_TYPE
|
|
13
11
|
from spinta.cli.helpers.admin.registry import admin_script_registry
|
|
12
|
+
from spinta.cli.helpers.message import cli_error
|
|
14
13
|
from spinta.cli.helpers.script.components import ScriptStatusCache
|
|
15
14
|
from spinta.cli.helpers.script.core import run_specific_script
|
|
16
15
|
from spinta.cli.helpers.script.helpers import sort_scripts_by_required
|
|
@@ -48,26 +47,30 @@ def admin(
|
|
|
48
47
|
False,
|
|
49
48
|
"-c",
|
|
50
49
|
"--check",
|
|
51
|
-
help=
|
|
50
|
+
help="Only runs script checks, skipping execution part (used to find out what scripts are needed to run).",
|
|
52
51
|
),
|
|
53
52
|
input_path: Optional[pathlib.Path] = Option(
|
|
54
53
|
None,
|
|
55
54
|
"-i",
|
|
56
55
|
"--input",
|
|
57
|
-
help=
|
|
56
|
+
help="Path to input file (some scripts might require extra data). If not given, script will read from stdin.",
|
|
57
|
+
),
|
|
58
|
+
output_path: Optional[pathlib.Path] = Option(
|
|
59
|
+
None,
|
|
60
|
+
"-o",
|
|
61
|
+
"--output",
|
|
62
|
+
help="Path to output file (some scripts might write extra data). If not given, script will write to stdout.",
|
|
58
63
|
),
|
|
59
64
|
):
|
|
60
65
|
context = configure_context(ctx.obj)
|
|
61
66
|
|
|
62
67
|
if force and check_only:
|
|
63
|
-
|
|
64
|
-
sys.exit(1)
|
|
68
|
+
cli_error("Cannot run force mode with check only mode")
|
|
65
69
|
|
|
66
70
|
load_config(context, ensure_config_dir=ensure_config_dir)
|
|
67
71
|
|
|
68
72
|
if not scripts:
|
|
69
|
-
|
|
70
|
-
sys.exit(1)
|
|
73
|
+
cli_error("At least one script needs to be specified")
|
|
71
74
|
|
|
72
75
|
script_objects = {}
|
|
73
76
|
for script in scripts:
|
|
@@ -86,5 +89,6 @@ def admin(
|
|
|
86
89
|
script_name=script,
|
|
87
90
|
check_only=check_only,
|
|
88
91
|
input_path=input_path,
|
|
92
|
+
output_path=output_path,
|
|
89
93
|
status_cache=status_cache,
|
|
90
94
|
)
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from spinta.cli.helpers.admin.components import AdminScript, Script, ADMIN_SCRIPT_TYPE
|
|
4
4
|
from spinta.cli.helpers.admin.scripts.changelog import migrate_changelog_duplicates, cli_requires_changelog_migrations
|
|
5
5
|
from spinta.cli.helpers.admin.scripts.deduplicate import migrate_duplicates, cli_requires_deduplicate_migrations
|
|
6
|
+
from spinta.cli.helpers.admin.scripts.enums import gather_invalid_enum_values
|
|
6
7
|
from spinta.cli.helpers.script.components import ScriptTarget, ScriptTag
|
|
7
8
|
from spinta.cli.helpers.script.registry import script_registry
|
|
8
9
|
from spinta.cli.helpers.upgrade.components import Script as UpgradeScript, UPGRADE_SCRIPT_TYPE
|
|
@@ -29,3 +30,6 @@ script_registry.register(
|
|
|
29
30
|
targets={ScriptTarget.BACKEND.value},
|
|
30
31
|
)
|
|
31
32
|
)
|
|
33
|
+
script_registry.register(
|
|
34
|
+
AdminScript(name=Script.ENUM_LIST.value, run=gather_invalid_enum_values, targets={ScriptTarget.BACKEND.value})
|
|
35
|
+
)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import pathlib
|
|
3
|
+
import sys
|
|
4
|
+
from contextlib import nullcontext
|
|
5
|
+
from typing import Iterator
|
|
6
|
+
|
|
7
|
+
from multipledispatch import dispatch
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
|
|
10
|
+
from spinta import commands
|
|
11
|
+
from spinta.backends import Backend
|
|
12
|
+
from spinta.backends.postgresql.components import PostgreSQL
|
|
13
|
+
from spinta.cli.helpers.script.helpers import ensure_store_is_loaded
|
|
14
|
+
from spinta.components import Context, Model, Property
|
|
15
|
+
from spinta.core.ufuncs import Expr
|
|
16
|
+
from spinta.manifests.components import Manifest
|
|
17
|
+
|
|
18
|
+
import sqlalchemy as sa
|
|
19
|
+
|
|
20
|
+
from spinta.ufuncs.resultbuilder.components import EnumResultBuilder
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dispatch(Backend, Property)
|
|
24
|
+
def gather_unique_property_values() -> list:
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dispatch(PostgreSQL, Property)
|
|
29
|
+
def gather_unique_property_values(backend: PostgreSQL, prop: Property) -> list:
|
|
30
|
+
table = backend.get_table(prop)
|
|
31
|
+
column = backend.get_column(table, prop)
|
|
32
|
+
result = backend.engine.execute(sa.select(column).distinct()).scalars().all()
|
|
33
|
+
return result
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dispatch(Property)
|
|
37
|
+
def gather_unique_property_values(prop: Property) -> list:
|
|
38
|
+
return gather_unique_property_values(prop.dtype.backend, prop)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_models_with_enums(context: Context, manifest: Manifest) -> Iterator[Model]:
|
|
42
|
+
for model in commands.get_models(context, manifest).values():
|
|
43
|
+
for prop in model.flatprops.values():
|
|
44
|
+
if prop.enum:
|
|
45
|
+
yield model
|
|
46
|
+
break
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class InvalidEnumProperty:
|
|
51
|
+
prop: Property
|
|
52
|
+
invalid_values: list = field(default_factory=list)
|
|
53
|
+
|
|
54
|
+
def add_invalid_value(self, value: object):
|
|
55
|
+
if value not in self.invalid_values:
|
|
56
|
+
self.invalid_values.append(value)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class InvalidEnumModel:
|
|
61
|
+
model: Model
|
|
62
|
+
enum_props: dict[str, InvalidEnumProperty] = field(default_factory=dict)
|
|
63
|
+
|
|
64
|
+
def add_invalid_value(self, prop: Property, value: object):
|
|
65
|
+
self.get_prop(prop).add_invalid_value(value)
|
|
66
|
+
|
|
67
|
+
def get_prop(self, prop: Property) -> InvalidEnumProperty:
|
|
68
|
+
if prop.place not in self.enum_props:
|
|
69
|
+
self.enum_props[prop.place] = InvalidEnumProperty(prop=prop)
|
|
70
|
+
return self.enum_props[prop.place]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def gather_invalid_enum_values(context: Context, output_path: pathlib.Path | None = None, **kwargs):
|
|
74
|
+
store = ensure_store_is_loaded(context)
|
|
75
|
+
manifest = store.manifest
|
|
76
|
+
invalid_models = {}
|
|
77
|
+
with context:
|
|
78
|
+
for model in get_models_with_enums(context, manifest):
|
|
79
|
+
enum_model = InvalidEnumModel(model=model)
|
|
80
|
+
for prop in model.flatprops.values():
|
|
81
|
+
if not prop.enum:
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
enum_contains_expr = any(isinstance(enum.prepare, Expr) for enum in prop.enum.values())
|
|
85
|
+
values = gather_unique_property_values(prop)
|
|
86
|
+
for value in values:
|
|
87
|
+
if value is None and not prop.dtype.required:
|
|
88
|
+
continue
|
|
89
|
+
elif value is None:
|
|
90
|
+
enum_model.add_invalid_value(prop, value)
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
if str(value) in prop.enum:
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
check_value = value
|
|
97
|
+
if enum_contains_expr:
|
|
98
|
+
for enum in prop.enum.values():
|
|
99
|
+
env = EnumResultBuilder(context).init(value)
|
|
100
|
+
val = env.resolve(enum.prepare)
|
|
101
|
+
if env.has_value_changed:
|
|
102
|
+
check_value = val
|
|
103
|
+
break
|
|
104
|
+
|
|
105
|
+
if str(check_value) not in prop.enum:
|
|
106
|
+
enum_model.add_invalid_value(prop, value)
|
|
107
|
+
|
|
108
|
+
if enum_model.enum_props:
|
|
109
|
+
invalid_models[model.model_type()] = enum_model
|
|
110
|
+
|
|
111
|
+
output_invalid_enums_to_csv(invalid_models, output_path)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def output_invalid_enums_to_csv(invalid_models: dict, output_path: pathlib.Path | None = None):
|
|
115
|
+
stream_ctx = open(output_path, "w") if output_path else nullcontext(sys.stdout)
|
|
116
|
+
with stream_ctx as stream:
|
|
117
|
+
writer = csv.DictWriter(stream, fieldnames=["model", "property", "invalid_value"], lineterminator="\n")
|
|
118
|
+
writer.writeheader()
|
|
119
|
+
for model_key, model in sorted(invalid_models.items()):
|
|
120
|
+
model_written = False
|
|
121
|
+
|
|
122
|
+
for prop_name, prop in model.enum_props.items():
|
|
123
|
+
it = iter(prop.invalid_values)
|
|
124
|
+
first = next(it, None)
|
|
125
|
+
if first is None:
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
model_name = ""
|
|
129
|
+
if not model_written:
|
|
130
|
+
model_name = model_key
|
|
131
|
+
model_written = True
|
|
132
|
+
|
|
133
|
+
writer.writerow(
|
|
134
|
+
{
|
|
135
|
+
"model": model_name,
|
|
136
|
+
"property": prop_name,
|
|
137
|
+
"invalid_value": first,
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
for value in it:
|
|
142
|
+
writer.writerow(
|
|
143
|
+
{
|
|
144
|
+
"model": "",
|
|
145
|
+
"property": "",
|
|
146
|
+
"invalid_value": value,
|
|
147
|
+
}
|
|
148
|
+
)
|
spinta/cli/helpers/message.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
1
3
|
import tqdm
|
|
2
4
|
from click import echo
|
|
3
5
|
from click.exceptions import Exit
|
|
@@ -9,7 +11,9 @@ def cli_error(message: str):
|
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
def cli_message(message: str, progress_bar: tqdm.tqdm = None):
|
|
14
|
+
# https://pubs.opengroup.org/onlinepubs/9799919799/
|
|
15
|
+
# This documentation states that `stderr` should be used for diagnostic messages (in our case status)
|
|
12
16
|
if progress_bar is not None:
|
|
13
|
-
progress_bar.write(message)
|
|
17
|
+
progress_bar.write(message, file=sys.stderr)
|
|
14
18
|
else:
|
|
15
|
-
echo(message)
|
|
19
|
+
echo(message, err=True)
|
|
@@ -31,7 +31,7 @@ class _ScriptMeta(type):
|
|
|
31
31
|
|
|
32
32
|
class ScriptBase(metaclass=_ScriptMeta):
|
|
33
33
|
"""
|
|
34
|
-
Represents
|
|
34
|
+
Represents a script with optional preconditions and target constraints.
|
|
35
35
|
|
|
36
36
|
Targets and tags are mainly used to be able to filter specific scripts. Main use case would be to set target as
|
|
37
37
|
specific object, like sqlalchemy keymap and tag as migration, so then you could filter all database migration
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
3
|
+
from spinta.cli.helpers.message import cli_message
|
|
5
4
|
from spinta.cli.helpers.script.components import ScriptStatus, ScriptBase, ScriptStatusCache
|
|
6
5
|
from spinta.cli.helpers.script.helpers import sort_scripts_by_required, script_check_status_message
|
|
7
6
|
from spinta.cli.helpers.script.registry import script_registry
|
|
@@ -54,7 +53,7 @@ def run_specific_script(
|
|
|
54
53
|
if force:
|
|
55
54
|
status = ScriptStatus.FORCED
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
cli_message(script_check_status_message(script_name, status))
|
|
58
57
|
if status in (ScriptStatus.FORCED, ScriptStatus.REQUIRED) and not check_only:
|
|
59
58
|
script.run(context, destructive=destructive, **kwargs)
|
|
60
59
|
if status_cache is not None:
|
|
@@ -73,7 +72,7 @@ def check_script(
|
|
|
73
72
|
script = script.value
|
|
74
73
|
|
|
75
74
|
if not script_registry.contains(script_type, script):
|
|
76
|
-
|
|
75
|
+
cli_message(f"Warning: {script_type!r} script {script!r} was not found")
|
|
77
76
|
return ScriptStatus.SKIPPED
|
|
78
77
|
|
|
79
78
|
script = script_registry.get(script_type, script)
|
|
@@ -92,9 +91,8 @@ def check_script(
|
|
|
92
91
|
ScriptStatus.REQUIRED,
|
|
93
92
|
ScriptStatus.SKIPPED,
|
|
94
93
|
):
|
|
95
|
-
|
|
96
|
-
f"Warning: {
|
|
97
|
-
err=True,
|
|
94
|
+
cli_message(
|
|
95
|
+
f"Warning: {script_type!r} script {required_script!r} requirement is not met for {script.name!r} script",
|
|
98
96
|
)
|
|
99
97
|
return ScriptStatus.SKIPPED
|
|
100
98
|
|
|
@@ -2,8 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from collections import defaultdict, deque
|
|
4
4
|
|
|
5
|
-
from typer import echo
|
|
6
5
|
|
|
6
|
+
from spinta.cli.helpers.message import cli_message
|
|
7
7
|
from spinta.cli.helpers.script.components import ScriptStatus
|
|
8
8
|
from spinta.cli.helpers.upgrade.components import UpgradeScript
|
|
9
9
|
from spinta.components import Context, Store
|
|
@@ -44,7 +44,7 @@ def sort_scripts_by_required(scripts: dict[str, UpgradeScript]) -> dict:
|
|
|
44
44
|
|
|
45
45
|
if len(result) != len(data):
|
|
46
46
|
unresolved = set(data) - set(result)
|
|
47
|
-
|
|
47
|
+
cli_message(f"Warning: Dependency cycle detected or unresolved dependencies in: {unresolved}")
|
|
48
48
|
# Extend results, potentially might cause errors, because of cycles
|
|
49
49
|
result.extend(unresolved)
|
|
50
50
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
from contextlib import ExitStack
|
|
2
2
|
from typing import Generator
|
|
3
3
|
|
|
4
|
-
from alembic.operations import Operations
|
|
5
|
-
from alembic.runtime.migration import MigrationContext
|
|
6
4
|
from click import echo
|
|
7
5
|
from sqlalchemy.engine import Inspector
|
|
8
6
|
from tqdm import tqdm
|
|
@@ -105,6 +103,9 @@ def gather_associated_tables(backend: PostgreSQL, table_identifier: TableIdentif
|
|
|
105
103
|
|
|
106
104
|
|
|
107
105
|
def migrate_schemas(context: Context, **kwargs):
|
|
106
|
+
from alembic.operations import Operations
|
|
107
|
+
from alembic.runtime.migration import MigrationContext
|
|
108
|
+
|
|
108
109
|
ensure_store_is_loaded(context)
|
|
109
110
|
store = context.get("store")
|
|
110
111
|
|
spinta/components.py
CHANGED
|
@@ -1095,7 +1095,8 @@ class Config:
|
|
|
1095
1095
|
scope_log: bool
|
|
1096
1096
|
check_contract_scopes: bool
|
|
1097
1097
|
default_auth_client: str
|
|
1098
|
-
default_access_level:
|
|
1098
|
+
default_access_level: Access
|
|
1099
|
+
access: Access
|
|
1099
1100
|
http_basic_auth: bool
|
|
1100
1101
|
token_validation_key: dict | None = None
|
|
1101
1102
|
token_validation_keys_download_url: str | None = None
|
spinta/config.py
CHANGED
|
@@ -344,7 +344,8 @@ CONFIG = {
|
|
|
344
344
|
},
|
|
345
345
|
"config_path": pathlib.Path("tests/config"),
|
|
346
346
|
"default_auth_client": "baa448a8-205c-4faa-a048-a10e4b32a136",
|
|
347
|
-
"default_access_level": "
|
|
347
|
+
"default_access_level": "open",
|
|
348
|
+
"access": "open",
|
|
348
349
|
"sync_retry_count": 0,
|
|
349
350
|
},
|
|
350
351
|
},
|
|
@@ -164,7 +164,7 @@ def getall(
|
|
|
164
164
|
builder.update(model=model, params={param.name: param for param in resource.params}, url_query_params=query)
|
|
165
165
|
|
|
166
166
|
props = {}
|
|
167
|
-
for prop in model.
|
|
167
|
+
for prop in model.flatprops.values():
|
|
168
168
|
if prop.external and prop.external.name:
|
|
169
169
|
root_source = _get_prop_full_source(model.external.name, prop.external.name)
|
|
170
170
|
props[prop.external.name] = {
|
|
@@ -21,7 +21,9 @@ from spinta.typing import ObjectData
|
|
|
21
21
|
from spinta.ufuncs.querybuilder.components import QueryParams
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def _get_data_soap(
|
|
24
|
+
def _get_data_soap(
|
|
25
|
+
url: str, backend: Soap, soap_request_body: dict, extra_headers: dict, source: str | None = None
|
|
26
|
+
) -> list[dict]:
|
|
25
27
|
for key, value in soap_request_body.items():
|
|
26
28
|
if isinstance(value, MakeCDATA):
|
|
27
29
|
soap_request_body[key] = value()
|
|
@@ -38,6 +40,15 @@ def _get_data_soap(url: str, backend: Soap, soap_request_body: dict, extra_heade
|
|
|
38
40
|
except zeep.exceptions.Error as e:
|
|
39
41
|
raise UnexpectedErrorReadingData(exception=type(e).__name__, message=str(e))
|
|
40
42
|
|
|
43
|
+
if isinstance(response_data, dict):
|
|
44
|
+
for part in source.split("/"):
|
|
45
|
+
if not part:
|
|
46
|
+
continue
|
|
47
|
+
if part in response_data:
|
|
48
|
+
response_data = response_data[part]
|
|
49
|
+
else:
|
|
50
|
+
response_data = []
|
|
51
|
+
break
|
|
41
52
|
if response_data and not isinstance(response_data, list):
|
|
42
53
|
response_data = [response_data]
|
|
43
54
|
|
|
@@ -137,6 +148,7 @@ def getall(
|
|
|
137
148
|
backend=backend,
|
|
138
149
|
soap_request_body=builder.soap_request_body,
|
|
139
150
|
extra_headers=http_headers,
|
|
151
|
+
source=model.external.name,
|
|
140
152
|
)
|
|
141
153
|
.flatten()
|
|
142
154
|
.to_dataframe(meta=meta)
|
|
@@ -276,6 +276,11 @@ def dask_get_all(
|
|
|
276
276
|
val = _get_row_value(context, row, sel, env.params)
|
|
277
277
|
if sel.prop:
|
|
278
278
|
if isinstance(sel.prop.dtype, PrimaryKey):
|
|
279
|
+
if isinstance(val, list):
|
|
280
|
+
val = [
|
|
281
|
+
list_value.get("_id", list_value) if isinstance(list_value, dict) else list_value
|
|
282
|
+
for list_value in val
|
|
283
|
+
]
|
|
279
284
|
val = keymap.encode(sel.prop.model.model_type(), val)
|
|
280
285
|
elif isinstance(sel.prop.dtype, Ref):
|
|
281
286
|
val = handle_ref_key_assignment(context, keymap, env, val, sel.prop.dtype)
|
|
@@ -336,12 +336,15 @@ def select(env: DaskDataFrameQueryBuilder, selected: Selected) -> Selected:
|
|
|
336
336
|
|
|
337
337
|
@ufunc.resolver(DaskDataFrameQueryBuilder, Ref, GetAttr)
|
|
338
338
|
def select(env: DaskDataFrameQueryBuilder, dtype: Ref, prep: GetAttr) -> Selected | None:
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
)
|
|
339
|
+
resolved_prep = env.call("select", prep)
|
|
340
|
+
|
|
341
|
+
result = {}
|
|
342
|
+
result["_id"] = Selected(prop=dtype.prop, prep=resolved_prep)
|
|
343
|
+
for prop in dtype.properties.values():
|
|
344
|
+
sel = env.call("select", prop)
|
|
345
|
+
result[prop.name] = sel
|
|
346
|
+
|
|
347
|
+
return Selected(prop=dtype.prop, prep=result)
|
|
345
348
|
|
|
346
349
|
|
|
347
350
|
@ufunc.resolver(DaskDataFrameQueryBuilder, Ref, object)
|
|
@@ -353,6 +356,18 @@ def select(env: DaskDataFrameQueryBuilder, dtype: Ref, prep: Any) -> Selected:
|
|
|
353
356
|
)
|
|
354
357
|
|
|
355
358
|
|
|
359
|
+
@ufunc.resolver(DaskDataFrameQueryBuilder, Ref)
|
|
360
|
+
def select(env: DaskDataFrameQueryBuilder, dtype: Ref) -> Selected:
|
|
361
|
+
prep = {}
|
|
362
|
+
prep["_id"] = Selected(item=dtype.prop.external.name, prop=dtype.prop)
|
|
363
|
+
|
|
364
|
+
for prop in dtype.properties.values():
|
|
365
|
+
sel = env.call("select", prop)
|
|
366
|
+
prep[prop.name] = sel
|
|
367
|
+
|
|
368
|
+
return Selected(prop=dtype.prop, prep=prep)
|
|
369
|
+
|
|
370
|
+
|
|
356
371
|
@ufunc.resolver(DaskDataFrameQueryBuilder, GetAttr)
|
|
357
372
|
def select(env: DaskDataFrameQueryBuilder, attr: GetAttr) -> Selected:
|
|
358
373
|
resolved = env.resolve_property(attr)
|
|
@@ -14,6 +14,10 @@ from spinta.utils.schema import NA
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def handle_ref_key_assignment(context: Context, keymap: KeyMap, env: Env, value: Any, ref: Ref) -> dict:
|
|
17
|
+
original_value = value
|
|
18
|
+
if isinstance(value, dict):
|
|
19
|
+
value = value["_id"]
|
|
20
|
+
|
|
17
21
|
keymap_name = ref.model.model_type()
|
|
18
22
|
if ref.refprops != ref.model.external.pkeys:
|
|
19
23
|
keymap_name = f"{keymap_name}.{'_'.join(prop.name for prop in ref.refprops)}"
|
|
@@ -55,7 +59,16 @@ def handle_ref_key_assignment(context: Context, keymap: KeyMap, env: Env, value:
|
|
|
55
59
|
|
|
56
60
|
# FIXME Quick hack when trying to get `Internal` model keys while running in `External` mode (should probably return error, or None)
|
|
57
61
|
if ref_model.mode == Mode.external and not check_if_model_has_backend_and_source(ref_model):
|
|
58
|
-
|
|
62
|
+
val = {"_id": keymap.encode(keymap_name, target_value)}
|
|
63
|
+
if isinstance(original_value, dict):
|
|
64
|
+
for nested_prop_name, nested_prop in ref.properties.items():
|
|
65
|
+
nested_value = original_value[nested_prop_name]
|
|
66
|
+
if isinstance(nested_value, dict):
|
|
67
|
+
nested_value = handle_ref_key_assignment(
|
|
68
|
+
context, keymap, env, nested_value, nested_prop.dtype
|
|
69
|
+
)
|
|
70
|
+
val[nested_prop_name] = nested_value
|
|
71
|
+
return val
|
|
59
72
|
|
|
60
73
|
expr_parts = ["select()"]
|
|
61
74
|
for i, prop in enumerate(ref.refprops):
|
|
@@ -67,7 +80,10 @@ def handle_ref_key_assignment(context: Context, keymap: KeyMap, env: Env, value:
|
|
|
67
80
|
for row in rows:
|
|
68
81
|
if val is not None:
|
|
69
82
|
raise MultiplePrimaryKeyCandidatesFound(ref, values=target_value)
|
|
70
|
-
|
|
83
|
+
if "_id" in row:
|
|
84
|
+
val = row["_id"]
|
|
85
|
+
else:
|
|
86
|
+
val = keymap.encode(keymap_name, target_value)
|
|
71
87
|
found_value = True
|
|
72
88
|
|
|
73
89
|
if not found_value:
|
|
@@ -84,6 +100,13 @@ def handle_ref_key_assignment(context: Context, keymap: KeyMap, env: Env, value:
|
|
|
84
100
|
values = values[0]
|
|
85
101
|
val[prop] = values
|
|
86
102
|
i = i + count
|
|
103
|
+
|
|
104
|
+
if isinstance(original_value, dict):
|
|
105
|
+
for nested_prop_name, nested_prop in ref.properties.items():
|
|
106
|
+
nested_value = original_value[nested_prop_name]
|
|
107
|
+
if isinstance(nested_value, dict):
|
|
108
|
+
nested_value = handle_ref_key_assignment(context, keymap, env, nested_value, nested_prop.dtype)
|
|
109
|
+
val[nested_prop_name] = nested_value
|
|
87
110
|
return val
|
|
88
111
|
|
|
89
112
|
|
spinta/exceptions.py
CHANGED
|
@@ -1223,3 +1223,9 @@ class SourceOrPrepareNotAllowed(UserError):
|
|
|
1223
1223
|
template = """
|
|
1224
1224
|
The source {source} was not expected. Delete it from the manifest or update the prepare function to allow it.
|
|
1225
1225
|
"""
|
|
1226
|
+
|
|
1227
|
+
|
|
1228
|
+
class PartialIncorrectProperty(BaseError):
|
|
1229
|
+
template = (
|
|
1230
|
+
"The composite property {property} is not correct. Check if all parts of the composite property are present."
|
|
1231
|
+
)
|
spinta/testing/client.py
CHANGED
|
@@ -94,7 +94,6 @@ def create_remote_server(
|
|
|
94
94
|
{
|
|
95
95
|
"config_path": confdir,
|
|
96
96
|
"default_auth_client": None,
|
|
97
|
-
"default_access_level": "protected",
|
|
98
97
|
}
|
|
99
98
|
)
|
|
100
99
|
context = create_test_context(rc)
|
|
@@ -294,7 +293,6 @@ def create_rc(
|
|
|
294
293
|
},
|
|
295
294
|
# tests/config/clients/3388ea36-4a4f-4821-900a-b574c8829d52.yml
|
|
296
295
|
"default_auth_client": "3388ea36-4a4f-4821-900a-b574c8829d52",
|
|
297
|
-
"default_access_level": "protected",
|
|
298
296
|
}
|
|
299
297
|
)
|
|
300
298
|
|
spinta/testing/csv.py
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import pathlib
|
|
2
2
|
|
|
3
3
|
from requests.models import Response
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def parse_csv(resp: Response) ->
|
|
6
|
+
def parse_csv(resp: Response) -> list[list[str]]:
|
|
7
7
|
resp.raise_for_status()
|
|
8
8
|
return [line.strip().split(",") for line in resp.text.splitlines()]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def read_csv(path: pathlib.Path) -> list[list[str]]:
|
|
12
|
+
with path.open("r") as f:
|
|
13
|
+
return [line.strip().split(",") for line in f.readlines()]
|
spinta/types/config.py
CHANGED
|
@@ -83,6 +83,11 @@ def load(context: Context, config: Config) -> Config:
|
|
|
83
83
|
default="private",
|
|
84
84
|
cast=lambda name: get_enum_by_name(Access, name),
|
|
85
85
|
)
|
|
86
|
+
config.access = rc.get(
|
|
87
|
+
"access",
|
|
88
|
+
default="open",
|
|
89
|
+
cast=lambda name: get_enum_by_name(Access, name),
|
|
90
|
+
)
|
|
86
91
|
config.http_basic_auth = rc.get("http_basic_auth", default=False, cast=asbool)
|
|
87
92
|
config.token_validation_key = rc.get("token_validation_key", cast=json.loads) or None
|
|
88
93
|
config.token_validation_keys_download_url = rc.get("token_validation_keys_download_url")
|
spinta/types/partial/link.py
CHANGED
|
@@ -2,23 +2,29 @@ from copy import copy
|
|
|
2
2
|
|
|
3
3
|
from spinta import commands
|
|
4
4
|
from spinta.components import Context, Property
|
|
5
|
-
from spinta.
|
|
5
|
+
from spinta.core.enums import Mode
|
|
6
|
+
from spinta.exceptions import PartialTypeNotFound, ParentNodeNotFound, PartialIncorrectProperty
|
|
6
7
|
from spinta.types.datatype import Partial, Ref
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
def get_ref_value(context: Context, prop: Property):
|
|
10
|
+
def get_ref_value(context: Context, prop: Property) -> Property | None:
|
|
10
11
|
parent = prop.parent
|
|
11
|
-
if isinstance(parent, Property) and isinstance(parent.dtype, Ref):
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
if not (isinstance(parent, Property) and isinstance(parent.dtype, Ref)):
|
|
13
|
+
return None
|
|
14
|
+
if isinstance(parent.dtype.model, str):
|
|
15
|
+
parent.dtype.model = commands.get_model(context, prop.model.manifest, parent.dtype.model)
|
|
16
|
+
model = parent.dtype.model
|
|
17
|
+
|
|
18
|
+
if prop.name not in model.properties:
|
|
19
|
+
return None
|
|
20
|
+
|
|
21
|
+
model_property_value = model.properties[prop.name]
|
|
22
|
+
model_dtype = model_property_value.dtype
|
|
23
|
+
if hasattr(model_dtype, "model") and isinstance(model_dtype.model, str):
|
|
24
|
+
model_property_value.dtype.model = commands.get_model(
|
|
25
|
+
context, prop.model.manifest, model_property_value.dtype.model
|
|
26
|
+
)
|
|
27
|
+
return model_property_value
|
|
22
28
|
|
|
23
29
|
|
|
24
30
|
@commands.link.register(Context, Partial)
|
|
@@ -29,14 +35,23 @@ def link(context: Context, dtype: Partial):
|
|
|
29
35
|
if isinstance(parent.dtype, Ref):
|
|
30
36
|
props = dtype.properties
|
|
31
37
|
result = get_ref_value(context, prop)
|
|
38
|
+
if not result:
|
|
39
|
+
raise PartialIncorrectProperty(dtype)
|
|
32
40
|
prop.dtype = copy(result.dtype)
|
|
33
41
|
prop.dtype.properties = props
|
|
34
42
|
prop.dtype.inherited = True
|
|
35
43
|
prop.given.explicit = False
|
|
36
44
|
prop.given.name = ""
|
|
37
45
|
prop.dtype.prop = prop
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
if prop.level is None:
|
|
47
|
+
prop.level = result.level
|
|
48
|
+
# For external mode copy the external mapping
|
|
49
|
+
# For internal mode leave it as is, because it breaks multi-level denormalization
|
|
50
|
+
if result.external and prop.model.mode == Mode.external:
|
|
51
|
+
prop.external = copy(result.external)
|
|
52
|
+
if isinstance(prop.dtype, Ref):
|
|
53
|
+
prop.dtype.refprops = []
|
|
54
|
+
commands.link(context, prop.dtype)
|
|
40
55
|
else:
|
|
41
56
|
raise PartialTypeNotFound(dtype)
|
|
42
57
|
else:
|
spinta/types/ref/link.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
3
|
from spinta import commands
|
|
4
|
-
from spinta.components import Context
|
|
4
|
+
from spinta.components import Context, Model
|
|
5
5
|
from spinta.types.datatype import Ref
|
|
6
6
|
from spinta.exceptions import ModelReferenceNotFound, MissingRefModel
|
|
7
7
|
from spinta.exceptions import ModelReferenceKeyNotFound
|
|
@@ -25,6 +25,8 @@ def link(context: Context, dtype: Ref) -> None:
|
|
|
25
25
|
# Self reference.
|
|
26
26
|
dtype.model = dtype.prop.model
|
|
27
27
|
else:
|
|
28
|
+
if isinstance(rmodel, Model):
|
|
29
|
+
rmodel = rmodel.name
|
|
28
30
|
if not commands.has_model(context, dtype.prop.model.manifest, rmodel):
|
|
29
31
|
raise ModelReferenceNotFound(dtype, ref=rmodel)
|
|
30
32
|
dtype.model = commands.get_model(context, dtype.prop.model.manifest, rmodel)
|
|
@@ -12,7 +12,7 @@ spinta/api/inspect.py,sha256=sf3Q8gUEqqCusPCiT1ZqH4Kyc7T9voKWWVRlQXSoIU0,6776
|
|
|
12
12
|
spinta/api/schema.py,sha256=rsWeKxk6JigoawD56pb8n8AYve59YIrKQyVrqpUsX9I,7634
|
|
13
13
|
spinta/api/validators.py,sha256=GcdKAq-3n9oe58VLClNOBXo3MSNyguCtgWqracHR_9w,666
|
|
14
14
|
spinta/asgi.py,sha256=Too66Cvo4UX0a05K-SSVRUrj8NEBVYpaql_1sq3d8gw,530
|
|
15
|
-
spinta/auth.py,sha256=
|
|
15
|
+
spinta/auth.py,sha256=4QmNrZpVrjaVU3E9g2OWWMkI_4pt7l0JXHW3JA6iXS0,42587
|
|
16
16
|
spinta/backends/__init__.py,sha256=i8zlodXE0ku_N38EBoDoNXvLvBWvc_AIm5SGHSP3aAI,56972
|
|
17
17
|
spinta/backends/components.py,sha256=4nV3Yvye2rYAnBe0PV-rPuK2IXnHzrUFm37ceGI47eo,1663
|
|
18
18
|
spinta/backends/constants.py,sha256=RdmpPhIIUsSwO6sQv1eZdmdl6Rf1h_3qAHDUcTOYOy0,828
|
|
@@ -173,18 +173,19 @@ spinta/backends/postgresql/ufuncs/result/ufuncs.py,sha256=n96VPXwU0vxS8lZu-8s_zK
|
|
|
173
173
|
spinta/backends/postgresql/ufuncs/ufuncs.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
174
174
|
spinta/backends/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
175
175
|
spinta/cli/__init__.py,sha256=3DWdLNWcfRk0mVZPwqKhQfRc47w716jhFjHLXSFTH-c,21
|
|
176
|
-
spinta/cli/admin.py,sha256=
|
|
176
|
+
spinta/cli/admin.py,sha256=FYFiMjWTcRGYZiS3GYcbF8h16XuYuq63VI9qToDGXP0,3123
|
|
177
177
|
spinta/cli/auth.py,sha256=WDwn7p5JDwTq3xS9pki3xykmRrd1D7VxRkyv7nGjvIY,6036
|
|
178
178
|
spinta/cli/config.py,sha256=U8WjKkHo9B0TczZuZLUhWmoCyh5YI8EW5loTqFJyGao,1736
|
|
179
179
|
spinta/cli/data.py,sha256=nOlVj2GEZIvs0oRjTRibmlR0DlLh-GUs3nefZVrLS0c,8288
|
|
180
180
|
spinta/cli/get.py,sha256=GUzj7hg3X0W06EIARveLL93vxKTWfVAUNvtQfmcxepA,1191
|
|
181
181
|
spinta/cli/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
182
182
|
spinta/cli/helpers/admin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
183
|
-
spinta/cli/helpers/admin/components.py,sha256=
|
|
184
|
-
spinta/cli/helpers/admin/registry.py,sha256=
|
|
183
|
+
spinta/cli/helpers/admin/components.py,sha256=ir_CXA-r6GOdHx2nLT5Jiy-9vmxWj-6Ypz5xf27tbz0,335
|
|
184
|
+
spinta/cli/helpers/admin/registry.py,sha256=aJH9rJAeTfBaLadGE7x266TvBHSPhMIowCMjQRq8Ynw,1524
|
|
185
185
|
spinta/cli/helpers/admin/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
186
186
|
spinta/cli/helpers/admin/scripts/changelog.py,sha256=r5heUIoGxILLUB2qqIpliN1JWYcGB6WmPfbVpOzWYm4,13771
|
|
187
187
|
spinta/cli/helpers/admin/scripts/deduplicate.py,sha256=kn4vR7ZFC2-uihRIjYTY0en1BCGEM915mGE8IPI1K_8,9128
|
|
188
|
+
spinta/cli/helpers/admin/scripts/enums.py,sha256=OiNvg3KGLk5QnwgwyHp8nqoT5rf_d0XzX-Ma_f4yWco,5176
|
|
188
189
|
spinta/cli/helpers/auth.py,sha256=qg35WJBCPHpQVLMOnvLKRqNcD6dSSX6NqldlauAuXBU,1196
|
|
189
190
|
spinta/cli/helpers/data.py,sha256=T1t0X01beFthJQnlKFXWHMYxXjnZaPMxsbCP0wlJgHg,5530
|
|
190
191
|
spinta/cli/helpers/errors.py,sha256=ILUm-sHIr4_bNlaonMuEaUJaPcYmOHz589M_sxpRzCo,412
|
|
@@ -198,7 +199,7 @@ spinta/cli/helpers/export/commands.py,sha256=obYO_6bXTfpJCkHn_fV9dsIwzhQwPQVUPhk
|
|
|
198
199
|
spinta/cli/helpers/export/components.py,sha256=a5Q9qYym5KeZ0lyb5RXmKVND5EzQdf-PaeezzQMecXQ,1773
|
|
199
200
|
spinta/cli/helpers/export/helpers.py,sha256=OpuPxrz3PNc9hKZROaavxdXbHsiiYoVXWWtxvETM0II,4737
|
|
200
201
|
spinta/cli/helpers/manifest.py,sha256=8R39le_edqfXnC1ar_Q79JgEz05gBko64SarOjSixBs,1317
|
|
201
|
-
spinta/cli/helpers/message.py,sha256=
|
|
202
|
+
spinta/cli/helpers/message.py,sha256=EwcZtC4JkUFsXcNmw6URfqPR2TQMLTIBD6EvfojMZ4M,518
|
|
202
203
|
spinta/cli/helpers/migrate.py,sha256=fhd4tra_jqYyEEMbh-2GBXQ3d_RGWt3HSfAmvSimiPo,460
|
|
203
204
|
spinta/cli/helpers/push/__init__.py,sha256=KEllTU28EFUKU9qGagCeaT6E49zXSb6HTQYzN6G8NAY,2141
|
|
204
205
|
spinta/cli/helpers/push/components.py,sha256=FeAPb4dOxv4Kok_Sf-3hpqbiHLPeyJ4J7r-D5oKHxao,2235
|
|
@@ -210,9 +211,9 @@ spinta/cli/helpers/push/sync.py,sha256=dSH5TuDTQwbl5qaVLGW5a4vV1eiq2Yo8dB4huRCKM
|
|
|
210
211
|
spinta/cli/helpers/push/utils.py,sha256=Jc9TNaosbYp2TecumSVDSP59EDsdcoUfXwAihV4xI30,5846
|
|
211
212
|
spinta/cli/helpers/push/write.py,sha256=mqKDHq_naooWGW_okpyj4jKjutwF7Gt3ssLhGB3mhyA,13563
|
|
212
213
|
spinta/cli/helpers/script/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
213
|
-
spinta/cli/helpers/script/components.py,sha256=
|
|
214
|
-
spinta/cli/helpers/script/core.py,sha256=
|
|
215
|
-
spinta/cli/helpers/script/helpers.py,sha256=
|
|
214
|
+
spinta/cli/helpers/script/components.py,sha256=OSmIuQ7wG20vdgYoRZKrkiHkw4YM8bV5puV7EdP348k,4417
|
|
215
|
+
spinta/cli/helpers/script/core.py,sha256=KltI0rHaowz__j5AbpTPTlVc-j3SAiGarJwGPyO1jEM,3716
|
|
216
|
+
spinta/cli/helpers/script/helpers.py,sha256=ubx9BhhrPqEWIVrYC2LUTgwILI3I1WDGd8e68RIBzkU,2226
|
|
216
217
|
spinta/cli/helpers/script/registry.py,sha256=h-wPrE7fOCD7B9hmqWTHNPHf1GyuEZuvJd9VmlB5khE,3266
|
|
217
218
|
spinta/cli/helpers/store.py,sha256=LLXQtbuU0tjBB3f1DmWqmy57htY3pjog4tmJZDu9IrA,5507
|
|
218
219
|
spinta/cli/helpers/sync/__init__.py,sha256=zarobjW3JbLgtYl9TWKusrFyzten5qRbwDo3447Kyrc,140
|
|
@@ -235,7 +236,7 @@ spinta/cli/helpers/upgrade/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQe
|
|
|
235
236
|
spinta/cli/helpers/upgrade/scripts/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
236
237
|
spinta/cli/helpers/upgrade/scripts/backends/postgresql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
237
238
|
spinta/cli/helpers/upgrade/scripts/backends/postgresql/comments.py,sha256=ZqbbY0yTgS1p46N_Fn-RDsWxYdgslIwqstcEczqTxGI,9239
|
|
238
|
-
spinta/cli/helpers/upgrade/scripts/backends/postgresql/schemas.py,sha256=
|
|
239
|
+
spinta/cli/helpers/upgrade/scripts/backends/postgresql/schemas.py,sha256=lUoeup6OumGb9A_8iTMVctyBQrza7jsufuZXWkcaLxs,11531
|
|
239
240
|
spinta/cli/helpers/upgrade/scripts/clients.py,sha256=-OrfJvJZ4e07TyYpE7ltPoqntBgs_21J2TuzH6ukYVQ,7420
|
|
240
241
|
spinta/cli/helpers/upgrade/scripts/keymaps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
241
242
|
spinta/cli/helpers/upgrade/scripts/keymaps/sqlalchemy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -266,8 +267,8 @@ spinta/commands/search.py,sha256=Yn-lxFhAQrDzDWFGlTm_WmQmUFXsu6NhuT7PpNJ67Ls,311
|
|
|
266
267
|
spinta/commands/version.py,sha256=rZGszGCU_cgyJyRUgZgg_ts3ZldqemLcugpOOY_UJ64,745
|
|
267
268
|
spinta/commands/write.py,sha256=en2dcD1nb2T28cQDMa41qbeIhZEVH2B1xPDP-nG8mBs,42398
|
|
268
269
|
spinta/compat.py,sha256=eI-tsNtWww_rEW6Gw7tcgoLvUMOb_f1vV-kLjzFPRMs,2919
|
|
269
|
-
spinta/components.py,sha256=
|
|
270
|
-
spinta/config.py,sha256=
|
|
270
|
+
spinta/components.py,sha256=uZ6VFzonu0MolUN2lYSrl_Ggsaoak6cNP5VcZ1BbtMc,35395
|
|
271
|
+
spinta/config.py,sha256=YR4Cxt1ceg-u9B4lZxOYKuAeGrsqiJKKA9pTffppn4A,16272
|
|
271
272
|
spinta/config.yml,sha256=RvUjqZt-EL5wnWaizmoz1b85aBz43NhQwK0fB9WWoHA,3447
|
|
272
273
|
spinta/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
273
274
|
spinta/core/access.py,sha256=EAfG-5hMmhvHdCRSZ65z1y_n6-8i-X-ISElMbR9MIxw,2247
|
|
@@ -285,13 +286,13 @@ spinta/datasets/backends/dataframe/backends/csv/commands/read.py,sha256=4klYZKtG
|
|
|
285
286
|
spinta/datasets/backends/dataframe/backends/csv/components.py,sha256=0EZvqouPeaPq3sHtzSGX9HW4eLtMMnRjAlMNYlu4cAI,209
|
|
286
287
|
spinta/datasets/backends/dataframe/backends/json/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
287
288
|
spinta/datasets/backends/dataframe/backends/json/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
288
|
-
spinta/datasets/backends/dataframe/backends/json/commands/read.py,sha256=
|
|
289
|
+
spinta/datasets/backends/dataframe/backends/json/commands/read.py,sha256=pjSsUEbZiCeXs02qFDoicfpJFSAFhB81qp4Lps9yt0U,7118
|
|
289
290
|
spinta/datasets/backends/dataframe/backends/json/components.py,sha256=kPKQEEvWGZKq_IpiwhVNG4A-7CaEux5fGEuwwcaHCLk,211
|
|
290
291
|
spinta/datasets/backends/dataframe/backends/memory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
291
292
|
spinta/datasets/backends/dataframe/backends/memory/components.py,sha256=tByPlAR0hp8WsL0i5JxAPbOh7bdawlw9UQEfT5SUIEo,226
|
|
292
293
|
spinta/datasets/backends/dataframe/backends/soap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
293
294
|
spinta/datasets/backends/dataframe/backends/soap/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
294
|
-
spinta/datasets/backends/dataframe/backends/soap/commands/read.py,sha256=
|
|
295
|
+
spinta/datasets/backends/dataframe/backends/soap/commands/read.py,sha256=5pVdcSQ47QH9RXgu6_sw8E0eMLW9colZ-6AgeWwnh_I,5667
|
|
295
296
|
spinta/datasets/backends/dataframe/backends/soap/components.py,sha256=TtJFhUfJ6MZTbCUxQC_yiADEGPL8uGVVX9nDVrGiJoA,1451
|
|
296
297
|
spinta/datasets/backends/dataframe/backends/soap/ufuncs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
297
298
|
spinta/datasets/backends/dataframe/backends/soap/ufuncs/components.py,sha256=VmKOHUeeCBcYz3xSh6qj657VeU7BgBL6X8DoSLmEMLE,886
|
|
@@ -302,15 +303,15 @@ spinta/datasets/backends/dataframe/backends/xml/commands/read.py,sha256=dqb_8lL_
|
|
|
302
303
|
spinta/datasets/backends/dataframe/backends/xml/components.py,sha256=CcqTkD2MWky4R_wuY588nUnuUrzagBWTctHa5kV47L4,209
|
|
303
304
|
spinta/datasets/backends/dataframe/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
304
305
|
spinta/datasets/backends/dataframe/commands/check.py,sha256=eghehENfCjCcGeCXGzPXRXczQenV7F5j9T1xuDVUx7c,1385
|
|
305
|
-
spinta/datasets/backends/dataframe/commands/read.py,sha256=
|
|
306
|
+
spinta/datasets/backends/dataframe/commands/read.py,sha256=kzQu-MD21H5UaA2fDIt4wLEb-ukfTevW6spk6vhkQGY,11095
|
|
306
307
|
spinta/datasets/backends/dataframe/components.py,sha256=cnlB1VlZrdtVo4-8q7LukyAvsUr9l2SD-R0PzqJSoMQ,235
|
|
307
308
|
spinta/datasets/backends/dataframe/ufuncs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
308
309
|
spinta/datasets/backends/dataframe/ufuncs/components.py,sha256=gmfS7P6gMyPbjJf2ZY-zWRySXGaIxbIPIqar6p9ARh8,90
|
|
309
310
|
spinta/datasets/backends/dataframe/ufuncs/query/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
310
311
|
spinta/datasets/backends/dataframe/ufuncs/query/components.py,sha256=uekQHMdpJi5506-sWH-HakCw_kGS_0BN5A5Ym1i4yAs,2342
|
|
311
|
-
spinta/datasets/backends/dataframe/ufuncs/query/ufuncs.py,sha256=
|
|
312
|
+
spinta/datasets/backends/dataframe/ufuncs/query/ufuncs.py,sha256=LUCL7rAqM1unZar0BwBH0oIA-FLt-L4E4_J9nBhlyiE,19537
|
|
312
313
|
spinta/datasets/backends/dataframe/ufuncs/ufuncs.py,sha256=LRy_jE48FYtW41-oj4G35ihA70P39qCGWfbgS7amv9U,391
|
|
313
|
-
spinta/datasets/backends/helpers.py,sha256=
|
|
314
|
+
spinta/datasets/backends/helpers.py,sha256=2UZGZfEsb45jpsLRFmeDhMTuXBk9DX2cAqo_oD4jL3U,7689
|
|
314
315
|
spinta/datasets/backends/notimpl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
315
316
|
spinta/datasets/backends/notimpl/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
316
317
|
spinta/datasets/backends/notimpl/commands/load.py,sha256=5wbxy7LJ61fBSM2YpSWqHRFWh2-OQgsG9SUiOW4zCzc,372
|
|
@@ -460,7 +461,7 @@ spinta/dimensions/prefix/commands/load.py,sha256=P8Lzd7smS-lJ30NTFBPs_2ywmZw2qlr
|
|
|
460
461
|
spinta/dimensions/prefix/components.py,sha256=5VJel2wvZUCol_4riD119CD5F5R6TSdf5ISeWDglKsk,618
|
|
461
462
|
spinta/dimensions/prefix/helpers.py,sha256=VE6TT50tqyy-9becV2PV_TMjA4HXGrSSidV4F7aUCxE,1003
|
|
462
463
|
spinta/dispatcher.py,sha256=FZaBJ7O2J-0hZ4fGL2w3a_cO8jzOVuxqyfOqON6jg0E,3769
|
|
463
|
-
spinta/exceptions.py,sha256=
|
|
464
|
+
spinta/exceptions.py,sha256=6KPBa-zLMyhz-focdNypsnTDyTXFCGzh4ShmPLM8Ohc,36046
|
|
464
465
|
spinta/fetcher.py,sha256=55-nxQy9WuISrLAPuJOxjJjtZ0opqTlPiCHI7iABiic,1330
|
|
465
466
|
spinta/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
466
467
|
spinta/formats/ascii/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -611,10 +612,10 @@ spinta/templates/error.html,sha256=nlC52cRicXqs1nxdwl_4ikMZA9-N9WKp1-OLOp_oJwA,1
|
|
|
611
612
|
spinta/templates/form.html,sha256=Ioszr7D8v_-ZkwmEHNeeMyHnh6p60te0wMDkKwdDaCA,794
|
|
612
613
|
spinta/testing/__init__.py,sha256=bXkhOgOOugNFKv6O6uHrANccQ98B4E352WeBtJDUZ4E,50
|
|
613
614
|
spinta/testing/cli.py,sha256=HPwUHeo2W5q5dRl56kIytJIxvG7WTdi52N_CMylEuvM,1840
|
|
614
|
-
spinta/testing/client.py,sha256=
|
|
615
|
+
spinta/testing/client.py,sha256=q_SLseq_hLN7O8-pxWJmNfJqeoMCGQrfWPy8Fh-oqQ8,10040
|
|
615
616
|
spinta/testing/config.py,sha256=o40GCI0qAeQGK1u-VrxtnUh45m0o-tPHySJ0oQmN4j4,2919
|
|
616
617
|
spinta/testing/context.py,sha256=9lDGKEN1wV58TW9KlrJyuzCo2vMer_HSP2MKQsKPZqI,3382
|
|
617
|
-
spinta/testing/csv.py,sha256=
|
|
618
|
+
spinta/testing/csv.py,sha256=JnrB3xwQDvd0EL7fQN9CcvLs9Bspydq0mdqAE11QlPM,357
|
|
618
619
|
spinta/testing/data.py,sha256=YHIypRRy1InkFwgx-jvLUaCCAvY1rA-QeHPYn4bL1iE,7259
|
|
619
620
|
spinta/testing/datasets.py,sha256=gzPQoq_1XEzN1v9ghESNEFN5GPrwE622uifObDaBKMs,1478
|
|
620
621
|
spinta/testing/dtypes.py,sha256=5qGBuCjDJywTJSvJmvA62DvY8H-dJFti441wtGkrvO0,6288
|
|
@@ -636,7 +637,7 @@ spinta/types/array/resolve.py,sha256=o8fEPK0RZx5FT6oMQ2RZyE8WYT3WK9qdh6QTORzndD8
|
|
|
636
637
|
spinta/types/backref/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
637
638
|
spinta/types/backref/link.py,sha256=_q7wHvuRUuGpllth_zTerq6Eoajq3C_WqjsqjxZTc7c,4684
|
|
638
639
|
spinta/types/command.py,sha256=pmlL4ZQYFwzNm0nMY4q7GZIoPNqiYxZ-k6CzfTByCg4,975
|
|
639
|
-
spinta/types/config.py,sha256=
|
|
640
|
+
spinta/types/config.py,sha256=Yl9oGiE3znHZgtHQNtr8s7BGARCV_O10ibe1Mp8orss,6572
|
|
640
641
|
spinta/types/datatype.py,sha256=zbarMo6S9MX__NHfnekw2Oto3MGqj3X4Ms5tWGodjps,16747
|
|
641
642
|
spinta/types/denorm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
642
643
|
spinta/types/denorm/check.py,sha256=2lZrDwd_F71hkflF7jvgFp_vyPRtAQV3HVlFZAEodwY,606
|
|
@@ -659,10 +660,10 @@ spinta/types/object/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
659
660
|
spinta/types/object/check.py,sha256=14D_lGNIsZvNevQ9FhRTKtGGK3VJA9ugCI83xg4QYX4,276
|
|
660
661
|
spinta/types/object/link.py,sha256=wjtzkVcS4gN-9etAauj2sLJczOi8Gwig-WpVaSFu_aA,361
|
|
661
662
|
spinta/types/partial/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
662
|
-
spinta/types/partial/link.py,sha256=
|
|
663
|
+
spinta/types/partial/link.py,sha256=G4ieydtTOJloGPqB7VHpjmyZ3BIKieA-I1ROX3cvyw0,2487
|
|
663
664
|
spinta/types/ref/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
664
665
|
spinta/types/ref/check.py,sha256=I8gFHJiuYWGw1UIlGx2vyxlqExt15EkJ1jPz409fLXs,853
|
|
665
|
-
spinta/types/ref/link.py,sha256=
|
|
666
|
+
spinta/types/ref/link.py,sha256=Cuu1E6iUYBcGXLYYm814ptlBe6lXNWr-QvBDGQm1X8U,2086
|
|
666
667
|
spinta/types/ref/resolve.py,sha256=X3J5etLCaBIWfp0saqBIb0XOS7DkpM8-tFhVUvlN8OQ,309
|
|
667
668
|
spinta/types/store.py,sha256=6dVzeIUoYZC-1F-LVA4ZSG23t593twvRY0-aa1-RWiY,5234
|
|
668
669
|
spinta/types/text/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -740,8 +741,8 @@ spinta/utils/tree.py,sha256=iF8eOSBagUoDmdJGNQgsYB_gshsFajmiUALXqU9luHE,591
|
|
|
740
741
|
spinta/utils/types.py,sha256=lfYSxKGPuPeUsO14d2OYodtbRY3zsa-o-z8HveVH3t0,801
|
|
741
742
|
spinta/utils/units.py,sha256=CFFLv1NHYsoSSzwiar3zXYmt4m3sccW5niUgkZQgo3k,747
|
|
742
743
|
spinta/utils/url.py,sha256=b6sqQEpmCdT3oV4vGDzXnN8w415InAYjIW_o2djhQS8,2950
|
|
743
|
-
spinta-0.2.
|
|
744
|
-
spinta-0.2.
|
|
745
|
-
spinta-0.2.
|
|
746
|
-
spinta-0.2.
|
|
747
|
-
spinta-0.2.
|
|
744
|
+
spinta-0.2.dev23.dist-info/METADATA,sha256=InX-K2DKF6mZUaQ5Kn7jEHA58o2JHRM38T1o_sM5ptU,10214
|
|
745
|
+
spinta-0.2.dev23.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
746
|
+
spinta-0.2.dev23.dist-info/entry_points.txt,sha256=-jdsOQZcMu3rUOwgIJNS3gZS4rwWPACuXXy128F676w,46
|
|
747
|
+
spinta-0.2.dev23.dist-info/licenses/LICENSE,sha256=JKmjfBLapeFWNI_qdVr5bXGlsuMPa6nRarKPK5davKw,1071
|
|
748
|
+
spinta-0.2.dev23.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|