dcicutils 8.7.1.1b6__tar.gz → 8.7.1.1b9__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/PKG-INFO +1 -1
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/portal_object_utils.py +7 -10
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/portal_utils.py +16 -11
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/structured_data.py +28 -1
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/pyproject.toml +1 -1
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/LICENSE.txt +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/README.rst +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/__init__.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/base.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/beanstalk_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/bundle_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/cloudformation_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/codebuild_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/command_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/common.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/contribution_scripts.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/contribution_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/creds_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/data_readers.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/data_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/deployment_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/diff_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/docker_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/ecr_scripts.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/ecr_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/ecs_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/env_base.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/env_manager.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/env_scripts.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/env_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/env_utils_legacy.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/es_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/exceptions.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/ff_mocks.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/ff_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/file_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/function_cache_decorator.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/glacier_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/jh_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/kibana/dashboards.json +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/kibana/readme.md +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/lang_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_policies/c4-infrastructure.jsonc +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_policies/c4-python-infrastructure.jsonc +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_policies/park-lab-common-server.jsonc +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_policies/park-lab-common.jsonc +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_policies/park-lab-gpl-pipeline.jsonc +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_policies/park-lab-pipeline.jsonc +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/log_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/misc_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/obfuscation_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/opensearch_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/project_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/qa_checkers.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/qa_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/redis_tools.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/redis_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/s3_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/schema_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/scripts/publish_to_pypi.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/scripts/run_license_checker.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/secrets_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/sheet_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/snapshot_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/ssl_certificate_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/task_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/tmpfile_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/trace_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/validation_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/variant_utils.py +0 -0
- {dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/zip_utils.py +0 -0
@@ -137,23 +137,20 @@ class PortalObject:
|
|
137
137
|
comparing_data = value
|
138
138
|
else:
|
139
139
|
return {}
|
140
|
-
return PortalObject._compare(this_data, comparing_data
|
140
|
+
return PortalObject._compare(this_data, comparing_data)
|
141
141
|
|
142
142
|
_ARRAY_KEY_REGULAR_EXPRESSION = re.compile(rf"^({Schema._ARRAY_NAME_SUFFIX_CHAR}\d+)$")
|
143
143
|
|
144
144
|
@staticmethod
|
145
|
-
def _compare(a: Any, b: Any,
|
145
|
+
def _compare(a: Any, b: Any, _path: Optional[str] = None) -> dict:
|
146
146
|
def diff_creating(value: Any) -> object: # noqa
|
147
|
-
|
148
|
-
return create_readonly_object(value=value, type=value_type,
|
147
|
+
return create_readonly_object(value=value,
|
149
148
|
creating_value=True, updating_value=None, deleting_value=False)
|
150
149
|
def diff_updating(value: Any, updating_value: Any) -> object: # noqa
|
151
|
-
|
152
|
-
return create_readonly_object(value=value, type=value_type,
|
150
|
+
return create_readonly_object(value=value,
|
153
151
|
creating_value=False, updating_value=updating_value, deleting_value=False)
|
154
152
|
def diff_deleting(value: Any) -> object: # noqa
|
155
|
-
|
156
|
-
return create_readonly_object(value=value, type=value_type,
|
153
|
+
return create_readonly_object(value=value,
|
157
154
|
creating_value=False, updating_value=None, deleting_value=True)
|
158
155
|
diffs = {}
|
159
156
|
if isinstance(a, dict) and isinstance(b, dict):
|
@@ -163,7 +160,7 @@ class PortalObject:
|
|
163
160
|
if a[key] != PortalObject._PROPERTY_DELETION_SENTINEL:
|
164
161
|
diffs[path] = diff_creating(a[key])
|
165
162
|
else:
|
166
|
-
diffs.update(PortalObject._compare(a[key], b[key],
|
163
|
+
diffs.update(PortalObject._compare(a[key], b[key], _path=path))
|
167
164
|
elif isinstance(a, list) and isinstance(b, list):
|
168
165
|
# Ignore order of array elements; not absolutely technically correct but suits our purpose.
|
169
166
|
for index in range(len(a)):
|
@@ -179,7 +176,7 @@ class PortalObject:
|
|
179
176
|
if index < len(b):
|
180
177
|
diffs[path] = diff_deleting(b[index])
|
181
178
|
elif len(b) < index:
|
182
|
-
diffs.update(PortalObject._compare(a[index], b[index],
|
179
|
+
diffs.update(PortalObject._compare(a[index], b[index], _path=path))
|
183
180
|
else:
|
184
181
|
diffs[path] = diff_creating(a[index])
|
185
182
|
elif a != b:
|
@@ -15,7 +15,7 @@ from typing import Callable, Dict, List, Optional, Tuple, Type, Union
|
|
15
15
|
from uuid import uuid4 as uuid
|
16
16
|
from webtest.app import TestApp, TestResponse
|
17
17
|
from wsgiref.simple_server import make_server as wsgi_make_server
|
18
|
-
from dcicutils.common import OrchestratedApp, ORCHESTRATED_APPS
|
18
|
+
from dcicutils.common import APP_SMAHT, OrchestratedApp, ORCHESTRATED_APPS
|
19
19
|
from dcicutils.ff_utils import get_metadata, get_schema, patch_metadata, post_metadata
|
20
20
|
from dcicutils.misc_utils import to_camel_case, VirtualApp
|
21
21
|
from dcicutils.tmpfile_utils import temporary_file
|
@@ -42,6 +42,7 @@ class Portal:
|
|
42
42
|
or a dcicutils.misc_utils.VirtualApp, or even a pyramid.router.Router.
|
43
43
|
8. From another Portal object (i.e. copy constructor).
|
44
44
|
"""
|
45
|
+
DEFAULT_APP = APP_SMAHT
|
45
46
|
KEYS_FILE_DIRECTORY = "~"
|
46
47
|
MIME_TYPE_JSON = "application/json"
|
47
48
|
|
@@ -60,7 +61,7 @@ class Portal:
|
|
60
61
|
self._vapp = None
|
61
62
|
for arg in unspecified:
|
62
63
|
if arg is not None:
|
63
|
-
raise Exception("Portal
|
64
|
+
raise Exception("Portal initialization error; extraneous arguments.")
|
64
65
|
|
65
66
|
def init_from_portal(portal: Portal, unspecified: Optional[list] = None) -> None:
|
66
67
|
init(unspecified=unspecified)
|
@@ -93,13 +94,13 @@ class Portal:
|
|
93
94
|
raise Exception(f"Portal server inconsistency: {server} vs {key_server}")
|
94
95
|
self._key["server"] = self._server = server
|
95
96
|
if not self._key:
|
96
|
-
raise Exception("Portal
|
97
|
+
raise Exception("Portal initialization error; from key.")
|
97
98
|
|
98
99
|
def init_from_key_pair(key_pair: tuple, server: Optional[str], unspecified: Optional[list] = []) -> None:
|
99
100
|
if len(key_pair) >= 2:
|
100
101
|
init_from_key({"key": key_pair[0], "secret": key_pair[1]}, server, unspecified=unspecified)
|
101
102
|
else:
|
102
|
-
raise Exception("Portal
|
103
|
+
raise Exception("Portal initialization error; from key-pair.")
|
103
104
|
|
104
105
|
def init_from_keys_file(keys_file: str, env: Optional[str], server: Optional[str],
|
105
106
|
unspecified: Optional[list] = []) -> None:
|
@@ -107,7 +108,7 @@ class Portal:
|
|
107
108
|
with io.open(keys_file := os.path.expanduser(keys_file)) as f:
|
108
109
|
keys = json.load(f)
|
109
110
|
except Exception:
|
110
|
-
raise Exception(f"Portal
|
111
|
+
raise Exception(f"Portal initialization error; cannot open keys-file: {keys_file}")
|
111
112
|
if isinstance(env, str) and env and isinstance(key := keys.get(env), dict):
|
112
113
|
init_from_key(key, server)
|
113
114
|
self._keys_file = keys_file
|
@@ -121,7 +122,8 @@ class Portal:
|
|
121
122
|
self._keys_file = keys_file
|
122
123
|
self._env = env
|
123
124
|
else:
|
124
|
-
raise Exception(f"Portal
|
125
|
+
raise Exception(f"Portal initialization error;"
|
126
|
+
f" {env or server or None} not found in keys-file: {keys_file}")
|
125
127
|
|
126
128
|
def init_from_env_server_app(env: str, server: str, app: Optional[str],
|
127
129
|
unspecified: Optional[list] = None) -> None:
|
@@ -146,7 +148,7 @@ class Portal:
|
|
146
148
|
return prefix + server if server else None
|
147
149
|
|
148
150
|
if (valid_app := app) and not (valid_app := Portal._valid_app(app)):
|
149
|
-
raise Exception(f"Portal
|
151
|
+
raise Exception(f"Portal initialization error; invalid app: {app}")
|
150
152
|
self._app = valid_app
|
151
153
|
if isinstance(arg, Portal):
|
152
154
|
init_from_portal(arg, unspecified=[env, server, app])
|
@@ -164,12 +166,15 @@ class Portal:
|
|
164
166
|
init_from_env_server_app(arg, server, app, unspecified=[env])
|
165
167
|
elif (isinstance(env, str) and env) or (isinstance(server, str) and server):
|
166
168
|
init_from_env_server_app(env, server, app, unspecified=[arg])
|
169
|
+
elif not arg and (keys_file := self._default_keys_file(app=self._app or Portal.DEFAULT_APP, env=env)):
|
170
|
+
# If no initial arg then look for default app keys file.
|
171
|
+
init_from_keys_file(keys_file, env, server)
|
167
172
|
elif raise_exception:
|
168
|
-
raise Exception("Portal
|
173
|
+
raise Exception("Portal initialization error; insufficient arguments.")
|
169
174
|
else:
|
170
175
|
init()
|
171
176
|
if not self.vapp and not self.key and raise_exception:
|
172
|
-
raise Exception("Portal
|
177
|
+
raise Exception("Portal initialization error; neither key nor vapp defined.")
|
173
178
|
|
174
179
|
@property
|
175
180
|
def ini_file(self) -> Optional[str]:
|
@@ -443,7 +448,7 @@ class Portal:
|
|
443
448
|
if isinstance(arg, list) or isinstance(arg, dict) or isinstance(arg, Callable):
|
444
449
|
return Portal(Portal._create_router_for_testing(arg))
|
445
450
|
elif isinstance(arg, str) and arg.endswith(".ini"):
|
446
|
-
return Portal(
|
451
|
+
return Portal(arg)
|
447
452
|
elif arg == "local" or arg is True:
|
448
453
|
minimal_ini_for_testing = "\n".join([
|
449
454
|
"[app:app]\nuse = egg:encoded\nfile_upload_bucket = dummy",
|
@@ -467,7 +472,7 @@ class Portal:
|
|
467
472
|
else:
|
468
473
|
minimal_ini_for_testing = "[app:app]\nuse = egg:encoded\nsqlalchemy.url = postgresql://dummy\n"
|
469
474
|
with temporary_file(content=minimal_ini_for_testing, suffix=".ini") as ini_file:
|
470
|
-
return Portal(
|
475
|
+
return Portal(ini_file)
|
471
476
|
|
472
477
|
@staticmethod
|
473
478
|
def _create_vapp(arg: Union[TestApp, VirtualApp, PyramidRouter, str] = None) -> TestApp:
|
@@ -11,8 +11,10 @@ from webtest.app import TestApp
|
|
11
11
|
from dcicutils.common import OrchestratedApp
|
12
12
|
from dcicutils.data_readers import CsvReader, Excel, RowReader
|
13
13
|
from dcicutils.file_utils import search_for_file
|
14
|
-
from dcicutils.misc_utils import (create_dict,
|
14
|
+
from dcicutils.misc_utils import (create_dict, create_readonly_object, load_json_if,
|
15
|
+
merge_objects, remove_empty_properties, right_trim,
|
15
16
|
split_string, to_boolean, to_enum, to_float, to_integer, VirtualApp)
|
17
|
+
from dcicutils.portal_object_utils import PortalObject
|
16
18
|
from dcicutils.portal_utils import Portal as PortalBase
|
17
19
|
from dcicutils.zip_utils import unpack_gz_file_to_temporary_file, unpack_files
|
18
20
|
|
@@ -61,6 +63,10 @@ class StructuredDataSet:
|
|
61
63
|
def data(self) -> dict:
|
62
64
|
return self._data
|
63
65
|
|
66
|
+
@property
|
67
|
+
def portal(self) -> Optional[Portal]:
|
68
|
+
return self._portal
|
69
|
+
|
64
70
|
@staticmethod
|
65
71
|
def load(file: str, portal: Optional[Union[VirtualApp, TestApp, Portal]] = None,
|
66
72
|
schemas: Optional[List[dict]] = None, autoadd: Optional[dict] = None,
|
@@ -140,6 +146,27 @@ class StructuredDataSet:
|
|
140
146
|
upload_file["path"] = file_path
|
141
147
|
return upload_files
|
142
148
|
|
149
|
+
def compare(self) -> dict:
|
150
|
+
diffs = {}
|
151
|
+
if self.data or self.portal:
|
152
|
+
refs = self.resolved_refs_with_uuids
|
153
|
+
for object_type in self.data:
|
154
|
+
if not diffs.get(object_type):
|
155
|
+
diffs[object_type] = []
|
156
|
+
for portal_object in self.data[object_type]:
|
157
|
+
portal_object = PortalObject(self.portal, portal_object, object_type)
|
158
|
+
existing_object, identifying_path = portal_object.lookup(include_identifying_path=True, raw=True)
|
159
|
+
if existing_object:
|
160
|
+
object_diffs = portal_object.compare(existing_object, consider_refs=True, resolved_refs=refs)
|
161
|
+
diffs[object_type].append(create_readonly_object(path=identifying_path,
|
162
|
+
uuid=existing_object.uuid,
|
163
|
+
diffs=object_diffs or None))
|
164
|
+
else:
|
165
|
+
# If there is no existing object we still create a record for this object
|
166
|
+
# but with no uuid which will be the indication that it does not exist.
|
167
|
+
diffs[object_type].append(create_readonly_object(path=identifying_path, uuid=None, diffs=None))
|
168
|
+
return diffs
|
169
|
+
|
143
170
|
def _load_file(self, file: str) -> None:
|
144
171
|
# Returns a dictionary where each property is the name (i.e. the type) of the data,
|
145
172
|
# and the value is array of dictionaries for the data itself. Handle these kinds of files:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "dcicutils"
|
3
|
-
version = "8.7.1.
|
3
|
+
version = "8.7.1.1b9" # TODO: To become 8.7.2
|
4
4
|
description = "Utility package for interacting with the 4DN Data Portal and other 4DN resources"
|
5
5
|
authors = ["4DN-DCIC Team <support@4dnucleome.org>"]
|
6
6
|
license = "MIT"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_policies/c4-infrastructure.jsonc
RENAMED
File without changes
|
File without changes
|
{dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_policies/park-lab-common-server.jsonc
RENAMED
File without changes
|
{dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_policies/park-lab-common.jsonc
RENAMED
File without changes
|
{dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_policies/park-lab-gpl-pipeline.jsonc
RENAMED
File without changes
|
{dcicutils-8.7.1.1b6 → dcicutils-8.7.1.1b9}/dcicutils/license_policies/park-lab-pipeline.jsonc
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|