eduloggen 1.4.1__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.
- eduloggen-1.4.1/.gitignore +227 -0
- eduloggen-1.4.1/CHANGELOG.md +278 -0
- eduloggen-1.4.1/LICENSE +21 -0
- eduloggen-1.4.1/PKG-INFO +258 -0
- eduloggen-1.4.1/README.md +195 -0
- eduloggen-1.4.1/docs/01_VISION.md +203 -0
- eduloggen-1.4.1/docs/02_PRD.md +1311 -0
- eduloggen-1.4.1/docs/03_ARCHITECTURE.md +3254 -0
- eduloggen-1.4.1/docs/04_EXPERIMENTAL_GENERATOR.md +266 -0
- eduloggen-1.4.1/docs/api/analysis.md +3 -0
- eduloggen-1.4.1/docs/api/benchmark.md +3 -0
- eduloggen-1.4.1/docs/api/cli.md +3 -0
- eduloggen-1.4.1/docs/api/config.md +3 -0
- eduloggen-1.4.1/docs/api/core.md +3 -0
- eduloggen-1.4.1/docs/api/datasets.md +3 -0
- eduloggen-1.4.1/docs/api/eduloggen.md +3 -0
- eduloggen-1.4.1/docs/api/evaluation.md +3 -0
- eduloggen-1.4.1/docs/api/generators.md +3 -0
- eduloggen-1.4.1/docs/api/index.md +24 -0
- eduloggen-1.4.1/docs/api/io.md +3 -0
- eduloggen-1.4.1/docs/api/models.md +3 -0
- eduloggen-1.4.1/docs/api/plugins.md +3 -0
- eduloggen-1.4.1/docs/api/privacy.md +3 -0
- eduloggen-1.4.1/docs/api/scenarios.md +3 -0
- eduloggen-1.4.1/docs/api/validation.md +3 -0
- eduloggen-1.4.1/docs/api/visualization.md +3 -0
- eduloggen-1.4.1/docs/api/workflow.md +3 -0
- eduloggen-1.4.1/docs/case-study-oulad.md +57 -0
- eduloggen-1.4.1/docs/changelog.md +55 -0
- eduloggen-1.4.1/docs/contributing.md +23 -0
- eduloggen-1.4.1/docs/getting-started.md +307 -0
- eduloggen-1.4.1/docs/index.md +35 -0
- eduloggen-1.4.1/docs/plugins.md +85 -0
- eduloggen-1.4.1/docs/privacy.md +54 -0
- eduloggen-1.4.1/mkdocs.yml +98 -0
- eduloggen-1.4.1/pyproject.toml +190 -0
- eduloggen-1.4.1/src/eduloggen/__init__.py +84 -0
- eduloggen-1.4.1/src/eduloggen/__main__.py +10 -0
- eduloggen-1.4.1/src/eduloggen/__version__.py +9 -0
- eduloggen-1.4.1/src/eduloggen/analysis/__init__.py +92 -0
- eduloggen-1.4.1/src/eduloggen/analysis/activity.py +201 -0
- eduloggen-1.4.1/src/eduloggen/analysis/features.py +83 -0
- eduloggen-1.4.1/src/eduloggen/analysis/graphs.py +59 -0
- eduloggen-1.4.1/src/eduloggen/analysis/profiles.py +48 -0
- eduloggen-1.4.1/src/eduloggen/analysis/result.py +148 -0
- eduloggen-1.4.1/src/eduloggen/analysis/sequences.py +52 -0
- eduloggen-1.4.1/src/eduloggen/analysis/service.py +77 -0
- eduloggen-1.4.1/src/eduloggen/analysis/sessionize.py +138 -0
- eduloggen-1.4.1/src/eduloggen/analysis/stats.py +81 -0
- eduloggen-1.4.1/src/eduloggen/analysis/strata.py +336 -0
- eduloggen-1.4.1/src/eduloggen/analysis/temporal.py +319 -0
- eduloggen-1.4.1/src/eduloggen/analysis/timing.py +74 -0
- eduloggen-1.4.1/src/eduloggen/analysis/transitions.py +181 -0
- eduloggen-1.4.1/src/eduloggen/api.py +294 -0
- eduloggen-1.4.1/src/eduloggen/benchmark/__init__.py +44 -0
- eduloggen-1.4.1/src/eduloggen/benchmark/protocol.py +183 -0
- eduloggen-1.4.1/src/eduloggen/benchmark/report.py +273 -0
- eduloggen-1.4.1/src/eduloggen/benchmark/runner.py +193 -0
- eduloggen-1.4.1/src/eduloggen/cli/__init__.py +11 -0
- eduloggen-1.4.1/src/eduloggen/cli/commands.py +659 -0
- eduloggen-1.4.1/src/eduloggen/cli/logs.py +70 -0
- eduloggen-1.4.1/src/eduloggen/cli/main.py +374 -0
- eduloggen-1.4.1/src/eduloggen/config/__init__.py +60 -0
- eduloggen-1.4.1/src/eduloggen/config/env.py +71 -0
- eduloggen-1.4.1/src/eduloggen/config/fingerprint.py +35 -0
- eduloggen-1.4.1/src/eduloggen/config/loader.py +98 -0
- eduloggen-1.4.1/src/eduloggen/config/merge.py +98 -0
- eduloggen-1.4.1/src/eduloggen/config/schema.py +445 -0
- eduloggen-1.4.1/src/eduloggen/core/__init__.py +66 -0
- eduloggen-1.4.1/src/eduloggen/core/constants.py +68 -0
- eduloggen-1.4.1/src/eduloggen/core/context.py +145 -0
- eduloggen-1.4.1/src/eduloggen/core/exceptions.py +178 -0
- eduloggen-1.4.1/src/eduloggen/core/typing.py +20 -0
- eduloggen-1.4.1/src/eduloggen/datasets/__init__.py +10 -0
- eduloggen-1.4.1/src/eduloggen/datasets/demo.py +208 -0
- eduloggen-1.4.1/src/eduloggen/evaluation/__init__.py +33 -0
- eduloggen-1.4.1/src/eduloggen/evaluation/clustering.py +149 -0
- eduloggen-1.4.1/src/eduloggen/evaluation/detection.py +262 -0
- eduloggen-1.4.1/src/eduloggen/generators/__init__.py +63 -0
- eduloggen-1.4.1/src/eduloggen/generators/artifact.py +150 -0
- eduloggen-1.4.1/src/eduloggen/generators/base.py +559 -0
- eduloggen-1.4.1/src/eduloggen/generators/calendar.py +277 -0
- eduloggen-1.4.1/src/eduloggen/generators/distributions.py +263 -0
- eduloggen-1.4.1/src/eduloggen/generators/markov.py +205 -0
- eduloggen-1.4.1/src/eduloggen/generators/registry.py +110 -0
- eduloggen-1.4.1/src/eduloggen/generators/semi_markov.py +86 -0
- eduloggen-1.4.1/src/eduloggen/generators/statistical.py +68 -0
- eduloggen-1.4.1/src/eduloggen/generators/tokenization.py +71 -0
- eduloggen-1.4.1/src/eduloggen/ingestion/__init__.py +12 -0
- eduloggen-1.4.1/src/eduloggen/io/__init__.py +108 -0
- eduloggen-1.4.1/src/eduloggen/io/base.py +176 -0
- eduloggen-1.4.1/src/eduloggen/io/coerce.py +170 -0
- eduloggen-1.4.1/src/eduloggen/io/corpus.py +394 -0
- eduloggen-1.4.1/src/eduloggen/io/ingest.py +222 -0
- eduloggen-1.4.1/src/eduloggen/io/mapping.py +422 -0
- eduloggen-1.4.1/src/eduloggen/io/quality.py +106 -0
- eduloggen-1.4.1/src/eduloggen/io/readers.py +269 -0
- eduloggen-1.4.1/src/eduloggen/io/streaming.py +31 -0
- eduloggen-1.4.1/src/eduloggen/io/writers.py +176 -0
- eduloggen-1.4.1/src/eduloggen/models/__init__.py +62 -0
- eduloggen-1.4.1/src/eduloggen/models/_checks.py +129 -0
- eduloggen-1.4.1/src/eduloggen/models/annotations.py +292 -0
- eduloggen-1.4.1/src/eduloggen/models/dataset.py +325 -0
- eduloggen-1.4.1/src/eduloggen/models/features.py +71 -0
- eduloggen-1.4.1/src/eduloggen/models/generator_model.py +140 -0
- eduloggen-1.4.1/src/eduloggen/models/records.py +322 -0
- eduloggen-1.4.1/src/eduloggen/models/schema_validate.py +126 -0
- eduloggen-1.4.1/src/eduloggen/models/vocab.py +97 -0
- eduloggen-1.4.1/src/eduloggen/plugins/__init__.py +34 -0
- eduloggen-1.4.1/src/eduloggen/plugins/registry.py +416 -0
- eduloggen-1.4.1/src/eduloggen/privacy/__init__.py +27 -0
- eduloggen-1.4.1/src/eduloggen/privacy/policies.py +173 -0
- eduloggen-1.4.1/src/eduloggen/py.typed +2 -0
- eduloggen-1.4.1/src/eduloggen/scenarios/__init__.py +98 -0
- eduloggen-1.4.1/src/eduloggen/scenarios/anomalies.py +496 -0
- eduloggen-1.4.1/src/eduloggen/scenarios/check.py +485 -0
- eduloggen-1.4.1/src/eduloggen/scenarios/clustering.py +258 -0
- eduloggen-1.4.1/src/eduloggen/scenarios/controls.py +249 -0
- eduloggen-1.4.1/src/eduloggen/scenarios/experiment.py +148 -0
- eduloggen-1.4.1/src/eduloggen/scenarios/inject.py +303 -0
- eduloggen-1.4.1/src/eduloggen/scenarios/profiles.py +1004 -0
- eduloggen-1.4.1/src/eduloggen/utils/__init__.py +12 -0
- eduloggen-1.4.1/src/eduloggen/utils/fs.py +112 -0
- eduloggen-1.4.1/src/eduloggen/utils/hashing.py +25 -0
- eduloggen-1.4.1/src/eduloggen/utils/seeding.py +61 -0
- eduloggen-1.4.1/src/eduloggen/validation/__init__.py +68 -0
- eduloggen-1.4.1/src/eduloggen/validation/base.py +158 -0
- eduloggen-1.4.1/src/eduloggen/validation/detailed.py +378 -0
- eduloggen-1.4.1/src/eduloggen/validation/distances.py +121 -0
- eduloggen-1.4.1/src/eduloggen/validation/metrics/__init__.py +51 -0
- eduloggen-1.4.1/src/eduloggen/validation/metrics/marginal.py +117 -0
- eduloggen-1.4.1/src/eduloggen/validation/metrics/privacy.py +142 -0
- eduloggen-1.4.1/src/eduloggen/validation/metrics/sequential.py +119 -0
- eduloggen-1.4.1/src/eduloggen/validation/metrics/temporal.py +32 -0
- eduloggen-1.4.1/src/eduloggen/validation/registry.py +77 -0
- eduloggen-1.4.1/src/eduloggen/validation/report.py +140 -0
- eduloggen-1.4.1/src/eduloggen/validation/service.py +135 -0
- eduloggen-1.4.1/src/eduloggen/visualization/__init__.py +73 -0
- eduloggen-1.4.1/src/eduloggen/visualization/base.py +155 -0
- eduloggen-1.4.1/src/eduloggen/visualization/dashboard.py +120 -0
- eduloggen-1.4.1/src/eduloggen/visualization/distributions.py +184 -0
- eduloggen-1.4.1/src/eduloggen/visualization/export.py +137 -0
- eduloggen-1.4.1/src/eduloggen/visualization/graphs.py +188 -0
- eduloggen-1.4.1/src/eduloggen/visualization/heatmaps.py +97 -0
- eduloggen-1.4.1/src/eduloggen/visualization/sankey.py +244 -0
- eduloggen-1.4.1/src/eduloggen/visualization/timeline.py +61 -0
- eduloggen-1.4.1/tests/__init__.py +5 -0
- eduloggen-1.4.1/tests/analysis/__init__.py +0 -0
- eduloggen-1.4.1/tests/analysis/conftest.py +63 -0
- eduloggen-1.4.1/tests/analysis/test_analyze.py +79 -0
- eduloggen-1.4.1/tests/analysis/test_fine_grained.py +218 -0
- eduloggen-1.4.1/tests/analysis/test_sessionize.py +125 -0
- eduloggen-1.4.1/tests/analysis/test_statistics.py +238 -0
- eduloggen-1.4.1/tests/benchmark/__init__.py +0 -0
- eduloggen-1.4.1/tests/benchmark/test_benchmark.py +266 -0
- eduloggen-1.4.1/tests/cli/__init__.py +0 -0
- eduloggen-1.4.1/tests/cli/conftest.py +80 -0
- eduloggen-1.4.1/tests/cli/test_api.py +119 -0
- eduloggen-1.4.1/tests/cli/test_cli.py +277 -0
- eduloggen-1.4.1/tests/config/__init__.py +0 -0
- eduloggen-1.4.1/tests/config/test_loading.py +209 -0
- eduloggen-1.4.1/tests/config/test_schema.py +181 -0
- eduloggen-1.4.1/tests/conftest.py +7 -0
- eduloggen-1.4.1/tests/core/__init__.py +0 -0
- eduloggen-1.4.1/tests/core/test_constants.py +63 -0
- eduloggen-1.4.1/tests/core/test_context.py +96 -0
- eduloggen-1.4.1/tests/core/test_exceptions.py +88 -0
- eduloggen-1.4.1/tests/datasets/__init__.py +0 -0
- eduloggen-1.4.1/tests/datasets/test_demo.py +25 -0
- eduloggen-1.4.1/tests/evaluation/__init__.py +0 -0
- eduloggen-1.4.1/tests/evaluation/test_clustering.py +79 -0
- eduloggen-1.4.1/tests/evaluation/test_detection.py +377 -0
- eduloggen-1.4.1/tests/generators/__init__.py +0 -0
- eduloggen-1.4.1/tests/generators/conftest.py +68 -0
- eduloggen-1.4.1/tests/generators/test_distributions.py +162 -0
- eduloggen-1.4.1/tests/generators/test_generators.py +423 -0
- eduloggen-1.4.1/tests/generators/test_registry_artifacts.py +285 -0
- eduloggen-1.4.1/tests/generators/test_start_times.py +111 -0
- eduloggen-1.4.1/tests/io/__init__.py +0 -0
- eduloggen-1.4.1/tests/io/test_coerce.py +153 -0
- eduloggen-1.4.1/tests/io/test_corpus.py +293 -0
- eduloggen-1.4.1/tests/io/test_ingest.py +223 -0
- eduloggen-1.4.1/tests/io/test_mapping.py +286 -0
- eduloggen-1.4.1/tests/io/test_readers_writers.py +290 -0
- eduloggen-1.4.1/tests/models/__init__.py +0 -0
- eduloggen-1.4.1/tests/models/conftest.py +33 -0
- eduloggen-1.4.1/tests/models/test_dataset.py +325 -0
- eduloggen-1.4.1/tests/models/test_records.py +267 -0
- eduloggen-1.4.1/tests/models/test_vocab_features.py +110 -0
- eduloggen-1.4.1/tests/plugins/__init__.py +0 -0
- eduloggen-1.4.1/tests/plugins/sample_plugin.py +83 -0
- eduloggen-1.4.1/tests/plugins/test_plugins.py +339 -0
- eduloggen-1.4.1/tests/privacy/__init__.py +0 -0
- eduloggen-1.4.1/tests/privacy/test_policies.py +131 -0
- eduloggen-1.4.1/tests/scenarios/__init__.py +0 -0
- eduloggen-1.4.1/tests/scenarios/test_anomalies.py +411 -0
- eduloggen-1.4.1/tests/scenarios/test_controls.py +499 -0
- eduloggen-1.4.1/tests/scenarios/test_profiles.py +552 -0
- eduloggen-1.4.1/tests/test_architecture.py +131 -0
- eduloggen-1.4.1/tests/test_package.py +64 -0
- eduloggen-1.4.1/tests/utils/__init__.py +0 -0
- eduloggen-1.4.1/tests/utils/test_utils.py +61 -0
- eduloggen-1.4.1/tests/validation/__init__.py +0 -0
- eduloggen-1.4.1/tests/validation/test_detailed.py +197 -0
- eduloggen-1.4.1/tests/validation/test_distances.py +71 -0
- eduloggen-1.4.1/tests/validation/test_validate.py +363 -0
- eduloggen-1.4.1/tests/visualization/__init__.py +0 -0
- eduloggen-1.4.1/tests/visualization/test_visualization.py +343 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
219
|
+
|
|
220
|
+
# macOS Finder metadata
|
|
221
|
+
.DS_Store
|
|
222
|
+
|
|
223
|
+
# Real datasets (download separately; never commit)
|
|
224
|
+
/OULAD/
|
|
225
|
+
studies/**/data/
|
|
226
|
+
studies/**/out/
|
|
227
|
+
studies/**/run.log
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to EduLogGen are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.4.1] - 2026-10-09
|
|
11
|
+
|
|
12
|
+
Ready for public release: PyPI publishing, citation metadata, an OULAD case
|
|
13
|
+
study, and more robust automatic profiles on real data.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Publishing: PyPI release workflow (trusted publishing, triggered by a
|
|
18
|
+
version tag, with a GitHub release from the changelog), documentation
|
|
19
|
+
deployment to GitHub Pages, `CITATION.cff`, `SECURITY.md`, and
|
|
20
|
+
`CODE_OF_CONDUCT.md`
|
|
21
|
+
- Case study on the Open University Learning Analytics Dataset
|
|
22
|
+
(`studies/oulad/`, docs page "Case study: OULAD") and a SoftwareX
|
|
23
|
+
manuscript draft (`paper/softwarex/`)
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- README: `pip install eduloggen`, a citation section, and absolute links so
|
|
28
|
+
they work on PyPI
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Markov back-off now says when every session has a single event, instead
|
|
33
|
+
of "reducing Markov order from 1 to 1"
|
|
34
|
+
- Automatic profiles on real data: event types below 1% of events are now
|
|
35
|
+
pooled into one `share:(other)` feature, and standardised features are
|
|
36
|
+
capped at ±3, so a handful of learners who used a rare activity no longer
|
|
37
|
+
form clusters of their own (found on OULAD, where k=4 produced clusters of
|
|
38
|
+
3 and 4 learners; now 42–80). `Standardizer` gains `clip` (default 3);
|
|
39
|
+
profile sets saved by 1.4.0 keep assigning without a cap.
|
|
40
|
+
- `fit_profiles` no longer fails when a profile only has very short sessions
|
|
41
|
+
(e.g. learners active one day a week): per-profile models now lower the
|
|
42
|
+
Markov order instead (`on_insufficient_data: backoff`) unless you set
|
|
43
|
+
`on_insufficient_data` yourself.
|
|
44
|
+
|
|
45
|
+
## [1.4.0] - 2026-10-09
|
|
46
|
+
|
|
47
|
+
Level 2, milestone M3: behavioural profiles and profile mixtures, and
|
|
48
|
+
generators that learn when sessions start.
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
- Generators now learn **when** sessions start (generator version 1.1):
|
|
53
|
+
models record session starts per hour of day (UTC) and per day of the
|
|
54
|
+
training period, and generation draws starts from that learned calendar.
|
|
55
|
+
Synthetic sessions no longer start in hours or on days without real
|
|
56
|
+
activity (on the demo course, 45% of sessions previously started in hours
|
|
57
|
+
with no real sessions; now none do). Models fitted with earlier versions
|
|
58
|
+
lack these counts and generate exactly as before. An explicit `calendar`
|
|
59
|
+
still takes precedence; `start_time` moves the learned pattern and remains
|
|
60
|
+
the earliest possible start.
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- Level 2, M3 — behavioural profiles (`eduloggen.scenarios`):
|
|
65
|
+
- `fit_profiles(mode="auto")`: seeded k-means++ (with restarts) on
|
|
66
|
+
standardised per-learner features (sessions, session length, duration,
|
|
67
|
+
success rate, event-type shares); profiles `profile_1..k` ordered by
|
|
68
|
+
size, with a table of distinguishing features; `ProfileSet.assign` maps
|
|
69
|
+
new (e.g. holdout) learners to the nearest training profile
|
|
70
|
+
- `fit_profiles(mode="provided")` with a `learner_id -> profile` mapping
|
|
71
|
+
(`load_profile_assignments` reads a CSV)
|
|
72
|
+
- `define_profiles` (manual): profiles defined by start and transition
|
|
73
|
+
probabilities, session lengths, sessions per learner, dwell times, and
|
|
74
|
+
activities, without real data; proportions alone are rejected
|
|
75
|
+
- one generator model per profile; profiles below `min_learners`
|
|
76
|
+
(default 5) are an error with guidance
|
|
77
|
+
- `generate_profiles`: mixture by share of synthetic learners, optional
|
|
78
|
+
per-profile controls and a shared calendar, ids remapped after mixing,
|
|
79
|
+
one learner-level `profile` annotation per synthetic learner
|
|
80
|
+
- `ProfileSet.save` / `load_profiles` with a content fingerprint
|
|
81
|
+
- `evaluate_clustering` (`eduloggen.evaluation`): adjusted Rand index,
|
|
82
|
+
normalised mutual information, purity, and the contingency table
|
|
83
|
+
- `SessionCalendar.daily`: optional per-day weights (used by learned
|
|
84
|
+
calendars; also available in calendar files)
|
|
85
|
+
|
|
86
|
+
## [1.3.0] - 2026-10-09
|
|
87
|
+
|
|
88
|
+
Fine-grained analysis: per activity, over time, per group, and a ranked view
|
|
89
|
+
of where synthetic data differs from real data.
|
|
90
|
+
|
|
91
|
+
### Added
|
|
92
|
+
|
|
93
|
+
- Per-activity profiles (`activity_profiles`): share, session and learner
|
|
94
|
+
reach, start/end shares, repeat share, dwell-time distribution,
|
|
95
|
+
predecessors and successors, success rate, mean score, companions
|
|
96
|
+
- Temporal analysis (`temporal_profile`, `deadline_effects`): timezone-aware
|
|
97
|
+
activity by hour and weekday, weekday x hour table, weekly trends from the
|
|
98
|
+
first session, daily counts, gaps between a learner's sessions, activity
|
|
99
|
+
ratio before deadlines
|
|
100
|
+
- Stratified analysis (FR-A.7; `analyze_by`, `split_by`, `session_groups`):
|
|
101
|
+
by `course`, `week`, `weekday`, `hour`, `learner_group`, or
|
|
102
|
+
`metadata:<key>`, with each group's mix compared to the overall mix
|
|
103
|
+
- Fine-grained validation (`compare_detailed`): ranked per-token,
|
|
104
|
+
per-transition (including invented and never-produced transitions),
|
|
105
|
+
per-length, per-hour, per-weekday, and per-group differences
|
|
106
|
+
- CLI: `analyze --detail --by --groups --timezone --deadlines`,
|
|
107
|
+
`validate --detailed --by --groups --timezone`; plot `activity_heatmap`
|
|
108
|
+
|
|
109
|
+
## [1.2.0] - 2026-10-09
|
|
110
|
+
|
|
111
|
+
Level 2, milestone M2: run controlled experiments and prove each control
|
|
112
|
+
took effect.
|
|
113
|
+
|
|
114
|
+
### Added
|
|
115
|
+
|
|
116
|
+
- Level 2, milestone M2: behavioural controls, course calendars, and
|
|
117
|
+
manipulation checks
|
|
118
|
+
- `eduloggen.scenarios.Controls` / `apply_controls`: `event_weights`
|
|
119
|
+
(reweight transitions into a token; `0` removes it), `dwell_scale`,
|
|
120
|
+
`session_length` (scale or fixed), `sessions_per_learner` (mean or fixed);
|
|
121
|
+
controls produce a new model whose parameters record them
|
|
122
|
+
- `eduloggen.generators.SessionCalendar`: period, timezone-aware hour and
|
|
123
|
+
weekday weights, deadline surges; `generate(..., calendar=...)` for every
|
|
124
|
+
generator; a learner's sessions never overlap and never start in
|
|
125
|
+
zero-weight hours
|
|
126
|
+
- `manipulation_check`: compares a controlled sample with an uncontrolled
|
|
127
|
+
baseline (same model, seed, size) for every control, the calendar, and
|
|
128
|
+
anomalies; sample-size-aware tolerances for distributional checks
|
|
129
|
+
- `ExperimentSettings` / `run_experiment` and CLI
|
|
130
|
+
`generate --experiment FILE` (controls, calendar, anomalies), writing
|
|
131
|
+
`manipulation_check.json` / `.md`
|
|
132
|
+
- Generator capability tag `supports_event_weights`
|
|
133
|
+
- Example `examples/configs/experiment.yaml`; experiment guide
|
|
134
|
+
|
|
135
|
+
## [1.1.0] - 2026-10-09
|
|
136
|
+
|
|
137
|
+
Level 2, milestone M1: benchmark anomaly detectors on synthetic data with
|
|
138
|
+
labelled ground truth.
|
|
139
|
+
|
|
140
|
+
### Added
|
|
141
|
+
|
|
142
|
+
- Level 2, milestone M1 (`docs/04_EXPERIMENTAL_GENERATOR.md`): labelled
|
|
143
|
+
anomaly injection for benchmarking detectors
|
|
144
|
+
- `eduloggen.scenarios`: five anomaly injectors — `event_frequency`,
|
|
145
|
+
`abnormal_timing`, `repetition`, `inactivity` (unusual but valid) and
|
|
146
|
+
`unexpected_transition` (invalid workflow); `inject_anomalies` with seeded,
|
|
147
|
+
disjoint session selection, one anomaly per session, no overlapping
|
|
148
|
+
sessions, and full id remapping so ids never reveal injected records;
|
|
149
|
+
`register_anomaly` for custom injectors
|
|
150
|
+
- `Annotations` ground-truth table in `eduloggen.models`, stored as
|
|
151
|
+
`annotations.csv` in corpora with its own fingerprint (`read_annotations`)
|
|
152
|
+
- `eduloggen.evaluation.evaluate_detection`: precision, recall, F1, false
|
|
153
|
+
positive rate, ROC-AUC, average precision, and recall per anomaly type and
|
|
154
|
+
per category
|
|
155
|
+
- CLI: `generate --anomalies FILE` and `evaluate --corpus --predictions`
|
|
156
|
+
- `privacy.remap_ids_with_mapping` returning the old-to-new id mapping
|
|
157
|
+
- Example `examples/configs/anomalies.yaml`; detector-testing guide
|
|
158
|
+
|
|
159
|
+
## [1.0.0] - 2026-10-09
|
|
160
|
+
|
|
161
|
+
First stable release: the complete Level 1 log generator. Learn from real
|
|
162
|
+
educational interaction logs, generate privacy-conscious synthetic sessions,
|
|
163
|
+
validate them, compare generators, and plot the results, from Python or the
|
|
164
|
+
`eduloggen` command line.
|
|
165
|
+
|
|
166
|
+
### Added
|
|
167
|
+
|
|
168
|
+
- `eduloggen.core` foundation package: exception hierarchy with machine-readable
|
|
169
|
+
codes, schema/artifact/report version constants, default `event_type`
|
|
170
|
+
vocabulary, immutable `RunContext`, and shared type aliases
|
|
171
|
+
- `eduloggen.models` canonical domain model: immutable, validated `LogRecord`,
|
|
172
|
+
`Session`, and `Participant`; `Dataset` / `SyntheticDataset` snapshots with
|
|
173
|
+
cross-record checks, content fingerprints, and manifests; `EventVocabulary`
|
|
174
|
+
with preserve / map-to-other / reject policies; `Feature`
|
|
175
|
+
- `eduloggen.io` ingestion and corpus I/O: streaming CSV/TSV, JSON Lines, and
|
|
176
|
+
Parquet readers and writers; `FieldMapping` with automatic casting,
|
|
177
|
+
timestamp formats and timezone policy, defaults, salted ID pseudonymization,
|
|
178
|
+
and metadata passthrough; `ingest()` with deduplication, sorting, strict
|
|
179
|
+
mode, and a value-free data quality report; atomic corpus directory
|
|
180
|
+
read/write with overwrite protection and fingerprint integrity checks
|
|
181
|
+
- `parquet` optional extra (`pyarrow`)
|
|
182
|
+
- `eduloggen.config`: strict, immutable `AppConfig` with sections from PRD §16.4;
|
|
183
|
+
YAML/TOML/JSON loading; precedence CLI > `EDULOGGEN_*` env allowlist > file >
|
|
184
|
+
defaults via `resolve_config`; config fingerprints; relative paths resolved
|
|
185
|
+
against the config file
|
|
186
|
+
- `FieldMapping.from_file` for YAML/TOML/JSON mapping files
|
|
187
|
+
- Example experiment config and field mapping under `examples/configs/`
|
|
188
|
+
- `pyyaml` as the first runtime dependency
|
|
189
|
+
- `eduloggen.utils`: versioned, order-independent seed derivation
|
|
190
|
+
(`derive_seed`, `make_rng`) and canonical JSON fingerprints
|
|
191
|
+
- `eduloggen.privacy`: seeded remapping of learner/session/event ids
|
|
192
|
+
(default for generated data) and metadata key stripping
|
|
193
|
+
- Architecture test enforcing the package dependency matrix (SAD §8.2)
|
|
194
|
+
- `eduloggen.analysis`: `sessionize()` with explicit / idle-timeout /
|
|
195
|
+
composite strategies and configurable tokenization; order-k transition
|
|
196
|
+
counts with start padding and additive smoothing; n-grams and rare-n-gram
|
|
197
|
+
detection; inter-event, sojourn, and duration statistics; session feature
|
|
198
|
+
rows, learner profiles, corpus features, and a navigation graph;
|
|
199
|
+
`analyze()` returning an `AnalysisResult` with JSON and Markdown exports
|
|
200
|
+
- `eduloggen.generators`: `BaseGenerator` contract (fit / generate / describe /
|
|
201
|
+
save / load) with shared population modelling (sessions per learner,
|
|
202
|
+
inter-session gaps, companion fields, courses) and seeded, id-remapped
|
|
203
|
+
output; `markov` (order-k with backoff and additive smoothing; empirical,
|
|
204
|
+
Poisson, or fixed lengths), `semi_markov` (per-token sojourn timing:
|
|
205
|
+
empirical, lognormal, gamma, exponential), and `independent` baseline;
|
|
206
|
+
generator registry; inspectable JSON model artifacts with integrity checks
|
|
207
|
+
- `GeneratorModel` in `eduloggen.models`
|
|
208
|
+
- `eduloggen.validation`: `validate()` comparing real and synthetic datasets
|
|
209
|
+
with 11 built-in metrics — `event_type_tvd`, `activity_jsd`,
|
|
210
|
+
`session_length_ks`, `session_duration_w1`, `interevent_time_ks`,
|
|
211
|
+
`bigram_tvd`, `transition_jsd`, `topn_path_overlap`, and privacy indicators
|
|
212
|
+
`exact_session_dup_rate`, `rare_ngram_replay_rate`, `nn_distance_p05`;
|
|
213
|
+
thresholds with pass / fail / info / skip statuses; `ValidationReport` with
|
|
214
|
+
JSON and Markdown output; metric registry for custom metrics
|
|
215
|
+
- `eduloggen.datasets.demo_dataset`: seeded, fully synthetic demo course corpus
|
|
216
|
+
- `eduloggen.benchmark`: protocol `session_fidelity_v1` with a learner-level
|
|
217
|
+
holdout split, multi-seed runs, mean/std aggregation, fit/generate timings,
|
|
218
|
+
a real-vs-real reference row, failure isolation per generator, and
|
|
219
|
+
`BenchmarkReport` JSON/Markdown output; `run_benchmark` in the API
|
|
220
|
+
- CLI commands `benchmark` and `demo`
|
|
221
|
+
- CI smoke job running the full pipeline and benchmark on the demo corpus
|
|
222
|
+
- `eduloggen.visualization` (optional `viz` extra): event/activity frequency,
|
|
223
|
+
session length and duration, inter-event time (histogram and ECDF), and
|
|
224
|
+
transition heatmap comparisons; sampled session timelines; validation and
|
|
225
|
+
benchmark charts; colour-blind-safe palette; reproducible PNG/SVG/PDF export;
|
|
226
|
+
`plot_datasets` for the standard set; CLI `plot` command
|
|
227
|
+
- `eduloggen.plugins`: entry-point discovery (`eduloggen.plugins.<kind>`) for
|
|
228
|
+
generators, metrics, readers, visualizers, and benchmark suites; contract
|
|
229
|
+
checks before registration; built-ins protected; broken plugins reported
|
|
230
|
+
without crashing (`strict=True` to fail); `register_plugin`,
|
|
231
|
+
`unregister_plugin`, `list_plugins`; CLI discovers at startup, `plugins`
|
|
232
|
+
shows sources, versions, and errors; run manifests record plugin versions
|
|
233
|
+
- Registration hooks: `register_reader` (with file suffixes),
|
|
234
|
+
`register_plot`, `register_protocol`; `BUILTIN_*` name sets
|
|
235
|
+
- Plugin author guide (`docs/plugins.md`)
|
|
236
|
+
- Node-link transition graph (`plot_transition_graph`) and multi-step Sankey
|
|
237
|
+
pathway diagram (`plot_sankey`, `sankey_flows`), matplotlib only
|
|
238
|
+
- `eduloggen.api` workflow façade (`load_config`, `ingest`, `load_dataset`,
|
|
239
|
+
`save_dataset`, `sessionize`, `analyze`, `fit_generator`, `generate`,
|
|
240
|
+
`validate`), re-exported from `import eduloggen`
|
|
241
|
+
- CLI commands `ingest`, `sessionize`, `analyze`, `fit`, `generate`,
|
|
242
|
+
`validate`, `info`, `plugins` with global `--config`, `--seed`, `-v`,
|
|
243
|
+
`--quiet`, `--json-logs`, `--force`, `--run-id`; exit codes 0/1/2;
|
|
244
|
+
`run_manifest.json` in every output directory; `python -m eduloggen`
|
|
245
|
+
- Documentation: privacy and responsible-use statement, end-to-end
|
|
246
|
+
getting-started guide, API reference pages for every package
|
|
247
|
+
|
|
248
|
+
### Changed
|
|
249
|
+
|
|
250
|
+
- Atomic output-directory writing moved to `eduloggen.utils.fs` and shared by
|
|
251
|
+
corpus and model artifacts
|
|
252
|
+
- SAD §8.2: `validation` may depend on `analysis` (documented in the matrix)
|
|
253
|
+
- Coverage gate raised from 80% to 95% (ADR-011)
|
|
254
|
+
- `io.format` and `ingest(format=...)` accept plugin reader names
|
|
255
|
+
- `eduloggen.cli` is now a package; the `eduloggen` console script entry
|
|
256
|
+
point is unchanged
|
|
257
|
+
- `eduloggen.ingestion` now re-exports the ingest API from `eduloggen.io`
|
|
258
|
+
|
|
259
|
+
## [0.1.0] - 2026-07-29
|
|
260
|
+
|
|
261
|
+
### Added
|
|
262
|
+
|
|
263
|
+
- Initial production-quality package skeleton (`src` layout)
|
|
264
|
+
- Package metadata and build configuration via `pyproject.toml`
|
|
265
|
+
- Tooling: Ruff, Black, MyPy, PyTest, pre-commit, MkDocs
|
|
266
|
+
- GitHub Actions continuous integration workflow
|
|
267
|
+
- Documented subpackage namespaces for ingestion, analysis, generators,
|
|
268
|
+
validation, visualization, and CLI
|
|
269
|
+
- MIT license and Semantic Versioning policy
|
|
270
|
+
|
|
271
|
+
[Unreleased]: https://github.com/anisbey1/EduLogGen/compare/v1.4.1...HEAD
|
|
272
|
+
[1.4.1]: https://github.com/anisbey1/EduLogGen/compare/v1.4.0...v1.4.1
|
|
273
|
+
[1.4.0]: https://github.com/anisbey1/EduLogGen/compare/v1.3.0...v1.4.0
|
|
274
|
+
[1.3.0]: https://github.com/anisbey1/EduLogGen/compare/v1.2.0...v1.3.0
|
|
275
|
+
[1.2.0]: https://github.com/anisbey1/EduLogGen/compare/v1.1.0...v1.2.0
|
|
276
|
+
[1.1.0]: https://github.com/anisbey1/EduLogGen/compare/v1.0.0...v1.1.0
|
|
277
|
+
[1.0.0]: https://github.com/anisbey1/EduLogGen/compare/v0.1.0...v1.0.0
|
|
278
|
+
[0.1.0]: https://github.com/anisbey1/EduLogGen/releases/tag/v0.1.0
|
eduloggen-1.4.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 anisbey1
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|