core-lens 0.1.dev155__tar.gz → 0.1.dev160__tar.gz
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.
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/PKG-INFO +1 -1
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/_version.py +2 -2
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/base/namespaces/plot.py +131 -8
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/base/namespaces/stats.py +12 -1
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_plot.py +2 -2
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.github/ISSUE_TEMPLATE/blank-proposal.yaml +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.github/ISSUE_TEMPLATE/bug-report.yaml +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.github/ISSUE_TEMPLATE/feature-request.yaml +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.github/pull_request_template.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.github/workflows/ci.yml +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.github/workflows/gh-pages.yml +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.github/workflows/pre-release.yml +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.github/workflows/release.yml +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.gitignore +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.gitmessage +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.pre-commit-config.yaml +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/.python-version +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/CONTRIBUTING.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/LICENSE +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/README.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/SKILLS.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/benchmarks/README.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/benchmarks/bench_aoi.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/benchmarks/bench_export.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/benchmarks/bench_polars_utils.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/benchmarks/bench_result.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/benchmarks/bench_schema.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/benchmarks/bench_season.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/benchmarks/bench_spatial.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/benchmarks/bench_view.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/benchmarks/run_all.sh +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/Makefile +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/make.bat +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/source/concepts.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/source/conf.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/source/export.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/source/index.rst +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/source/intro.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/source/logging.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/source/plots.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/source/plugins.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/source/queries.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/source/quickstart.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/docs/source/stats.md +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/examples/demo_mws.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/examples/demo_tehsil.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/hooks/mypy.sh +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/hooks/no-parquet-outside-fixtures.sh +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/hooks/pytest.sh +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/pyproject.toml +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/__init__.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/__main__.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/aoi.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/base/__init__.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/base/entity.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/base/namespaces/__init__.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/base/result.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/base/view.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/entities/__init__.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/entities/mws.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/entities/tehsil.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/entities/waterbody.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/export/__init__.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/export/formats.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/py.typed +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/schema/__init__.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/schema/detection.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/schema/profile.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/utils/__init__.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/utils/paths.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/utils/polars_utils.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/utils/season.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/src/core_lens/utils/spatial.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/fixtures/generate_fixtures.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/conftest.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_aoi.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_entities.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_entity.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_export.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_main.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_polars_utils.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_profile.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_result.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_schema_detection.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_schema_profile.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_season.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_season_config.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_spatial.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_stats.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/tests/unit/test_view.py +0 -0
- {core_lens-0.1.dev155 → core_lens-0.1.dev160}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-lens
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.dev160
|
|
4
4
|
Summary: Query, analyse, and visualise CoreStack's microwatershed and Earth science data through a clean, composable Python API.
|
|
5
5
|
Project-URL: Homepage, https://github.com/ApoorvaKashyap/core-lens
|
|
6
6
|
Project-URL: Issues, https://github.com/ApoorvaKashyap/core-lens/issues
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.1.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1, '
|
|
21
|
+
__version__ = version = '0.1.dev160'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 'dev160')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -27,7 +27,6 @@ class SubplotOn(Enum):
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
if TYPE_CHECKING:
|
|
30
|
-
import lonboard
|
|
31
30
|
from bokeh.plotting import figure as BokehFigure
|
|
32
31
|
from core_lens.base.result import Result
|
|
33
32
|
|
|
@@ -224,6 +223,113 @@ def _apply_theme(fig: Any, result: "Result", title: str) -> None:
|
|
|
224
223
|
fig.add_layout(subtitle)
|
|
225
224
|
|
|
226
225
|
|
|
226
|
+
class MapWithLegend:
|
|
227
|
+
"""Wrapper around lonboard.Map that adds a legend for Jupyter and HTML exports."""
|
|
228
|
+
|
|
229
|
+
def __init__(
|
|
230
|
+
self,
|
|
231
|
+
map_widget: Any,
|
|
232
|
+
cmap: Any,
|
|
233
|
+
v_min: float,
|
|
234
|
+
v_max: float,
|
|
235
|
+
column_name: str,
|
|
236
|
+
clamped_min: bool = False,
|
|
237
|
+
clamped_max: bool = False,
|
|
238
|
+
) -> None:
|
|
239
|
+
self._map = map_widget
|
|
240
|
+
self._cmap = cmap
|
|
241
|
+
self._v_min = v_min
|
|
242
|
+
self._v_max = v_max
|
|
243
|
+
self._column_name = column_name
|
|
244
|
+
self._clamped_min = clamped_min
|
|
245
|
+
self._clamped_max = clamped_max
|
|
246
|
+
|
|
247
|
+
def __getattr__(self, name: str) -> Any:
|
|
248
|
+
return getattr(self._map, name)
|
|
249
|
+
|
|
250
|
+
def _generate_legend_div(self, absolute: bool = True) -> str:
|
|
251
|
+
import matplotlib.colors as mcolors
|
|
252
|
+
import numpy as np
|
|
253
|
+
|
|
254
|
+
stops = 5
|
|
255
|
+
style = (
|
|
256
|
+
"position:absolute; bottom:35px; left:20px; z-index:999; "
|
|
257
|
+
if absolute
|
|
258
|
+
else ""
|
|
259
|
+
)
|
|
260
|
+
style += (
|
|
261
|
+
"background:white; padding:8px 12px; border-radius:4px; "
|
|
262
|
+
"box-shadow:0 1px 4px rgba(0,0,0,.3); font-family:sans-serif; font-size:12px;"
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
html = [
|
|
266
|
+
f'<div style="{style}">',
|
|
267
|
+
f'<b style="margin-bottom:4px;display:inline-block;">{self._column_name}</b><br>',
|
|
268
|
+
]
|
|
269
|
+
|
|
270
|
+
if np.isnan(self._v_min) or np.isnan(self._v_max) or self._v_min >= self._v_max:
|
|
271
|
+
html.append('<span style="color:gray;">No Data</span>')
|
|
272
|
+
else:
|
|
273
|
+
for i in range(stops):
|
|
274
|
+
frac = (stops - 1 - i) / (
|
|
275
|
+
stops - 1
|
|
276
|
+
) # Reverse order so highest is on top
|
|
277
|
+
val = self._v_min + frac * (self._v_max - self._v_min)
|
|
278
|
+
rgba = self._cmap(frac)
|
|
279
|
+
hex_color = mcolors.to_hex(rgba)
|
|
280
|
+
|
|
281
|
+
prefix = ""
|
|
282
|
+
if i == stops - 1 and self._clamped_min:
|
|
283
|
+
prefix = "< "
|
|
284
|
+
elif i == 0 and self._clamped_max:
|
|
285
|
+
prefix = "> "
|
|
286
|
+
|
|
287
|
+
html.append(
|
|
288
|
+
f'<div style="margin-top:2px;">'
|
|
289
|
+
f'<span style="background:{hex_color};width:12px;height:12px;'
|
|
290
|
+
f"display:inline-block;margin-right:8px;vertical-align:middle;"
|
|
291
|
+
f'border:1px solid #ccc;"></span>'
|
|
292
|
+
f'<span style="vertical-align:middle;">{prefix}{val:.2f}</span></div>'
|
|
293
|
+
)
|
|
294
|
+
html.append("</div>")
|
|
295
|
+
return "".join(html)
|
|
296
|
+
|
|
297
|
+
def _ipython_display_(self) -> None:
|
|
298
|
+
import ipywidgets # type: ignore[import-untyped]
|
|
299
|
+
from IPython.display import display
|
|
300
|
+
|
|
301
|
+
legend_div = self._generate_legend_div(absolute=False)
|
|
302
|
+
legend_html = ipywidgets.HTML(legend_div)
|
|
303
|
+
|
|
304
|
+
display(ipywidgets.VBox([legend_html, self._map])) # type: ignore[no-untyped-call]
|
|
305
|
+
|
|
306
|
+
def to_html(
|
|
307
|
+
self, filename: str | None = None, title: str | None = None
|
|
308
|
+
) -> str | None:
|
|
309
|
+
"""Export the map to a static HTML string or file, injecting the legend."""
|
|
310
|
+
# lonboard.Map to_html doesn't accept title in all versions,
|
|
311
|
+
# so we conditionally pass it if it's not None or we can just omit title if we aren't sure.
|
|
312
|
+
# But wait, we can just use `self._map.to_html()`
|
|
313
|
+
if title is not None:
|
|
314
|
+
try:
|
|
315
|
+
html_str = self._map.to_html(title=title)
|
|
316
|
+
except TypeError:
|
|
317
|
+
html_str = self._map.to_html()
|
|
318
|
+
else:
|
|
319
|
+
html_str = self._map.to_html()
|
|
320
|
+
|
|
321
|
+
legend_div = self._generate_legend_div(absolute=True)
|
|
322
|
+
|
|
323
|
+
# Inject right before closing </body> tag
|
|
324
|
+
html_str = html_str.replace("</body>", legend_div + "</body>")
|
|
325
|
+
|
|
326
|
+
if filename is not None:
|
|
327
|
+
with open(filename, "w", encoding="utf-8") as f:
|
|
328
|
+
f.write(html_str)
|
|
329
|
+
return None
|
|
330
|
+
return str(html_str)
|
|
331
|
+
|
|
332
|
+
|
|
227
333
|
class PlotNamespace:
|
|
228
334
|
"""Visualisation namespace for :class:`~core_lens.base.result.Result`.
|
|
229
335
|
|
|
@@ -238,8 +344,12 @@ class PlotNamespace:
|
|
|
238
344
|
self.result = result
|
|
239
345
|
|
|
240
346
|
def choropleth(
|
|
241
|
-
self,
|
|
242
|
-
|
|
347
|
+
self,
|
|
348
|
+
column: str,
|
|
349
|
+
subplot_on: SubplotOn | None = None,
|
|
350
|
+
v_min: float | None = None,
|
|
351
|
+
v_max: float | None = None,
|
|
352
|
+
) -> "MapWithLegend":
|
|
243
353
|
"""Render an interactive choropleth map using Lonboard.
|
|
244
354
|
|
|
245
355
|
If the Result does not already have geometry, it will be attached
|
|
@@ -312,20 +422,33 @@ class PlotNamespace:
|
|
|
312
422
|
|
|
313
423
|
with warnings.catch_warnings():
|
|
314
424
|
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
315
|
-
|
|
425
|
+
actual_min, actual_max = np.nanmin(values), np.nanmax(values)
|
|
426
|
+
|
|
427
|
+
calc_v_min = float(v_min) if v_min is not None else float(actual_min)
|
|
428
|
+
calc_v_max = float(v_max) if v_max is not None else float(actual_max)
|
|
429
|
+
|
|
430
|
+
clamped_min = v_min is not None and actual_min < v_min
|
|
431
|
+
clamped_max = v_max is not None and actual_max > v_max
|
|
316
432
|
|
|
317
|
-
if np.isnan(
|
|
433
|
+
if np.isnan(calc_v_min) or np.isnan(calc_v_max) or calc_v_min >= calc_v_max:
|
|
318
434
|
norm_values = np.where(np.isnan(values), np.nan, 0.0)
|
|
319
435
|
else:
|
|
320
|
-
norm_values = (
|
|
436
|
+
norm_values = np.clip(
|
|
437
|
+
(values - calc_v_min) / (calc_v_max - calc_v_min), 0.0, 1.0
|
|
438
|
+
)
|
|
321
439
|
|
|
322
|
-
cmap = mpl.colormaps["
|
|
440
|
+
cmap = mpl.colormaps["plasma_r"]
|
|
323
441
|
|
|
324
442
|
layer = lonboard.PolygonLayer(
|
|
325
443
|
arrow_table,
|
|
326
444
|
get_fill_color=apply_continuous_cmap(norm_values, cmap),
|
|
445
|
+
get_line_color=[255, 255, 255, 180],
|
|
446
|
+
line_width_min_pixels=0.25,
|
|
447
|
+
)
|
|
448
|
+
map_widget = lonboard.Map(layers=[layer])
|
|
449
|
+
return MapWithLegend(
|
|
450
|
+
map_widget, cmap, calc_v_min, calc_v_max, column, clamped_min, clamped_max
|
|
327
451
|
)
|
|
328
|
-
return lonboard.Map(layers=[layer])
|
|
329
452
|
|
|
330
453
|
def timeseries(
|
|
331
454
|
self,
|
|
@@ -515,7 +515,16 @@ class StatsNamespace:
|
|
|
515
515
|
).alias("pct_change"),
|
|
516
516
|
]
|
|
517
517
|
)
|
|
518
|
-
|
|
518
|
+
|
|
519
|
+
final_cols = join_keys + [
|
|
520
|
+
"year_from",
|
|
521
|
+
"year_to",
|
|
522
|
+
"value_from",
|
|
523
|
+
"value_to",
|
|
524
|
+
"change",
|
|
525
|
+
"pct_change",
|
|
526
|
+
]
|
|
527
|
+
data = joined.select(final_cols)
|
|
519
528
|
|
|
520
529
|
else: # trend
|
|
521
530
|
period_df = df.filter(
|
|
@@ -538,6 +547,8 @@ class StatsNamespace:
|
|
|
538
547
|
rows.append(
|
|
539
548
|
{
|
|
540
549
|
key: eid,
|
|
550
|
+
"year_from": int(from_period),
|
|
551
|
+
"year_to": int(to_period),
|
|
541
552
|
"slope": float(cast(float, slope)),
|
|
542
553
|
"r_squared": float(cast(float, r_val) ** 2),
|
|
543
554
|
"direction": (
|
|
@@ -9,7 +9,7 @@ from typing import Any
|
|
|
9
9
|
from core_lens.base.entity import BaseEntity
|
|
10
10
|
from core_lens.base.result import Result
|
|
11
11
|
from core_lens.schema.profile import Resolution, SchemaProfile
|
|
12
|
-
from core_lens.base.namespaces.plot import SubplotOn
|
|
12
|
+
from core_lens.base.namespaces.plot import SubplotOn, MapWithLegend
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class DummyEntity(BaseEntity):
|
|
@@ -79,7 +79,7 @@ def test_choropleth_basic(dummy_result: Result) -> None:
|
|
|
79
79
|
"""Test choropleth creates a lonboard Map correctly."""
|
|
80
80
|
plot_map = dummy_result.plot.choropleth("value")
|
|
81
81
|
|
|
82
|
-
assert isinstance(plot_map,
|
|
82
|
+
assert isinstance(plot_map, MapWithLegend)
|
|
83
83
|
assert len(plot_map.layers) == 1
|
|
84
84
|
layer = plot_map.layers[0]
|
|
85
85
|
assert isinstance(layer, lonboard.PolygonLayer)
|
|
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
|
|
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
|
|
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
|