diff --git a/keystroke_dynamic_software-10690462/base/__init__.py b/keystroke_dynamic_software-10690462/base/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/keystroke_dynamic_software-10690462/base/__pycache__/__init__.cpython-310.pyc b/keystroke_dynamic_software-10690462/base/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..c4fc340
Binary files /dev/null and b/keystroke_dynamic_software-10690462/base/__pycache__/__init__.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/base/__pycache__/settings.cpython-310.pyc b/keystroke_dynamic_software-10690462/base/__pycache__/settings.cpython-310.pyc
new file mode 100644
index 0000000..f007cca
Binary files /dev/null and b/keystroke_dynamic_software-10690462/base/__pycache__/settings.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/base/__pycache__/urls.cpython-310.pyc b/keystroke_dynamic_software-10690462/base/__pycache__/urls.cpython-310.pyc
new file mode 100644
index 0000000..9ec1c04
Binary files /dev/null and b/keystroke_dynamic_software-10690462/base/__pycache__/urls.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/base/__pycache__/wsgi.cpython-310.pyc b/keystroke_dynamic_software-10690462/base/__pycache__/wsgi.cpython-310.pyc
new file mode 100644
index 0000000..b39b63d
Binary files /dev/null and b/keystroke_dynamic_software-10690462/base/__pycache__/wsgi.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/base/asgi.py b/keystroke_dynamic_software-10690462/base/asgi.py
new file mode 100644
index 0000000..305d1ba
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/base/asgi.py
@@ -0,0 +1,16 @@
+"""
+ASGI config for base project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'base.settings')
+
+application = get_asgi_application()
diff --git a/keystroke_dynamic_software-10690462/base/settings.py b/keystroke_dynamic_software-10690462/base/settings.py
new file mode 100644
index 0000000..93d6fd1
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/base/settings.py
@@ -0,0 +1,115 @@
+import os
+from pathlib import Path
+
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'django-insecure-es6v21dua^os=sy$p1sim^bu#xkfacu0e#vaxyzk*%g6_fw2ph'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+
+ # custom app
+ 'core',
+]
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'base.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [ os.path.join(BASE_DIR, '/templates')],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'base.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': BASE_DIR / 'db.sqlite3',
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/4.2/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/4.2/howto/static-files/
+
+STATIC_URL = 'static/'
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
diff --git a/keystroke_dynamic_software-10690462/base/urls.py b/keystroke_dynamic_software-10690462/base/urls.py
new file mode 100644
index 0000000..40e6a76
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/base/urls.py
@@ -0,0 +1,10 @@
+from django.conf import settings
+from django.urls import path, include
+from django.conf.urls.static import static
+
+urlpatterns = [
+ path('', include('core.urls'))
+] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+
+
+
diff --git a/keystroke_dynamic_software-10690462/base/wsgi.py b/keystroke_dynamic_software-10690462/base/wsgi.py
new file mode 100644
index 0000000..c82316e
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/base/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for base project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'base.settings')
+
+application = get_wsgi_application()
diff --git a/keystroke_dynamic_software-10690462/core/__init__.py b/keystroke_dynamic_software-10690462/core/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/keystroke_dynamic_software-10690462/core/__pycache__/__init__.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..80a42f9
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/__pycache__/__init__.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/__pycache__/apps.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/__pycache__/apps.cpython-310.pyc
new file mode 100644
index 0000000..23db066
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/__pycache__/apps.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/__pycache__/forms.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/__pycache__/forms.cpython-310.pyc
new file mode 100644
index 0000000..8074266
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/__pycache__/forms.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/__pycache__/models.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/__pycache__/models.cpython-310.pyc
new file mode 100644
index 0000000..8757e04
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/__pycache__/models.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/__pycache__/urls.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/__pycache__/urls.cpython-310.pyc
new file mode 100644
index 0000000..846e4ba
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/__pycache__/urls.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/__pycache__/utils.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/__pycache__/utils.cpython-310.pyc
new file mode 100644
index 0000000..25cb4f8
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/__pycache__/utils.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/__pycache__/views.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/__pycache__/views.cpython-310.pyc
new file mode 100644
index 0000000..2e6b724
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/__pycache__/views.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/apps.py b/keystroke_dynamic_software-10690462/core/apps.py
new file mode 100644
index 0000000..8115ae6
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class CoreConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'core'
diff --git a/keystroke_dynamic_software-10690462/core/forms.py b/keystroke_dynamic_software-10690462/core/forms.py
new file mode 100644
index 0000000..3c572e8
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/forms.py
@@ -0,0 +1,15 @@
+from django import forms
+
+class PangramForm(forms.Form):
+ pangram = forms.CharField(required=True, widget=forms.TextInput(
+ attrs={
+ 'placeholder': 'Enter above text here for mood test',
+ 'autocomplete': "off"
+ }
+ ))
+ mood = forms.CharField(required=False, widget=forms.TextInput(attrs={'type': "hidden"}))
+ kd = forms.CharField(required=False, widget=forms.TextInput(attrs={'type': "hidden"}))
+ dukl = forms.CharField(required=False, widget=forms.TextInput(attrs={'type': "hidden"}))
+ ddkl = forms.CharField(required=False, widget=forms.TextInput(attrs={'type': "hidden"}))
+ udkl = forms.CharField(required=False, widget=forms.TextInput(attrs={'type': "hidden"}))
+ uukl = forms.CharField(required=False, widget=forms.TextInput(attrs={'type': "hidden"}))
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/management/__init__.py b/keystroke_dynamic_software-10690462/core/management/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/keystroke_dynamic_software-10690462/core/management/__pycache__/__init__.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/management/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..9313430
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/management/__pycache__/__init__.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/management/commands/__init__.py b/keystroke_dynamic_software-10690462/core/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/keystroke_dynamic_software-10690462/core/management/commands/__pycache__/__init__.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/management/commands/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..2552cc2
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/management/commands/__pycache__/__init__.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/management/commands/__pycache__/seed.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/management/commands/__pycache__/seed.cpython-310.pyc
new file mode 100644
index 0000000..0761fe8
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/management/commands/__pycache__/seed.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/management/commands/seed.py b/keystroke_dynamic_software-10690462/core/management/commands/seed.py
new file mode 100644
index 0000000..a56ee3c
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/management/commands/seed.py
@@ -0,0 +1,37 @@
+from django.core.management.base import BaseCommand, CommandError
+from core.models import Pangram
+
+PANGRAMS = [
+ "The quick brown fox jumps over a lazy dog",
+ "Show mangled quartz flip vibe exactly",
+ "Pack my box with five dozen liquor jugs",
+ "Jumpy halfling dwarves pick quartz box",
+ "Vex quest wizard, judge my backflop hand",
+ "The jay, pig, fox, zebra and my wolves quack",
+ "Quest judge wizard bonks foxy chimp love",
+ "Fix problem quickly with galvanized jets",
+ "Heavy boxes perform quick waltzes and jigs",
+ "A wizard's job is to vex chumps quickly in fog",
+ "When zombies arrive, quickly fax judge Pat",
+ "Pack my red box with five dozen quality jugs",
+ "Blewjs computer quiz favored proxy hacking",
+ "Who packed five dozen old quart jugs in my box",
+ "Both fickle dwarves jinx my pig quiz",
+ "Fat hag dwarves quickly zap jinx mob",
+ "Fox dwarves chop my talking quiz job",
+ "Public junk dwarves quiz mighty fox",
+ "How quickly daft jumping zebras vex",
+ "Two driven jocks help fax my big quiz",
+ "Go, lazy fat vixen, be shrewd, jump quick",
+ "Big dwarves heckle my top quiz of jinx",
+ "Public junk dwarves hug my quartz fox",
+ "Five jumping wizards hex bolty quick",
+ "Five hexing wizard bots jump quickly",
+]
+
+class Command(BaseCommand):
+ help = "Seed pangram to the database"
+
+ def handle(self, *args, **options):
+ for statement in PANGRAMS:
+ Pangram.objects.create(statement=statement.lower())
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/migrations/0001_initial.py b/keystroke_dynamic_software-10690462/core/migrations/0001_initial.py
new file mode 100644
index 0000000..6a0b403
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/migrations/0001_initial.py
@@ -0,0 +1,93 @@
+# Generated by Django 4.2 on 2023-04-12 23:04
+
+from django.conf import settings
+import django.contrib.auth.models
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('auth', '0012_alter_user_first_name_max_length'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Pangram',
+ fields=[
+ ('id', models.IntegerField(primary_key=True, serialize=False)),
+ ('statement', models.CharField(max_length=220, unique=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Profile',
+ fields=[
+ ('user_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
+ ('temp_password', models.CharField(max_length=25)),
+ ('backup_secret', models.CharField(max_length=25)),
+ ('trottle', models.IntegerField(default=0)),
+ ('pangram', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.pangram')),
+ ],
+ options={
+ 'verbose_name': 'user',
+ 'verbose_name_plural': 'users',
+ 'abstract': False,
+ },
+ bases=('auth.user',),
+ managers=[
+ ('objects', django.contrib.auth.models.UserManager()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='UserBaseKeyStroke',
+ fields=[
+ ('id', models.IntegerField(primary_key=True, serialize=False)),
+ ('mood', models.CharField(choices=[('h', 'happy'), ('s', 'sad'), ('n', 'normal'), ('a', 'angry')], max_length=1)),
+ ('kd', models.DurationField()),
+ ('uukl', models.DurationField()),
+ ('udkl', models.DurationField()),
+ ('ddkl', models.DurationField()),
+ ('dukl', models.DurationField()),
+ ('profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='base_keystroke', to='core.profile')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='UnauthorizedAuthStroke',
+ fields=[
+ ('id', models.IntegerField(primary_key=True, serialize=False)),
+ ('mood', models.CharField(choices=[('h', 'happy'), ('s', 'sad'), ('n', 'normal'), ('a', 'angry')], max_length=1)),
+ ('kd', models.DurationField()),
+ ('uukl', models.DurationField()),
+ ('udkl', models.DurationField()),
+ ('ddkl', models.DurationField()),
+ ('dukl', models.DurationField()),
+ ('ed', models.FloatField(blank=True, help_text='euclidean distance', null=True)),
+ ('er', models.IntegerField(blank=True, help_text='error rate', null=True)),
+ ('profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='unauth_strokes', to='core.profile')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='AuthKeyStroke',
+ fields=[
+ ('id', models.IntegerField(primary_key=True, serialize=False)),
+ ('mood', models.CharField(choices=[('h', 'happy'), ('s', 'sad'), ('n', 'normal'), ('a', 'angry')], max_length=1)),
+ ('kd', models.DurationField()),
+ ('uukl', models.DurationField()),
+ ('udkl', models.DurationField()),
+ ('ddkl', models.DurationField()),
+ ('dukl', models.DurationField()),
+ ('ed', models.FloatField(blank=True, help_text='euclidean distance', null=True)),
+ ('er', models.IntegerField(blank=True, help_text='error rate', null=True)),
+ ('profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='auth_strokes', to='core.profile')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/keystroke_dynamic_software-10690462/core/migrations/0002_remove_profile_temp_password.py b/keystroke_dynamic_software-10690462/core/migrations/0002_remove_profile_temp_password.py
new file mode 100644
index 0000000..637525f
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/migrations/0002_remove_profile_temp_password.py
@@ -0,0 +1,17 @@
+# Generated by Django 4.2 on 2023-04-13 06:30
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='profile',
+ name='temp_password',
+ ),
+ ]
diff --git a/keystroke_dynamic_software-10690462/core/migrations/0003_alter_authkeystroke_ddkl_alter_authkeystroke_dukl_and_more.py b/keystroke_dynamic_software-10690462/core/migrations/0003_alter_authkeystroke_ddkl_alter_authkeystroke_dukl_and_more.py
new file mode 100644
index 0000000..6ea03a3
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/migrations/0003_alter_authkeystroke_ddkl_alter_authkeystroke_dukl_and_more.py
@@ -0,0 +1,88 @@
+# Generated by Django 4.2 on 2023-04-13 20:02
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0002_remove_profile_temp_password'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='authkeystroke',
+ name='ddkl',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='authkeystroke',
+ name='dukl',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='authkeystroke',
+ name='kd',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='authkeystroke',
+ name='udkl',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='authkeystroke',
+ name='uukl',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='unauthorizedauthstroke',
+ name='ddkl',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='unauthorizedauthstroke',
+ name='dukl',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='unauthorizedauthstroke',
+ name='kd',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='unauthorizedauthstroke',
+ name='udkl',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='unauthorizedauthstroke',
+ name='uukl',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='userbasekeystroke',
+ name='ddkl',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='userbasekeystroke',
+ name='dukl',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='userbasekeystroke',
+ name='kd',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='userbasekeystroke',
+ name='udkl',
+ field=models.JSONField(),
+ ),
+ migrations.AlterField(
+ model_name='userbasekeystroke',
+ name='uukl',
+ field=models.JSONField(),
+ ),
+ ]
diff --git a/keystroke_dynamic_software-10690462/core/migrations/__init__.py b/keystroke_dynamic_software-10690462/core/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/keystroke_dynamic_software-10690462/core/migrations/__pycache__/0001_initial.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/migrations/__pycache__/0001_initial.cpython-310.pyc
new file mode 100644
index 0000000..ded76cd
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/migrations/__pycache__/0001_initial.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/migrations/__pycache__/0002_remove_profile_temp_password.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/migrations/__pycache__/0002_remove_profile_temp_password.cpython-310.pyc
new file mode 100644
index 0000000..024241a
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/migrations/__pycache__/0002_remove_profile_temp_password.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/migrations/__pycache__/0003_alter_authkeystroke_ddkl_alter_authkeystroke_dukl_and_more.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/migrations/__pycache__/0003_alter_authkeystroke_ddkl_alter_authkeystroke_dukl_and_more.cpython-310.pyc
new file mode 100644
index 0000000..a48e0ae
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/migrations/__pycache__/0003_alter_authkeystroke_ddkl_alter_authkeystroke_dukl_and_more.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/migrations/__pycache__/__init__.cpython-310.pyc b/keystroke_dynamic_software-10690462/core/migrations/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..16a7661
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/migrations/__pycache__/__init__.cpython-310.pyc differ
diff --git a/keystroke_dynamic_software-10690462/core/models.py b/keystroke_dynamic_software-10690462/core/models.py
new file mode 100644
index 0000000..ef6d2ac
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/models.py
@@ -0,0 +1,195 @@
+import json
+from django.db import models
+from django.contrib.auth.models import User
+
+from . import utils
+
+
+class Pangram(models.Model):
+ """Pangram model holds pre-seeded statement which would be used
+ for authentication purposes
+ """
+ id = models.IntegerField(primary_key=True)
+ statement = models.CharField(max_length=220, unique=True)
+
+ def __str__(self) -> str:
+ """Returns a string representation of model instance"""
+ return self.statement
+
+# No need for Mood model
+# class MoodState(models.Model):
+
+
+class Profile(User):
+ """Extends user model"""
+ last_login = None
+ first_name = None
+ last_name = None
+ email = None
+ pangram = models.ForeignKey(Pangram, on_delete=models.SET_NULL, null=True)
+ backup_secret = models.CharField(max_length=25)
+ trottle = models.IntegerField(default=0)
+
+ def __str__(self) -> str:
+ """Returns string representation of model instance"""
+ return self.username
+
+ def save(self, *args, **kwargs):
+ self.username = self.username.lower()
+ super().save(args, kwargs)
+
+ def mood_euclidean_distances(self, mood, new_strokes):
+ """Computes mood euclidean distance of a profile"""
+ base_mood = self.base_keystroke.get(mood=mood)
+ distances = [
+ utils.calculate_euclidean_distance(
+ json.loads(base_mood.kd),
+ json.loads(new_strokes['kd'])
+ ),
+ utils.calculate_euclidean_distance(
+ json.loads(base_mood.ddkl),
+ json.loads(new_strokes['ddkl'])
+ ),
+ utils.calculate_euclidean_distance(
+ json.loads(base_mood.udkl),
+ json.loads(new_strokes['udkl'])
+ ),
+ utils.calculate_euclidean_distance(
+ json.loads(base_mood.dukl),
+ json.loads(new_strokes['dukl'])
+ ),
+ utils.calculate_euclidean_distance(
+ json.loads(base_mood.uukl),
+ json.loads(new_strokes['uukl'])
+ ),
+ ]
+
+ return distances, [
+ json.loads(base_mood.kd),
+ json.loads(base_mood.ddkl),
+ json.loads(base_mood.udkl),
+ json.loads(base_mood.dukl),
+ json.loads(base_mood.uukl)
+ ]
+
+ def update_auth_login_keystroke_dynamic(self, mood, er, ed, kd, udkl, uukl, dukl, ddkl, tilt):
+ """Updates user's keystroke dynamic euclidean distance and error rate
+
+ mode --> the keystroke mood whose dynamic is to be update
+ ed --> the new euclidean distance
+ er --> the new error rate
+ tilt --> either high or reset. high when authentication fails else reset
+
+ kd
+ udkl
+ uukl
+ dukl
+ ddkl
+ """
+ mood_stroke = self.auth_strokes.get(mood=mood)
+ mood_stroke.kd = kd
+ mood_stroke.udkl = udkl
+ mood_stroke.dukl = dukl
+ mood_stroke.ddkl = ddkl
+ mood_stroke.uukl = uukl
+ mood_stroke.er = er
+ mood_stroke.ed = ed
+ mood_stroke.save()
+
+ self.increase_trottle() if tilt == 'high' else self.reset_trottle()
+
+ def create_unauth_login_keystroke_dynamic(
+ self, mood, er, ed, kd, udkl, uukl, dukl, ddkl, tilt
+ ):
+ """Updates user's keystroke dynamic euclidean distance and error rate
+
+ mode --> the keystroke mood whose dynamic is to be update
+ ed --> the new euclidean distance
+ er --> the new error rate
+ tilt --> either high or reset. high when authentication fails else reset
+ """
+ self.unauth_strokes.create(
+ er = er,
+ ed = ed,
+ kd = kd,
+ udkl = udkl,
+ dukl = dukl,
+ ddkl = ddkl,
+ uukl = uukl,
+ mood = mood
+ )
+
+ self.increase_trottle() if tilt == 'high' else self.reset_trottle()
+
+ def reset_trottle(self):
+ self.trottle = 0
+ self.save()
+
+ def increase_trottle(self):
+ self.trottle = self.trottle + 1
+ self.save()
+
+ def save_authentication_login_strokes_by_form_type(
+ self, mood, er, ed, kd, udkl, uukl, dukl, ddkl, tilt, form_type
+ ):
+ if form_type == "auth":
+ self.update_auth_login_keystroke_dynamic(
+ mood, er, ed, kd, udkl, uukl, dukl, ddkl, tilt)
+
+ elif form_type == "unauth":
+ self.create_unauth_login_keystroke_dynamic(
+ mood, er, ed, kd, udkl, uukl, dukl, ddkl, tilt)
+
+
+
+
+
+class UserBaseKeyStroke(models.Model):
+ """Captures user's base keystroke"""
+ id = models.IntegerField(primary_key=True)
+ profile = models.ForeignKey(
+ Profile, on_delete=models.CASCADE, related_name="base_keystroke")
+ mood = models.CharField(max_length=1, choices=(
+ ('h', 'happy'), ('s', 'sad'),
+ ('n', 'normal'), ('a', 'angry')
+ ))
+ kd = models.JSONField(blank=False, null=False)
+ uukl = models.JSONField(blank=False, null=False)
+ udkl = models.JSONField(blank=False, null=False)
+ ddkl = models.JSONField(blank=False, null=False)
+ dukl = models.JSONField(blank=False, null=False)
+
+
+class AuthStrokeCommonInfo(models.Model):
+ """Capture user's authentication keystroke"""
+ id = models.IntegerField(primary_key=True)
+ mood = models.CharField(max_length=1, choices=(
+ ('h', 'happy'), ('s', 'sad'),
+ ('n', 'normal'), ('a', 'angry')
+ ))
+ kd = models.JSONField(blank=False, null=False)
+ uukl = models.JSONField(blank=False, null=False)
+ udkl = models.JSONField(blank=False, null=False)
+ ddkl = models.JSONField(blank=False, null=False)
+ dukl = models.JSONField(blank=False, null=False)
+ ed = models.FloatField(blank=True, null=True, help_text="euclidean distance")
+ er = models.IntegerField(help_text="error rate", blank=True, null=True)
+
+ class Meta:
+ abstract = True
+
+ def __str__(self) -> str:
+ """Return string representation of model instance"""
+ return "Auth stroke for {mood} mood with euclidean distance of {ed} and error rate of {er}".format(
+ mood=self.mood, ed=self.ed, er=self.er
+ )
+
+
+
+class AuthKeyStroke(AuthStrokeCommonInfo):
+ """Captures strokes for authenticated/authorized user"""
+ profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='auth_strokes')
+
+class UnauthorizedAuthStroke(AuthStrokeCommonInfo):
+ """Captures strokes for unauthenticated/unauthorized user"""
+ profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='unauth_strokes')
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/static/core/angry.jpg b/keystroke_dynamic_software-10690462/core/static/core/angry.jpg
new file mode 100644
index 0000000..bca8b66
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/static/core/angry.jpg differ
diff --git a/keystroke_dynamic_software-10690462/core/static/core/happy.jpeg b/keystroke_dynamic_software-10690462/core/static/core/happy.jpeg
new file mode 100644
index 0000000..1a932c7
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/static/core/happy.jpeg differ
diff --git a/keystroke_dynamic_software-10690462/core/static/core/logo.jpeg b/keystroke_dynamic_software-10690462/core/static/core/logo.jpeg
new file mode 100644
index 0000000..9a483be
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/static/core/logo.jpeg differ
diff --git a/keystroke_dynamic_software-10690462/core/static/core/normal.jpg b/keystroke_dynamic_software-10690462/core/static/core/normal.jpg
new file mode 100644
index 0000000..11f1056
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/static/core/normal.jpg differ
diff --git a/keystroke_dynamic_software-10690462/core/static/core/sad.jpg b/keystroke_dynamic_software-10690462/core/static/core/sad.jpg
new file mode 100644
index 0000000..3678494
Binary files /dev/null and b/keystroke_dynamic_software-10690462/core/static/core/sad.jpg differ
diff --git a/keystroke_dynamic_software-10690462/core/static/core/scripts/base.js b/keystroke_dynamic_software-10690462/core/static/core/scripts/base.js
new file mode 100644
index 0000000..d71e48c
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/static/core/scripts/base.js
@@ -0,0 +1,190 @@
+
+const KD = [];
+const DDKL = [];
+const UUKL = [];
+const DUKL = [];
+const UDKL = [];
+
+// logs all key strokes with their timestamp
+const key_stroke_time_stamp = [
+ // {
+ // "key": "a",
+ // "timePressed": xxxx
+ // "timeReleased": xxxx
+ // },
+]
+
+
+
+/* This event captures key strokes. It sets the key stroked and the
+* time key pressed down
+*/
+const capture_key_press_event = (e) => {
+ if (validate_key_captured(e.key) === false){
+ return
+ }
+
+ key_stroke_time_stamp.push(
+ {
+ "key": e.key,
+ "timePressed": new Date().getUTCMilliseconds() || 0,
+ "timeReleased": undefined
+ }
+ )
+}
+
+
+/* This event retrieves the key released from key strokes.
+ * It updates the timeReleased of the corresponding released key which
+ * was pressed down.
+ *
+ * Futher computations happens here
+ * -- kd computation
+ */
+const capture_key_release_event = (e) => {
+ if (validate_key_captured(e.key) === false){
+ return
+ }
+
+ // as there can be more than one of same key captured in keystroke,
+ // filter is used so all key stroked having same key can be collected
+ let similar_key_strokes = key_stroke_time_stamp.filter( stroke => stroke.key === e.key)
+
+ // the last item on the list would be the most recent stroke captured
+ let key_stroke = similar_key_strokes[similar_key_strokes.length - 1]
+ key_stroke.timeReleased = new Date().getUTCMilliseconds() || 0;
+
+ // captures kd -- time between the key pressed and the key release
+ compute_key_kd(key_stroke=key_stroke)
+}
+
+
+/* Computes time between the key pressed and the key release
+ *
+ * @param key_stroke hold the key stroked and the time it was pressed and released
+ */
+const compute_key_kd = (key_stroke) => {
+ KD.push((key_stroke.timePressed - key_stroke.timeReleased) || 0)
+}
+
+
+/* Computes time between current key release and the next key pressed
+ *
+ * @param prev_key_release_timestamp a numeric value representing timestamp for which the
+ * last key released was captured
+ *
+ * @param current_key_pressed_timestamp a numeric value representing timestamp for which the
+ * current key captured was pressed down
+ */
+const compute_udkl = (prev_key_release_timestamp, current_key_pressed_timestamp) => {
+ UDKL.push((prev_key_release_timestamp - current_key_pressed_timestamp) || 0)
+}
+
+
+/* Computes time differences between two successful key releases
+ *
+ * @param prev_key_release_timestamp holds timestamp for which the
+ * previous key pressed down was released
+ *
+ * @param current_key_release_timestamp holds timestamp for which the
+ * current key pressed down is released
+ */
+const compute_uukl = (prev_key_release_timestamp, current_key_release_timestamp) => {
+ UUKL.push((prev_key_release_timestamp - current_key_release_timestamp) || 0)
+}
+
+
+/* Computes the time differences an immediate older key press down timestamp
+ * and most recent key release timestamp
+ *
+ * @param prev_key_pressed_down_timestamp holds timestamp where which a key is
+ * pressed down. In this case the timestamp should be of a key that was
+ * previously pressed down
+ *
+ * @param current_key_release_timestamp holds timestamp where a the current key is
+ * released.
+ */
+const compute_dukl = (prev_key_pressed_down_timestamp, current_key_release_timestamp) => {
+ DUKL.push((prev_key_pressed_down_timestamp - current_key_release_timestamp) || 0)
+}
+
+
+/* Computes the time differences an immediate older key press down timestamp
+ * and most recent key pressed down timestamp
+ *
+ * @param prev_key_pressed_down_timestamp holds timestamp where which a key is
+ * pressed down. In this case the timestamp should be of a key that was
+ * previously pressed down
+ *
+ * @param current_key_release_timestamp holds timestamp where a the current key is
+ * pressed down.
+ */
+const compute_ddkl = (prev_key_pressed_down_timestamp, current_key_pressed_down_timestamp) => {
+ DDKL.push((prev_key_pressed_down_timestamp - current_key_pressed_down_timestamp) || 0)
+}
+
+
+/* Validates captured key and return true if the key is either an alphabet or puntuation
+ * and false otherwise
+ *
+ * @param key a string representing what a user has pressed on the keyboard
+ */
+const validate_key_captured = (key) => {
+ const allowed_keys = [
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ ',', '.', '-', "'", ';', ':'
+ ]
+ return allowed_keys.find(item => item.toLowerCase() === key.toLowerCase()) ? true : false;
+}
+
+
+const set_up_keystrokes_timestamps_to_form_field = () => {
+ document.getElementById('id_kd').value = JSON.stringify(KD)
+ document.getElementById('id_udkl').value = JSON.stringify(UDKL)
+ document.getElementById('id_ddkl').value = JSON.stringify(DDKL)
+ document.getElementById('id_uukl').value = JSON.stringify(UUKL)
+ document.getElementById('id_dukl').value = JSON.stringify(DUKL)
+}
+
+
+const compute_key_strokes = () => {
+ key_stroke_time_stamp.forEach((current_key_pressed, current_index) => {
+ if (current_index > 0) {
+ const last_key_released = key_stroke_time_stamp[
+ key_stroke_time_stamp.length - (current_index + 1)
+ ]
+
+ compute_udkl(
+ prev_key_release_timestamp=last_key_released.timeReleased,
+ current_key_pressed_timestamp=current_key_pressed.timePressed
+ )
+
+ compute_ddkl(
+ prev_key_pressed_down_timestamp = last_key_released.timePressed,
+ current_key_pressed_down_timestamp = current_key_pressed.timePressed
+ )
+
+ compute_uukl(
+ prev_key_release_timestamp=last_key_released.timeReleased,
+ current_key_release_timestamp=current_key_pressed.timeReleased
+ )
+
+ compute_dukl(
+ prev_key_pressed_down_timestamp = last_key_released.timePressed,
+ current_key_release_timestamp = current_key_pressed.timeReleased
+ )
+ }
+ })
+
+ set_up_keystrokes_timestamps_to_form_field()
+}
+
+
+
+const form_custom_submission = (e) => {
+ e.preventDefault();
+ compute_key_strokes()
+ document.querySelector('form').submit()
+}
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/angry.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/angry.html
new file mode 100644
index 0000000..d587bd3
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/angry.html
@@ -0,0 +1,59 @@
+{% extends 'core/templates/base.html' %}
+{% load static %}
+
+{% block head %}
+
Complete Angry Mood Test
+
+{% endblock head %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+
+
+
Hello {{request.user.profile.username|upper}}, this is a ANGRY mood test
+ {{ request.user.profile.pangram.statement }}
+
+
+
+ enter above pangram in red below
+
+
+
+
+{% endblock content %}
+
+
+{% block script %}
+
+{% endblock script %}
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/base.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/base.html
new file mode 100644
index 0000000..ea30f88
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/base.html
@@ -0,0 +1,55 @@
+{% load static %}
+
+
+
+
+
+
+
+ {% block head %} {% endblock head %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% if messages %}
+
+ {% for message in messages %}
+ {{ message }}
+ {% endfor %}
+
+ {% endif %}
+
+ {% block content %} {% endblock content %}
+
+
+
+
+
+
+
+
+
+ {% block script %} {% endblock script %}
+
+
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/choose-mood.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/choose-mood.html
new file mode 100644
index 0000000..4df728a
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/choose-mood.html
@@ -0,0 +1,98 @@
+{% extends 'core/templates/base.html' %}
+{% load i18n %}
+{% load static %}
+
+{% block head %}
+Choose A Mood
+{% endblock head %}
+
+{% block content %}
+
+
+
+
+
+
+ {{request.user.profile.username|upper|default:request.session.username}} How're you feeling Today?
+
+
+
+
+ {% csrf_token %}
+
+
+
+
+
+
+
+
+
+
+
Happy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Normal
+
+
+
+
+
+
+
+
+
+
+
+
Angry
+
+
+
+
+
+
+ Proceed
+
+
+
+
+{% endblock content %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/dashboard.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/dashboard.html
new file mode 100644
index 0000000..b5edda7
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/dashboard.html
@@ -0,0 +1,14 @@
+{% extends 'core/templates/base.html' %}
+{% block head %}
+Dashboard
+{% endblock head %}
+
+{% block content %}
+
+
+
logout
+
Welcome {{ request.user.username }}
+
Secret Key :: {{ request.user.profile.backup_secret }}
+
+
+{% endblock content %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/forget_password_validate_username.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/forget_password_validate_username.html
new file mode 100644
index 0000000..1659c8b
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/forget_password_validate_username.html
@@ -0,0 +1,42 @@
+{% extends 'core/templates/base.html' %}
+{% load static %}
+{% block head %}
+ Forget Password - validate user
+{% endblock head %}
+
+{% block content %}
+
+
+
+ Forgot Password
+
+
+ {% csrf_token %}
+ Enter Username
+
+
+
+
+
+ Continue
+
+
+
+
+
+{% endblock content %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/happy.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/happy.html
new file mode 100644
index 0000000..c5078e5
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/happy.html
@@ -0,0 +1,58 @@
+{% extends 'core/templates/base.html' %}
+{% load i18n %}
+{% load static %}
+
+{% block head %}
+ Complete Happy Mood Test
+
+{% endblock head %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+
+
Hello {{request.user.profile.username|upper}}, this is a HAPPY mood test
+ {{ request.user.profile.pangram.statement }}
+
+
+
+ enter above pangram in red below
+
+
+{% csrf_token %}
+
+
+
+
+
+
+
+ Submit
+
+
+
+{% endblock content %}
+
+{% block script %}
+
+{% endblock script %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/index.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/index.html
new file mode 100644
index 0000000..6877559
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/index.html
@@ -0,0 +1,18 @@
+{% extends 'core/templates/base.html' %}
+{% block head %}
+ Index
+{% endblock head %}
+
+{% block content %}
+
+
+
Welcome to Earth
+
To explore further, you have two choices
+
+
+
+
+{% endblock content %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/normal.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/normal.html
new file mode 100644
index 0000000..9e60d8d
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/normal.html
@@ -0,0 +1,58 @@
+{% extends 'core/templates/base.html' %}
+{% load i18n %}
+{% load static %}
+
+{% block head %}
+ Complete Normal Mood Test
+
+{% endblock head %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+
+
Hello {{request.user.profile.username|upper}}, this is a NORMAL mood test
+ {{ request.user.profile.pangram.statement }}
+
+
+
+ enter above pangram in red below
+
+
+{% csrf_token %}
+
+
+
+
+
+
+
+ Submit
+
+
+
+{% endblock content %}
+
+{% block script %}
+
+{% endblock script %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/register.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/register.html
new file mode 100644
index 0000000..b22e572
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/register.html
@@ -0,0 +1,58 @@
+{% extends 'core/templates/base.html' %}
+{% load static %}
+{% block head %}
+Register
+{% endblock head %}
+
+{% block content %}
+
+
+
+
+ Create An Account
+
+
+
+ {% csrf_token %}
+ Please enter your credentials
+
+
+
+
+
+ Register
+
+
Forgot password?
+
+
+
+
+
+
+
+ We are more than just a school
+
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing
+ elit, sed do eiusmod tempor incididunt ut labore et
+ dolore magna aliqua. Ut enim ad minim veniam, quis
+ nostrud exercitation ullamco laboris nisi ut aliquip ex
+ ea commodo consequat.
+
+
+
+
+{% endblock content %}
+
+
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/sad.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/sad.html
new file mode 100644
index 0000000..f6d1cab
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/sad.html
@@ -0,0 +1,60 @@
+{% extends 'core/templates/base.html' %}
+{% load i18n %}
+{% load static %}
+
+{% block head %}
+ Complete Sad Mood Test
+
+{% endblock head %}
+
+
+{% block content %}
+
+
+
+
+
+
+
+
+
+
Hello {{request.user.profile.username|upper}}, this is a SAD mood test
+ {{ request.user.profile.pangram.statement }}
+
+
+
+ enter above pangram in red below
+
+
+{% csrf_token %}
+
+
+
+
+
+
+
+ Submit
+
+
+
+{% endblock content %}
+
+
+{% block script %}
+
+{% endblock script %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/secret_key_login.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/secret_key_login.html
new file mode 100644
index 0000000..65a7be4
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/secret_key_login.html
@@ -0,0 +1,49 @@
+{% extends 'core/templates/base.html' %}
+{% load static %}
+{% block head %}
+ Login
+{% endblock head %}
+
+{% block content %}
+
+
+
+ Forgot Password - ({{request.session.username}})
+
+
+ {% csrf_token %}
+
+ Enter the memorable information (ZKP/2FA secret key) following characters above to sign in
+
+
+
+ Log in
+
+
+
+
+
+{% endblock content %}
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/signin.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/signin.html
new file mode 100644
index 0000000..977f92d
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/signin.html
@@ -0,0 +1,59 @@
+{% extends 'core/templates/base.html' %}
+{% load static %}
+{% block head %}
+ Login
+{% endblock head %}
+
+{% block content %}
+
+
+
+
+ Account Login
+
+
+
+ {% csrf_token %}
+ Please login to your account
+
+
+
+
+
+ Continue
+
+
Forgot password?
+
+
+
+
+{% endblock content %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/visualize.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/visualize.html
new file mode 100644
index 0000000..48e884d
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/visualize.html
@@ -0,0 +1,77 @@
+{% extends 'core/templates/base.html' %}
+{% block head %}
+ Index
+{% endblock head %}
+
+{% block content %}
+
+
+
+
+ {% if mood == 'h' %}
+ Happy Mood
+ {% else %}
+ Happy Mood
+ {% endif %}
+
+ {% if mood == 'a' %}
+ Angry Mood
+ {% else %}
+ Angry Mood
+ {% endif %}
+
+ {% if mood == 'm' %}
+ Normal Mood
+ {% else %}
+ Normal Mood
+ {% endif %}
+
+ {% if mood == 's' %}
+ Sad Mood
+ {% else %}
+ Sad Mood
+ {% endif %}
+
+
+
+
+
+
+ UUKL
+ DUKL
+ DDKL
+ UDKL
+
+
+
+
+
+
+
+
+ {% if chart %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock content %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/angry.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/angry.html
new file mode 100644
index 0000000..dc636ed
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/angry.html
@@ -0,0 +1,63 @@
+{% extends 'core/templates/base.html' %}
+{% load i18n %}
+{% load static %}
+
+{% block head %}
+ Complete Angry Mood Test
+
+{% endblock head %}
+
+{% block content %}
+
+
+
+
+
+
Hello {{request.session.username}}, this is a ANGRY mood test
+ {{ request.session.pangram_text }}
+
+
+
+ enter above pangram in red below
+
+
+ {% csrf_token %}
+
+
+
+
+
+
+
+
+
+
+ Authorised User
+ Unauthorized User
+
+
+
+
+
+ Submit
+
+
+{% endblock content %}
+
+{% block script %}
+
+{% endblock script %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/happy.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/happy.html
new file mode 100644
index 0000000..5cba024
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/happy.html
@@ -0,0 +1,63 @@
+{% extends 'core/templates/base.html' %}
+{% load i18n %}
+{% load static %}
+
+{% block head %}
+ Complete Happy Mood Test
+
+{% endblock head %}
+
+{% block content %}
+
+
+
+
+
+
Hello {{request.session.username}}, this is a HAPPY mood test
+ {{ request.session.pangram_text }}
+
+
+
+ enter above pangram in red below
+
+
+{% csrf_token %}
+
+
+
+
+
+
+
+
+
+
+ Authorised User
+ Unauthorized User
+
+
+
+
+
+ Submit
+
+
+{% endblock content %}
+
+{% block script %}
+
+{% endblock script %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/normal.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/normal.html
new file mode 100644
index 0000000..1d67fac
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/normal.html
@@ -0,0 +1,63 @@
+{% extends 'core/templates/base.html' %}
+{% load i18n %}
+{% load static %}
+
+{% block head %}
+ Complete NORMAL Mood Test
+
+{% endblock head %}
+
+{% block content %}
+
+
+
+
+
+
Hello {{request.session.username}}, this is a NORMAL mood test
+ {{ request.session.pangram_text }}
+
+
+
+ enter above pangram in red below
+
+
+{% csrf_token %}
+
+
+
+
+
+
+
+
+
+
+ Authorised User
+ Unauthorized User
+
+
+
+
+
+ Submit
+
+
+{% endblock content %}
+
+{% block script %}
+
+{% endblock script %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/sad.html b/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/sad.html
new file mode 100644
index 0000000..a26661a
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/templates/core/templates/wizard/sad.html
@@ -0,0 +1,63 @@
+{% extends 'core/templates/base.html' %}
+{% load i18n %}
+{% load static %}
+
+{% block head %}
+ Complete Sad Mood Test
+
+{% endblock head %}
+
+{% block content %}
+
+
+
+
+
+
Hello {{request.session.username}}, this is a SAD mood test
+ {{ request.session.pangram_text }}
+
+
+
+ enter above pangram in red below
+
+
+{% csrf_token %}
+
+
+
+
+
+
+
+
+
+
+ Authorised User
+ Unauthorized User
+
+
+
+
+
+ Submit
+
+
+{% endblock content %}
+
+{% block script %}
+
+{% endblock script %}
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/tests.py b/keystroke_dynamic_software-10690462/core/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/keystroke_dynamic_software-10690462/core/urls.py b/keystroke_dynamic_software-10690462/core/urls.py
new file mode 100644
index 0000000..461bca0
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/urls.py
@@ -0,0 +1,21 @@
+from django.urls import path
+from . import views
+
+app_name = 'core'
+urlpatterns = [
+ path('', views.index, name="index"),
+ path('signin/', views.signin, name="signin"),
+ path('signout/', views.signout, name='signout'),
+ path('register/', views.register, name="register"),
+ path('dashboard/', views.dashboard, name='dashboard'),
+ path('visualize/', views.visualize, name='visualize'),
+ path('to-xlsx/', views.visualize_to_excel, name='to-xlsx'),
+ path('select-mood/', views.select_mood, name='select-mood'),
+ path('mood-check/', views.compute_mood, name='compute-mood'),
+ path('key-signin/', views.secret_key_login, name='secret-key-login'),
+ path('forget-password/', views.forget_password_validate_username, name='forget-password'),
+ path('complete-profile/sad', views.record_base_sad_mood_strokes_view, name="record-profile-sad-mood"),
+ path('complete-profile/happy', views.record_base_happy_mood_strokes_view, name="record-profile-happy-mood"),
+ path('complete-profile/angry', views.record_base_angry_mood_strokes_view, name="record-profile-angry-mood"),
+ path('complete-profile/normal', views.record_base_normal_mood_strokes_view, name="record-profile-normal-mood"),
+]
\ No newline at end of file
diff --git a/keystroke_dynamic_software-10690462/core/utils.py b/keystroke_dynamic_software-10690462/core/utils.py
new file mode 100644
index 0000000..29c457e
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/utils.py
@@ -0,0 +1,156 @@
+import math
+import json
+import random
+import base64
+from io import BytesIO
+from uuid import uuid4
+
+from django.shortcuts import render
+
+import pandas as pd
+import matplotlib.pyplot as plt
+
+
+THRESH = 25
+
+
+def generate_random_backup_secret():
+ return ''.join(random.choices(str(uuid4()).replace('-', ''), k=THRESH))
+
+def generate_3_random_number() -> list:
+ number_list = list(range(0, 25))
+ return random.choices(number_list, k=3)
+
+
+def redirect_to_unregistered_mood_view(request, route_to_render):
+ """checks through the registered mood, and redirect to
+ any missing mood which would be as a result of it not
+ being registered yet for the given profile.
+
+ This function would return None if all moods are
+ registered
+ """
+ registered_moods = [x[0] for x in list(request.user.profile.base_keystroke.all().values_list('mood'))]
+
+ if len(registered_moods) == 4:
+ return render(request, 'core/templates/dashboard.html')
+
+ if 'h' not in registered_moods:
+ # happy mood not yet registered
+ return render(request, "core/templates/happy.html")
+
+ if 's' not in registered_moods:
+ # sad mood not yet registered
+ return render(request, "core/templates/sad.html")
+
+ if 'n' not in registered_moods:
+ # normal mood not yet registered
+ return render(request, "core/templates/normal.html")
+
+ if 'a' not in registered_moods:
+ # angry mood not yet registered
+ return render(request, "core/templates/angry.html")
+
+ # if all moods are tracked, redirect to route to render
+ return route_to_render
+
+
+def calculate_euclidean_distance(v1, v2):
+ return math.sqrt(sum((x - y) ** 2 for x, y in zip(v1, v2)))
+
+
+def calculate_error_rate(distances, thresholds):
+ errors = []
+ for dist, thresh in zip(distances, thresholds):
+ try:
+ result = abs(dist - sum(thresh)) / abs(sum(thresh))
+ except ZeroDivisionError:
+ print("Why Me !!!!")
+ result = abs(dist - sum(thresh))
+
+ errors.append(result)
+
+ return sum(errors) / len(errors)
+
+
+def mood_data_from_submitted_form(form_cleaned_data):
+ return {
+ "kd" : form_cleaned_data['kd'],
+ "udkl" : form_cleaned_data['udkl'],
+ "ddkl" : form_cleaned_data['ddkl'],
+ "uukl" : form_cleaned_data['uukl'],
+ "dukl" : form_cleaned_data['dukl']
+ }
+
+
+def get_mood_full_name(short_name):
+ if short_name == 'a':
+ return 'angry'
+ elif short_name == 'h':
+ return 'happy'
+ elif short_name == 's':
+ return 'sad'
+ else:
+ return 'normal'
+
+
+def get_graph():
+ buffer = BytesIO()
+ plt.savefig(buffer, format='png')
+ buffer.seek(0)
+
+ image_png = buffer.getvalue()
+
+ graph = base64.b64encode(image_png)
+ graph = graph.decode('utf-8')
+ buffer.close()
+ return graph
+
+
+def clean_payload(feat, records):
+ features = {
+ 'kd' : 1,
+ 'uukl' : 2,
+ 'udkl' : 3,
+ 'dukl' : 4,
+ 'ddkl' : 5,
+ }
+
+ feat_to_retrieve = features[feat]
+
+
+ all_x = []
+ all_y = []
+ for payload in records:
+ holder = None
+ for data in payload:
+ if data and isinstance(data, str):
+ holder = json.loads(data)
+
+ all_x.append(payload[0])
+ if holder and len(holder) > feat_to_retrieve:
+ all_y.append(holder[0])
+
+ return all_x, all_y
+
+
+def get_plot(x, y, title):
+ plt.switch_backend('AGG')
+ plt.figure(figsize=(5,5))
+ plt.title(title)
+ plt.plot(x, [abs(i) for i in y])
+ plt.xticks(ticks=list(range(1, max(x)+1)))
+ plt.ylim(ymin=0)
+ m = max(y) * 10
+ plt.ylim(ymax=m)
+ plt.xlim(xmin=0)
+ plt.xlabel('user id')
+ plt.ylabel('miliseconds')
+ plt.tight_layout()
+ graph = get_graph()
+ return graph
+
+
+def to_xlsx(data, mood):
+ df = pd.DataFrame(data)
+ return df.to_excel(f'{mood}.xlsx')
diff --git a/keystroke_dynamic_software-10690462/core/views.py b/keystroke_dynamic_software-10690462/core/views.py
new file mode 100644
index 0000000..5c90a6d
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/core/views.py
@@ -0,0 +1,443 @@
+import json
+import math
+
+from django.contrib.auth import authenticate, login, logout
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth.mixins import LoginRequiredMixin
+from django.http import HttpResponseRedirect, HttpResponse
+from django.shortcuts import render
+from django.contrib import messages
+from django.urls import reverse
+
+from core import models
+from core import utils
+from core import forms
+
+
+
+def index(request):
+ """Process request to retrieve index page"""
+ return render(request, "core/templates/index.html")
+
+
+def signin(request):
+ """Process request to retrieve index page"""
+ if request.method == "GET":
+ return render(request, "core/templates/signin.html")
+
+ username = request.POST.get('username')
+ query = models.Profile.objects.filter(username=username)
+ if not query.exists():
+ messages.error(request, 'User not found')
+ return HttpResponseRedirect(reverse('core:signin'))
+
+ user = query.first()
+ if user.trottle == 3:
+ messages.error(request, 'Too many login attempt')
+ return HttpResponseRedirect(reverse('core:signin'))
+
+ request.session['username'] = username
+ request.session['pangram_text'] = query.first().pangram.statement
+ return HttpResponseRedirect(reverse('core:select-mood'))
+
+
+def forget_password_validate_username(request):
+ """Validates username exists when on forget passsword request"""
+ if request.method == "GET":
+ return render(request, "core/templates/forget_password_validate_username.html")
+
+ username = request.POST.get('username')
+ request.session['username'] = username
+ request.session['rn1'], request.session['rn2'], request.session['rn3'] = utils.generate_3_random_number()
+ return HttpResponseRedirect(reverse('core:secret-key-login'))
+
+
+def secret_key_login(request):
+ """Validates username exists when on forget passsword request"""
+ if request.method == "GET":
+ return render(request, "core/templates/secret_key_login.html")
+
+ key1 = request.POST.get('zkp-1')
+ key2 = request.POST.get('zkp-2')
+ key3 = request.POST.get('zkp-3')
+
+
+ try:
+ user = models.Profile.objects.get(username=request.session['username'])
+ except:
+ messages.error(request, 'Invalid credentials')
+ return HttpResponseRedirect(reverse('core:secret-key-login'))
+
+ sk = user.backup_secret
+ rn1 = sk[request.session['rn1']]
+ rn2 = sk[request.session['rn2']]
+ rn3 = sk[request.session['rn3']]
+
+ if not ((rn1 == key1) and (rn2 == key2) and (rn3 == key3)):
+ messages.error(request, 'Invalid credentials')
+ return HttpResponseRedirect(reverse('core:signin'))
+
+ login(request, user)
+ user.reset_trottle()
+ return HttpResponseRedirect(reverse('core:dashboard'))
+
+
+def register(request):
+ """Process request to retrieve index page"""
+ if request.method == "GET":
+ return render(request, "core/templates/register.html")
+
+ username = request.POST.get('username')
+ if models.Profile.objects.filter(username=username).exists():
+ messages.error(request, "There already exist a user with same username")
+ return HttpResponseRedirect(reverse("core:register"))
+
+ profile = models.Profile.objects.create(
+ username=username,
+ pangram=models.Pangram.objects.order_by('?').first(),
+ backup_secret=utils.generate_random_backup_secret(),
+ )
+ profile.set_password("random")
+ login(request, profile)
+
+ return HttpResponseRedirect(reverse("core:record-profile-happy-mood"))
+
+
+def signout(request):
+ logout(request)
+ return HttpResponseRedirect(reverse("core:index"))
+
+
+def dashboard(request):
+ """Doctype"""
+ if not request.user.is_authenticated:
+ messages.error(request, "Access denied as authentication is required")
+ return HttpResponseRedirect(reverse("core:index"))
+ return render(request, 'core/templates/dashboard.html')
+
+
+@login_required(login_url='/signin/')
+def record_base_happy_mood_strokes_view(request):
+ """Handles request to track a user's happy mode keystrokes"""
+ HAPPY_MODE = 'h'
+ if request.method == "GET":
+ return utils.redirect_to_unregistered_mood_view(
+ request=request,
+ route_to_render=render(request, "core/templates/happy.html")
+ )
+
+ kd = request.POST.get('kd')
+ udkl = request.POST.get('udkl')
+ ddkl = request.POST.get('ddkl')
+ uukl = request.POST.get('uukl')
+ dukl = request.POST.get('dukl')
+
+ models.UserBaseKeyStroke.objects.create(
+ profile=request.user.profile,
+ mood=HAPPY_MODE, kd=kd, udkl=udkl,
+ ddkl=ddkl, uukl=uukl, dukl=dukl
+ )
+
+ models.AuthKeyStroke.objects.create(
+ profile=request.user.profile,
+ mood=HAPPY_MODE, kd=kd, udkl=udkl,
+ ddkl=ddkl, uukl=uukl, dukl=dukl,
+ # Euclidean Distance and Error Rate at this point are authentic
+ # and directly from the user. Euclidean distance cannot be computed
+ # at this point as there's nothing to compute against.
+ #
+ # Because the value here is gotten directly from the user, the error
+ # rate would default to zero
+ ed=0, er=0
+ )
+
+ return HttpResponseRedirect(reverse("core:record-profile-sad-mood"))
+
+
+@login_required(login_url='/signin/')
+def record_base_sad_mood_strokes_view(request):
+ """Handles request to track a user's sad mode keystrokes"""
+ SAD_MODE = 's'
+ if request.method == "GET":
+ return utils.redirect_to_unregistered_mood_view(
+ request=request,
+ route_to_render=render(request, "core/templates/sad.html")
+ )
+
+ kd = request.POST.get('kd')
+ udkl = request.POST.get('udkl')
+ ddkl = request.POST.get('ddkl')
+ uukl = request.POST.get('uukl')
+ dukl = request.POST.get('dukl')
+
+
+ models.UserBaseKeyStroke.objects.create(
+ profile=request.user.profile,
+ mood=SAD_MODE, kd=kd, udkl=udkl,
+ ddkl=ddkl, uukl=uukl, dukl=dukl
+ )
+
+ models.AuthKeyStroke.objects.create(
+ profile=request.user.profile,
+ mood=SAD_MODE, kd=kd, udkl=udkl,
+ ddkl=ddkl, uukl=uukl, dukl=dukl,
+ # Euclidean Distance and Error Rate at this point are authentic
+ # and directly from the user. Euclidean distance cannot be computed
+ # at this point as there's nothing to compute against.
+ #
+ # Because the value here is gotten directly from the user, the error
+ # rate would default to zero
+ ed=0, er=0
+ )
+
+ return HttpResponseRedirect(reverse("core:record-profile-normal-mood"))
+
+
+@login_required(login_url='/signin/')
+def record_base_normal_mood_strokes_view(request):
+ """Handles request to track a user's normal mode keystrokes"""
+ NORMAL_MODE = 'n'
+ if request.method == "GET":
+ return utils.redirect_to_unregistered_mood_view(
+ request=request,
+ route_to_render=render(request, "core/templates/normal.html")
+ )
+
+ kd = request.POST.get('kd')
+ udkl = request.POST.get('udkl')
+ ddkl = request.POST.get('ddkl')
+ uukl = request.POST.get('uukl')
+ dukl = request.POST.get('dukl')
+
+
+ models.UserBaseKeyStroke.objects.create(
+ profile=request.user.profile,
+ mood=NORMAL_MODE, kd=kd, udkl=udkl,
+ ddkl=ddkl, uukl=uukl, dukl=dukl
+ )
+
+ models.AuthKeyStroke.objects.create(
+ profile=request.user.profile,
+ mood=NORMAL_MODE, kd=kd, udkl=udkl,
+ ddkl=ddkl, uukl=uukl, dukl=dukl,
+ # Euclidean Distance and Error Rate at this point are authentic
+ # and directly from the user. Euclidean distance cannot be computed
+ # at this point as there's nothing to compute against.
+ #
+ # Because the value here is gotten directly from the user, the error
+ # rate would default to zero
+ ed=0, er=0
+ )
+
+ return HttpResponseRedirect(reverse("core:record-profile-angry-mood"))
+
+
+@login_required(login_url='/signin/')
+def record_base_angry_mood_strokes_view(request):
+ """Handles request to track a user's angry mode keystrokes"""
+ ANGRY_MODE = 'a'
+ if request.method == "GET":
+ return utils.redirect_to_unregistered_mood_view(
+ request=request,
+ route_to_render=render(request, "core/templates/dashboard.html")
+ )
+
+ kd = request.POST.get('kd')
+ udkl = request.POST.get('udkl')
+ ddkl = request.POST.get('ddkl')
+ uukl = request.POST.get('uukl')
+ dukl = request.POST.get('dukl')
+
+
+ models.UserBaseKeyStroke.objects.create(
+ profile=request.user.profile,
+ mood=ANGRY_MODE, kd=kd, udkl=udkl,
+ ddkl=ddkl, uukl=uukl, dukl=dukl
+ )
+
+ models.AuthKeyStroke.objects.create(
+ profile=request.user.profile,
+ mood=ANGRY_MODE, kd=kd, udkl=udkl,
+ ddkl=ddkl, uukl=uukl, dukl=dukl,
+ # Euclidean Distance and Error Rate at this point are authentic
+ # and directly from the user. Euclidean distance cannot be computed
+ # at this point as there's nothing to compute against.
+ #
+ # Because the value here is gotten directly from the user, the error
+ # rate would default to zero
+ ed=0, er=0
+ )
+
+ return HttpResponseRedirect(reverse("core:dashboard"))
+
+
+def select_mood(request):
+ """Handles request to render view for which user can select
+ their current mood
+ """
+ if request.method == 'GET':
+ try:
+ request.session['username']
+ except:
+ messages.error(request, 'First enter your username')
+ return render(request, 'core/templates/signin.html')
+
+ return render(request, 'core/templates/choose-mood.html')
+
+ # set the selected mood to the user session
+ request.session['mood'] = request.POST.get('mood')
+ return HttpResponseRedirect(reverse('core:compute-mood'))
+
+
+def compute_mood(request):
+ if request.method == "GET":
+ try:
+ mood = request.session['mood']
+ except:
+ messages.error(request, 'No mood selected yet')
+ return render(request, 'core/templates/choose-mood.html')
+ return render(request, f'core/templates/wizard/{utils.get_mood_full_name(mood)}.html')
+
+ username = request.session['username']
+ form_auth_type = request.POST.get('authority')
+ mood_form = forms.PangramForm(request.POST)
+
+ mood_form.is_valid()
+
+ collected_pangram_text = mood_form.cleaned_data['pangram']
+ mood_strokes = utils.mood_data_from_submitted_form(mood_form.cleaned_data)
+
+ try:
+ # check collected pangram against the saved base pangram text
+ profile = models.Profile.objects.get(username=username)
+ statement = profile.pangram.statement
+ print(statement)
+ print(collected_pangram_text)
+ if statement != collected_pangram_text:
+ messages.error(request, "Invalid pangram")
+ return HttpResponseRedirect(reverse('core:signin'))
+ except:
+ messages.error(request, "Invalid credentials")
+ return HttpResponseRedirect(reverse('core:signin'))
+
+ try:
+ # calculate euclidean distances for each mood
+ mood_euclidean_distances, base_mood_strokes = profile.mood_euclidean_distances(
+ mood=mood_form.cleaned_data['mood'], new_strokes=mood_strokes)
+
+ error_rate = utils.calculate_error_rate(mood_euclidean_distances, base_mood_strokes)
+ except TypeError as e:
+ messages.error(request, "Invalid credentials")
+ return HttpResponseRedirect(reverse("core:signin"))
+
+
+ error_rate_threshold = 10
+
+ print("ER --> ", error_rate, "\n\n")
+ if error_rate <= error_rate_threshold:
+ profile.save_authentication_login_strokes_by_form_type(
+ tilt='reset',
+ er=error_rate,
+ kd=mood_strokes['kd'],
+ udkl=mood_strokes['udkl'],
+ uukl=mood_strokes['uukl'],
+ dukl=mood_strokes['dukl'],
+ ddkl=mood_strokes['ddkl'],
+ form_type=form_auth_type,
+ ed=sum(mood_euclidean_distances),
+ mood=mood_form.cleaned_data['mood'],
+ )
+ login(request, profile)
+ messages.success(request, "Authentication successful")
+
+ print("Trottle -> ", profile.trottle )
+ return HttpResponseRedirect(reverse("core:dashboard"))
+
+ profile.save_authentication_login_strokes_by_form_type(
+ tilt='high',
+ er=error_rate,
+ kd=mood_strokes['kd'],
+ udkl=mood_strokes['udkl'],
+ uukl=mood_strokes['uukl'],
+ dukl=mood_strokes['dukl'],
+ ddkl=mood_strokes['ddkl'],
+ form_type=form_auth_type,
+ ed=sum(mood_euclidean_distances),
+ mood=mood_form.cleaned_data['mood'],
+ )
+ messages.error(request, "Invalid credentials")
+ print("Trottle -> ", profile.trottle )
+ return HttpResponseRedirect(reverse("core:signin"))
+
+
+@login_required(login_url='/signin/')
+def visualize(request):
+ """Visualizes the euclidean distance keystrokes data"""
+ mood = request.GET.get('mood') or 'n'
+ req_feature = request.GET.get('feat') or 'uukl'
+
+ all_payload = list(models.AuthKeyStroke.objects.filter(mood=mood).values_list(
+ 'id',
+ 'kd',
+ 'uukl',
+ 'udkl',
+ 'dukl',
+ 'ddkl',
+ ))
+
+ all_x, all_y = utils.clean_payload(feat=req_feature, records=all_payload)
+ title = "Displaying {feature} for {mood} mood".format(
+ feature=req_feature, mood=utils.get_mood_full_name(mood)
+ )
+ chart = utils.get_plot(all_x, all_y, title)
+ return render(
+ request,
+ 'core/templates/visualize.html',
+ {
+ 'mfn': utils.get_mood_full_name(mood),
+ 'chart': chart, 'mood':mood, 'feat': req_feature,
+ }
+ )
+
+
+@login_required(login_url='/signin/')
+def visualize_to_excel(request):
+ mood = request.GET.get('mood') or 'n'
+ auth_type = request.GET.get('type') or 'unauth'
+
+ if auth_type == 'auth':
+ all_payload = list(models.AuthKeyStroke.objects.filter(mood=mood).values(
+ 'kd',
+ 'er',
+ 'ed',
+ 'uukl',
+ 'udkl',
+ 'dukl',
+ 'ddkl',
+ ))
+ else:
+ all_payload = list(models.UnauthorizedAuthStroke.objects.filter(mood=mood).values(
+ 'kd',
+ 'er',
+ 'ed',
+ 'uukl',
+ 'udkl',
+ 'dukl',
+ 'ddkl',
+ ))
+
+ data = []
+ for entry in all_payload:
+ tmp_data = {
+ 'kd': sum([abs(i) for i in json.loads(entry['kd'])]),
+ 'ed': entry['ed'],
+ 'er': entry['er'],
+ 'udkl': sum([abs(i) for i in json.loads(entry['udkl'])]),
+ 'uukl': sum([abs(i) for i in json.loads(entry['uukl'])]),
+ 'ddkl': sum([abs(i) for i in json.loads(entry['ddkl'])]),
+ 'dukl': sum([abs(i) for i in json.loads(entry['dukl'])]),
+ }
+ data.append(tmp_data)
+
+ utils.to_xlsx(data=data, mood=f"{auth_type}_{utils.get_mood_full_name(mood)}")
+ return HttpResponse('Download complete')
diff --git a/keystroke_dynamic_software-10690462/manage.py b/keystroke_dynamic_software-10690462/manage.py
new file mode 100644
index 0000000..cd0be93
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/manage.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+ """Run administrative tasks."""
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'base.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ ) from exc
+ execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/keystroke_dynamic_software-10690462/poetry.lock b/keystroke_dynamic_software-10690462/poetry.lock
new file mode 100644
index 0000000..ae3b274
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/poetry.lock
@@ -0,0 +1,597 @@
+# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
+
+[[package]]
+name = "asgiref"
+version = "3.6.0"
+description = "ASGI specs, helper code, and adapters"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "asgiref-3.6.0-py3-none-any.whl", hash = "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac"},
+ {file = "asgiref-3.6.0.tar.gz", hash = "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"},
+]
+
+[package.extras]
+tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
+
+[[package]]
+name = "contourpy"
+version = "1.0.7"
+description = "Python library for calculating contours of 2D quadrilateral grids"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:95c3acddf921944f241b6773b767f1cbce71d03307270e2d769fd584d5d1092d"},
+ {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc1464c97579da9f3ab16763c32e5c5d5bb5fa1ec7ce509a4ca6108b61b84fab"},
+ {file = "contourpy-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8acf74b5d383414401926c1598ed77825cd530ac7b463ebc2e4f46638f56cce6"},
+ {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c71fdd8f1c0f84ffd58fca37d00ca4ebaa9e502fb49825484da075ac0b0b803"},
+ {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99e9486bf1bb979d95d5cffed40689cb595abb2b841f2991fc894b3452290e8"},
+ {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87f4d8941a9564cda3f7fa6a6cd9b32ec575830780677932abdec7bcb61717b0"},
+ {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9e20e5a1908e18aaa60d9077a6d8753090e3f85ca25da6e25d30dc0a9e84c2c6"},
+ {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a877ada905f7d69b2a31796c4b66e31a8068b37aa9b78832d41c82fc3e056ddd"},
+ {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6381fa66866b0ea35e15d197fc06ac3840a9b2643a6475c8fff267db8b9f1e69"},
+ {file = "contourpy-1.0.7-cp310-cp310-win32.whl", hash = "sha256:3c184ad2433635f216645fdf0493011a4667e8d46b34082f5a3de702b6ec42e3"},
+ {file = "contourpy-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:3caea6365b13119626ee996711ab63e0c9d7496f65641f4459c60a009a1f3e80"},
+ {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed33433fc3820263a6368e532f19ddb4c5990855e4886088ad84fd7c4e561c71"},
+ {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38e2e577f0f092b8e6774459317c05a69935a1755ecfb621c0a98f0e3c09c9a5"},
+ {file = "contourpy-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ae90d5a8590e5310c32a7630b4b8618cef7563cebf649011da80874d0aa8f414"},
+ {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130230b7e49825c98edf0b428b7aa1125503d91732735ef897786fe5452b1ec2"},
+ {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58569c491e7f7e874f11519ef46737cea1d6eda1b514e4eb5ac7dab6aa864d02"},
+ {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d43960d809c4c12508a60b66cb936e7ed57d51fb5e30b513934a4a23874fae"},
+ {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:152fd8f730c31fd67fe0ffebe1df38ab6a669403da93df218801a893645c6ccc"},
+ {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9056c5310eb1daa33fc234ef39ebfb8c8e2533f088bbf0bc7350f70a29bde1ac"},
+ {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a9d7587d2fdc820cc9177139b56795c39fb8560f540bba9ceea215f1f66e1566"},
+ {file = "contourpy-1.0.7-cp311-cp311-win32.whl", hash = "sha256:4ee3ee247f795a69e53cd91d927146fb16c4e803c7ac86c84104940c7d2cabf0"},
+ {file = "contourpy-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:5caeacc68642e5f19d707471890f037a13007feba8427eb7f2a60811a1fc1350"},
+ {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd7dc0e6812b799a34f6d12fcb1000539098c249c8da54f3566c6a6461d0dbad"},
+ {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0f9d350b639db6c2c233d92c7f213d94d2e444d8e8fc5ca44c9706cf72193772"},
+ {file = "contourpy-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e96a08b62bb8de960d3a6afbc5ed8421bf1a2d9c85cc4ea73f4bc81b4910500f"},
+ {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:031154ed61f7328ad7f97662e48660a150ef84ee1bc8876b6472af88bf5a9b98"},
+ {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e9ebb4425fc1b658e13bace354c48a933b842d53c458f02c86f371cecbedecc"},
+ {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efb8f6d08ca7998cf59eaf50c9d60717f29a1a0a09caa46460d33b2924839dbd"},
+ {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6c180d89a28787e4b73b07e9b0e2dac7741261dbdca95f2b489c4f8f887dd810"},
+ {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b8d587cc39057d0afd4166083d289bdeff221ac6d3ee5046aef2d480dc4b503c"},
+ {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:769eef00437edf115e24d87f8926955f00f7704bede656ce605097584f9966dc"},
+ {file = "contourpy-1.0.7-cp38-cp38-win32.whl", hash = "sha256:62398c80ef57589bdbe1eb8537127321c1abcfdf8c5f14f479dbbe27d0322e66"},
+ {file = "contourpy-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:57119b0116e3f408acbdccf9eb6ef19d7fe7baf0d1e9aaa5381489bc1aa56556"},
+ {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30676ca45084ee61e9c3da589042c24a57592e375d4b138bd84d8709893a1ba4"},
+ {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e927b3868bd1e12acee7cc8f3747d815b4ab3e445a28d2e5373a7f4a6e76ba1"},
+ {file = "contourpy-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:366a0cf0fc079af5204801786ad7a1c007714ee3909e364dbac1729f5b0849e5"},
+ {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ba9bb365446a22411f0673abf6ee1fea3b2cf47b37533b970904880ceb72f3"},
+ {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71b0bf0c30d432278793d2141362ac853859e87de0a7dee24a1cea35231f0d50"},
+ {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7281244c99fd7c6f27c1c6bfafba878517b0b62925a09b586d88ce750a016d2"},
+ {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b6d0f9e1d39dbfb3977f9dd79f156c86eb03e57a7face96f199e02b18e58d32a"},
+ {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7f6979d20ee5693a1057ab53e043adffa1e7418d734c1532e2d9e915b08d8ec2"},
+ {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5dd34c1ae752515318224cba7fc62b53130c45ac6a1040c8b7c1a223c46e8967"},
+ {file = "contourpy-1.0.7-cp39-cp39-win32.whl", hash = "sha256:c5210e5d5117e9aec8c47d9156d1d3835570dd909a899171b9535cb4a3f32693"},
+ {file = "contourpy-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:60835badb5ed5f4e194a6f21c09283dd6e007664a86101431bf870d9e86266c4"},
+ {file = "contourpy-1.0.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ce41676b3d0dd16dbcfabcc1dc46090aaf4688fd6e819ef343dbda5a57ef0161"},
+ {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a011cf354107b47c58ea932d13b04d93c6d1d69b8b6dce885e642531f847566"},
+ {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31a55dccc8426e71817e3fe09b37d6d48ae40aae4ecbc8c7ad59d6893569c436"},
+ {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69f8ff4db108815addd900a74df665e135dbbd6547a8a69333a68e1f6e368ac2"},
+ {file = "contourpy-1.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efe99298ba37e37787f6a2ea868265465410822f7bea163edcc1bd3903354ea9"},
+ {file = "contourpy-1.0.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a1e97b86f73715e8670ef45292d7cc033548266f07d54e2183ecb3c87598888f"},
+ {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc331c13902d0f50845099434cd936d49d7a2ca76cb654b39691974cb1e4812d"},
+ {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24847601071f740837aefb730e01bd169fbcaa610209779a78db7ebb6e6a7051"},
+ {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abf298af1e7ad44eeb93501e40eb5a67abbf93b5d90e468d01fc0c4451971afa"},
+ {file = "contourpy-1.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:64757f6460fc55d7e16ed4f1de193f362104285c667c112b50a804d482777edd"},
+ {file = "contourpy-1.0.7.tar.gz", hash = "sha256:d8165a088d31798b59e91117d1f5fc3df8168d8b48c4acc10fc0df0d0bdbcc5e"},
+]
+
+[package.dependencies]
+numpy = ">=1.16"
+
+[package.extras]
+bokeh = ["bokeh", "chromedriver", "selenium"]
+docs = ["furo", "sphinx-copybutton"]
+mypy = ["contourpy[bokeh]", "docutils-stubs", "mypy (==0.991)", "types-Pillow"]
+test = ["Pillow", "matplotlib", "pytest"]
+test-no-images = ["pytest"]
+
+[[package]]
+name = "cycler"
+version = "0.11.0"
+description = "Composable style cycles"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"},
+ {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"},
+]
+
+[[package]]
+name = "django"
+version = "4.2"
+description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
+category = "main"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "Django-4.2-py3-none-any.whl", hash = "sha256:ad33ed68db9398f5dfb33282704925bce044bef4261cd4fb59e4e7f9ae505a78"},
+ {file = "Django-4.2.tar.gz", hash = "sha256:c36e2ab12824e2ac36afa8b2515a70c53c7742f0d6eaefa7311ec379558db997"},
+]
+
+[package.dependencies]
+asgiref = ">=3.6.0,<4"
+sqlparse = ">=0.3.1"
+tzdata = {version = "*", markers = "sys_platform == \"win32\""}
+
+[package.extras]
+argon2 = ["argon2-cffi (>=19.1.0)"]
+bcrypt = ["bcrypt"]
+
+[[package]]
+name = "et-xmlfile"
+version = "1.1.0"
+description = "An implementation of lxml.xmlfile for the standard library"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"},
+ {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"},
+]
+
+[[package]]
+name = "fonttools"
+version = "4.39.3"
+description = "Tools to manipulate font files"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "fonttools-4.39.3-py3-none-any.whl", hash = "sha256:64c0c05c337f826183637570ac5ab49ee220eec66cf50248e8df527edfa95aeb"},
+ {file = "fonttools-4.39.3.zip", hash = "sha256:9234b9f57b74e31b192c3fc32ef1a40750a8fbc1cd9837a7b7bfc4ca4a5c51d7"},
+]
+
+[package.extras]
+all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"]
+graphite = ["lz4 (>=1.7.4.2)"]
+interpolatable = ["munkres", "scipy"]
+lxml = ["lxml (>=4.0,<5)"]
+pathops = ["skia-pathops (>=0.5.0)"]
+plot = ["matplotlib"]
+repacker = ["uharfbuzz (>=0.23.0)"]
+symfont = ["sympy"]
+type1 = ["xattr"]
+ufo = ["fs (>=2.2.0,<3)"]
+unicode = ["unicodedata2 (>=15.0.0)"]
+woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
+
+[[package]]
+name = "kiwisolver"
+version = "1.4.4"
+description = "A fast implementation of the Cassowary constraint solver"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"},
+ {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"},
+ {file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"},
+ {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"},
+ {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"},
+ {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"},
+ {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"},
+ {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"},
+ {file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"},
+ {file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-win32.whl", hash = "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e"},
+ {file = "kiwisolver-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686"},
+ {file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"},
+ {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"},
+ {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"},
+ {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"},
+ {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"},
+ {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"},
+ {file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"},
+ {file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"},
+ {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"},
+ {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"},
+ {file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"},
+ {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"},
+ {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"},
+ {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"},
+ {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"},
+ {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"},
+ {file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"},
+ {file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"},
+ {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"},
+ {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"},
+ {file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"},
+ {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"},
+ {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"},
+ {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"},
+ {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"},
+ {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"},
+ {file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"},
+ {file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"},
+ {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"},
+ {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"},
+ {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"},
+ {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"},
+ {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9"},
+ {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8"},
+ {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286"},
+ {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb"},
+ {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f"},
+ {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008"},
+ {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767"},
+ {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9"},
+ {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2"},
+ {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b"},
+ {file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"},
+]
+
+[[package]]
+name = "matplotlib"
+version = "3.7.1"
+description = "Python plotting package"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:95cbc13c1fc6844ab8812a525bbc237fa1470863ff3dace7352e910519e194b1"},
+ {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:08308bae9e91aca1ec6fd6dda66237eef9f6294ddb17f0d0b3c863169bf82353"},
+ {file = "matplotlib-3.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:544764ba51900da4639c0f983b323d288f94f65f4024dc40ecb1542d74dc0500"},
+ {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56d94989191de3fcc4e002f93f7f1be5da476385dde410ddafbb70686acf00ea"},
+ {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99bc9e65901bb9a7ce5e7bb24af03675cbd7c70b30ac670aa263240635999a4"},
+ {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb7d248c34a341cd4c31a06fd34d64306624c8cd8d0def7abb08792a5abfd556"},
+ {file = "matplotlib-3.7.1-cp310-cp310-win32.whl", hash = "sha256:ce463ce590f3825b52e9fe5c19a3c6a69fd7675a39d589e8b5fbe772272b3a24"},
+ {file = "matplotlib-3.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d7bc90727351fb841e4d8ae620d2d86d8ed92b50473cd2b42ce9186104ecbba"},
+ {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:770a205966d641627fd5cf9d3cb4b6280a716522cd36b8b284a8eb1581310f61"},
+ {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f67bfdb83a8232cb7a92b869f9355d677bce24485c460b19d01970b64b2ed476"},
+ {file = "matplotlib-3.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2bf092f9210e105f414a043b92af583c98f50050559616930d884387d0772aba"},
+ {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89768d84187f31717349c6bfadc0e0d8c321e8eb34522acec8a67b1236a66332"},
+ {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83111e6388dec67822e2534e13b243cc644c7494a4bb60584edbff91585a83c6"},
+ {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a867bf73a7eb808ef2afbca03bcdb785dae09595fbe550e1bab0cd023eba3de0"},
+ {file = "matplotlib-3.7.1-cp311-cp311-win32.whl", hash = "sha256:fbdeeb58c0cf0595efe89c05c224e0a502d1aa6a8696e68a73c3efc6bc354304"},
+ {file = "matplotlib-3.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:c0bd19c72ae53e6ab979f0ac6a3fafceb02d2ecafa023c5cca47acd934d10be7"},
+ {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:6eb88d87cb2c49af00d3bbc33a003f89fd9f78d318848da029383bfc08ecfbfb"},
+ {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:cf0e4f727534b7b1457898c4f4ae838af1ef87c359b76dcd5330fa31893a3ac7"},
+ {file = "matplotlib-3.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:46a561d23b91f30bccfd25429c3c706afe7d73a5cc64ef2dfaf2b2ac47c1a5dc"},
+ {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8704726d33e9aa8a6d5215044b8d00804561971163563e6e6591f9dcf64340cc"},
+ {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4cf327e98ecf08fcbb82685acaf1939d3338548620ab8dfa02828706402c34de"},
+ {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:617f14ae9d53292ece33f45cba8503494ee199a75b44de7717964f70637a36aa"},
+ {file = "matplotlib-3.7.1-cp38-cp38-win32.whl", hash = "sha256:7c9a4b2da6fac77bcc41b1ea95fadb314e92508bf5493ceff058e727e7ecf5b0"},
+ {file = "matplotlib-3.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:14645aad967684e92fc349493fa10c08a6da514b3d03a5931a1bac26e6792bd1"},
+ {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:81a6b377ea444336538638d31fdb39af6be1a043ca5e343fe18d0f17e098770b"},
+ {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:28506a03bd7f3fe59cd3cd4ceb2a8d8a2b1db41afede01f66c42561b9be7b4b7"},
+ {file = "matplotlib-3.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8c587963b85ce41e0a8af53b9b2de8dddbf5ece4c34553f7bd9d066148dc719c"},
+ {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8bf26ade3ff0f27668989d98c8435ce9327d24cffb7f07d24ef609e33d582439"},
+ {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:def58098f96a05f90af7e92fd127d21a287068202aa43b2a93476170ebd99e87"},
+ {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f883a22a56a84dba3b588696a2b8a1ab0d2c3d41be53264115c71b0a942d8fdb"},
+ {file = "matplotlib-3.7.1-cp39-cp39-win32.whl", hash = "sha256:4f99e1b234c30c1e9714610eb0c6d2f11809c9c78c984a613ae539ea2ad2eb4b"},
+ {file = "matplotlib-3.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:3ba2af245e36990facf67fde840a760128ddd71210b2ab6406e640188d69d136"},
+ {file = "matplotlib-3.7.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3032884084f541163f295db8a6536e0abb0db464008fadca6c98aaf84ccf4717"},
+ {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a2cb34336110e0ed8bb4f650e817eed61fa064acbefeb3591f1b33e3a84fd96"},
+ {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b867e2f952ed592237a1828f027d332d8ee219ad722345b79a001f49df0936eb"},
+ {file = "matplotlib-3.7.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:57bfb8c8ea253be947ccb2bc2d1bb3862c2bccc662ad1b4626e1f5e004557042"},
+ {file = "matplotlib-3.7.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:438196cdf5dc8d39b50a45cb6e3f6274edbcf2254f85fa9b895bf85851c3a613"},
+ {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21e9cff1a58d42e74d01153360de92b326708fb205250150018a52c70f43c290"},
+ {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d4725d70b7c03e082bbb8a34639ede17f333d7247f56caceb3801cb6ff703d"},
+ {file = "matplotlib-3.7.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:97cc368a7268141afb5690760921765ed34867ffb9655dd325ed207af85c7529"},
+ {file = "matplotlib-3.7.1.tar.gz", hash = "sha256:7b73305f25eab4541bd7ee0b96d87e53ae9c9f1823be5659b806cd85786fe882"},
+]
+
+[package.dependencies]
+contourpy = ">=1.0.1"
+cycler = ">=0.10"
+fonttools = ">=4.22.0"
+kiwisolver = ">=1.0.1"
+numpy = ">=1.20"
+packaging = ">=20.0"
+pillow = ">=6.2.0"
+pyparsing = ">=2.3.1"
+python-dateutil = ">=2.7"
+
+[[package]]
+name = "numpy"
+version = "1.24.2"
+description = "Fundamental package for array computing in Python"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "numpy-1.24.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eef70b4fc1e872ebddc38cddacc87c19a3709c0e3e5d20bf3954c147b1dd941d"},
+ {file = "numpy-1.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8d2859428712785e8a8b7d2b3ef0a1d1565892367b32f915c4a4df44d0e64f5"},
+ {file = "numpy-1.24.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6524630f71631be2dabe0c541e7675db82651eb998496bbe16bc4f77f0772253"},
+ {file = "numpy-1.24.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a51725a815a6188c662fb66fb32077709a9ca38053f0274640293a14fdd22978"},
+ {file = "numpy-1.24.2-cp310-cp310-win32.whl", hash = "sha256:2620e8592136e073bd12ee4536149380695fbe9ebeae845b81237f986479ffc9"},
+ {file = "numpy-1.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:97cf27e51fa078078c649a51d7ade3c92d9e709ba2bfb97493007103c741f1d0"},
+ {file = "numpy-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7de8fdde0003f4294655aa5d5f0a89c26b9f22c0a58790c38fae1ed392d44a5a"},
+ {file = "numpy-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4173bde9fa2a005c2c6e2ea8ac1618e2ed2c1c6ec8a7657237854d42094123a0"},
+ {file = "numpy-1.24.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cecaed30dc14123020f77b03601559fff3e6cd0c048f8b5289f4eeabb0eb281"},
+ {file = "numpy-1.24.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a23f8440561a633204a67fb44617ce2a299beecf3295f0d13c495518908e910"},
+ {file = "numpy-1.24.2-cp311-cp311-win32.whl", hash = "sha256:e428c4fbfa085f947b536706a2fc349245d7baa8334f0c5723c56a10595f9b95"},
+ {file = "numpy-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:557d42778a6869c2162deb40ad82612645e21d79e11c1dc62c6e82a2220ffb04"},
+ {file = "numpy-1.24.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d0a2db9d20117bf523dde15858398e7c0858aadca7c0f088ac0d6edd360e9ad2"},
+ {file = "numpy-1.24.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c72a6b2f4af1adfe193f7beb91ddf708ff867a3f977ef2ec53c0ffb8283ab9f5"},
+ {file = "numpy-1.24.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c29e6bd0ec49a44d7690ecb623a8eac5ab8a923bce0bea6293953992edf3a76a"},
+ {file = "numpy-1.24.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2eabd64ddb96a1239791da78fa5f4e1693ae2dadc82a76bc76a14cbb2b966e96"},
+ {file = "numpy-1.24.2-cp38-cp38-win32.whl", hash = "sha256:e3ab5d32784e843fc0dd3ab6dcafc67ef806e6b6828dc6af2f689be0eb4d781d"},
+ {file = "numpy-1.24.2-cp38-cp38-win_amd64.whl", hash = "sha256:76807b4063f0002c8532cfeac47a3068a69561e9c8715efdad3c642eb27c0756"},
+ {file = "numpy-1.24.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4199e7cfc307a778f72d293372736223e39ec9ac096ff0a2e64853b866a8e18a"},
+ {file = "numpy-1.24.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:adbdce121896fd3a17a77ab0b0b5eedf05a9834a18699db6829a64e1dfccca7f"},
+ {file = "numpy-1.24.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:889b2cc88b837d86eda1b17008ebeb679d82875022200c6e8e4ce6cf549b7acb"},
+ {file = "numpy-1.24.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780"},
+ {file = "numpy-1.24.2-cp39-cp39-win32.whl", hash = "sha256:63e45511ee4d9d976637d11e6c9864eae50e12dc9598f531c035265991910468"},
+ {file = "numpy-1.24.2-cp39-cp39-win_amd64.whl", hash = "sha256:a77d3e1163a7770164404607b7ba3967fb49b24782a6ef85d9b5f54126cc39e5"},
+ {file = "numpy-1.24.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:92011118955724465fb6853def593cf397b4a1367495e0b59a7e69d40c4eb71d"},
+ {file = "numpy-1.24.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa"},
+ {file = "numpy-1.24.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:150947adbdfeceec4e5926d956a06865c1c690f2fd902efede4ca6fe2e657c3f"},
+ {file = "numpy-1.24.2.tar.gz", hash = "sha256:003a9f530e880cb2cd177cba1af7220b9aa42def9c4afc2a2fc3ee6be7eb2b22"},
+]
+
+[[package]]
+name = "openpyxl"
+version = "3.1.2"
+description = "A Python library to read/write Excel 2010 xlsx/xlsm files"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"},
+ {file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"},
+]
+
+[package.dependencies]
+et-xmlfile = "*"
+
+[[package]]
+name = "packaging"
+version = "23.1"
+description = "Core utilities for Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
+ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
+]
+
+[[package]]
+name = "pandas"
+version = "2.0.0"
+description = "Powerful data structures for data analysis, time series, and statistics"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pandas-2.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bbb2c5e94d6aa4e632646a3bacd05c2a871c3aa3e85c9bec9be99cb1267279f2"},
+ {file = "pandas-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5337c87c4e963f97becb1217965b6b75c6fe5f54c4cf09b9a5ac52fc0bd03d3"},
+ {file = "pandas-2.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ded51f7e3dd9b4f8b87f2ceb7bd1a8df2491f7ee72f7074c6927a512607199e"},
+ {file = "pandas-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c858de9e9fc422d25e67e1592a6e6135d7bcf9a19fcaf4d0831a0be496bf21"},
+ {file = "pandas-2.0.0-cp310-cp310-win32.whl", hash = "sha256:2d1d138848dd71b37e3cbe7cd952ff84e2ab04d8988972166e18567dcc811245"},
+ {file = "pandas-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:d08e41d96bc4de6f500afe80936c68fce6099d5a434e2af7c7fd8e7c72a3265d"},
+ {file = "pandas-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:24472cfc7ced511ac90608728b88312be56edc8f19b9ed885a7d2e47ffaf69c0"},
+ {file = "pandas-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ffb14f50c74ee541610668137830bb93e9dfa319b1bef2cedf2814cd5ac9c70"},
+ {file = "pandas-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c24c7d12d033a372a9daf9ff2c80f8b0af6f98d14664dbb0a4f6a029094928a7"},
+ {file = "pandas-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8318de0f886e4dcb8f9f36e45a3d6a6c3d1cfdc508354da85e739090f0222991"},
+ {file = "pandas-2.0.0-cp311-cp311-win32.whl", hash = "sha256:57c34b79c13249505e850d0377b722961b99140f81dafbe6f19ef10239f6284a"},
+ {file = "pandas-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:8f987ec26e96a8490909bc5d98c514147236e49830cba7df8690f6087c12bbae"},
+ {file = "pandas-2.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b3ba8f5dd470d8bfbc4259829589f4a32881151c49e36384d9eb982b35a12020"},
+ {file = "pandas-2.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fcd471c9d9f60926ab2f15c6c29164112f458acb42280365fbefa542d0c2fc74"},
+ {file = "pandas-2.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9253edfd015520ce77a9343eb7097429479c039cd3ebe81d7810ea11b4b24695"},
+ {file = "pandas-2.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977326039bd1ded620001a1889e2ed4798460a6bc5a24fbaebb5f07a41c32a55"},
+ {file = "pandas-2.0.0-cp38-cp38-win32.whl", hash = "sha256:78425ca12314b23356c28b16765639db10ebb7d8983f705d6759ff7fe41357fa"},
+ {file = "pandas-2.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:d93b7fcfd9f3328072b250d6d001dcfeec5d3bb66c1b9c8941e109a46c0c01a8"},
+ {file = "pandas-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:425705cee8be54db2504e8dd2a730684790b15e5904b750c367611ede49098ab"},
+ {file = "pandas-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a4f789b7c012a608c08cda4ff0872fd979cb18907a37982abe884e6f529b8793"},
+ {file = "pandas-2.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3bb9d840bf15656805f6a3d87eea9dcb7efdf1314a82adcf7f00b820427c5570"},
+ {file = "pandas-2.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0778ab54c8f399d83d98ffb674d11ec716449956bc6f6821891ab835848687f2"},
+ {file = "pandas-2.0.0-cp39-cp39-win32.whl", hash = "sha256:70db5c278bbec0306d32bf78751ff56b9594c05a5098386f6c8a563659124f91"},
+ {file = "pandas-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f3320bb55f34af4193020158ef8118ee0fb9aec7cc47d2084dbfdd868a0a24f"},
+ {file = "pandas-2.0.0.tar.gz", hash = "sha256:cda9789e61b44463c1c4fe17ef755de77bcd13b09ba31c940d20f193d63a5dc8"},
+]
+
+[package.dependencies]
+numpy = [
+ {version = ">=1.21.0", markers = "python_version >= \"3.10\""},
+ {version = ">=1.23.2", markers = "python_version >= \"3.11\""},
+]
+python-dateutil = ">=2.8.2"
+pytz = ">=2020.1"
+tzdata = ">=2022.1"
+
+[package.extras]
+all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"]
+aws = ["s3fs (>=2021.08.0)"]
+clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"]
+compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"]
+computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"]
+excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"]
+feather = ["pyarrow (>=7.0.0)"]
+fss = ["fsspec (>=2021.07.0)"]
+gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"]
+hdf5 = ["tables (>=3.6.1)"]
+html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"]
+mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"]
+output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"]
+parquet = ["pyarrow (>=7.0.0)"]
+performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"]
+plot = ["matplotlib (>=3.6.1)"]
+postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"]
+spss = ["pyreadstat (>=1.1.2)"]
+sql-other = ["SQLAlchemy (>=1.4.16)"]
+test = ["hypothesis (>=6.34.2)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"]
+xml = ["lxml (>=4.6.3)"]
+
+[[package]]
+name = "pillow"
+version = "9.5.0"
+description = "Python Imaging Library (Fork)"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"},
+ {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"},
+ {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"},
+ {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"},
+ {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"},
+ {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"},
+ {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"},
+ {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"},
+ {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"},
+ {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"},
+ {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"},
+ {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"},
+ {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"},
+ {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"},
+ {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"},
+ {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"},
+ {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"},
+ {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"},
+ {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"},
+ {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"},
+ {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"},
+ {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"},
+ {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"},
+ {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"},
+ {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"},
+ {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"},
+ {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"},
+ {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"},
+ {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"},
+ {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"},
+ {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"},
+ {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"},
+ {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"},
+ {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"},
+ {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"},
+ {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"},
+ {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"},
+ {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"},
+ {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"},
+ {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"},
+ {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"},
+ {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"},
+ {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"},
+ {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"},
+ {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"},
+ {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"},
+ {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"},
+ {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"},
+ {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"},
+ {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"},
+ {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"},
+ {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"},
+ {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"},
+ {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"},
+ {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"},
+ {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"},
+ {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"},
+ {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"},
+ {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"},
+ {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"},
+ {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"},
+ {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"},
+ {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"},
+ {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"},
+ {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"},
+ {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"},
+]
+
+[package.extras]
+docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"]
+tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
+
+[[package]]
+name = "pyparsing"
+version = "3.0.9"
+description = "pyparsing module - Classes and methods to define and execute parsing grammars"
+category = "main"
+optional = false
+python-versions = ">=3.6.8"
+files = [
+ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
+ {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
+]
+
+[package.extras]
+diagrams = ["jinja2", "railroad-diagrams"]
+
+[[package]]
+name = "python-dateutil"
+version = "2.8.2"
+description = "Extensions to the standard Python datetime module"
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+files = [
+ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
+ {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+]
+
+[package.dependencies]
+six = ">=1.5"
+
+[[package]]
+name = "pytz"
+version = "2023.3"
+description = "World timezone definitions, modern and historical"
+category = "main"
+optional = false
+python-versions = "*"
+files = [
+ {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"},
+ {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"},
+]
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+files = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+
+[[package]]
+name = "sqlparse"
+version = "0.4.3"
+description = "A non-validating SQL parser."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+files = [
+ {file = "sqlparse-0.4.3-py3-none-any.whl", hash = "sha256:0323c0ec29cd52bceabc1b4d9d579e311f3e4961b98d174201d5622a23b85e34"},
+ {file = "sqlparse-0.4.3.tar.gz", hash = "sha256:69ca804846bb114d2ec380e4360a8a340db83f0ccf3afceeb1404df028f57268"},
+]
+
+[[package]]
+name = "tzdata"
+version = "2023.3"
+description = "Provider of IANA time zone data"
+category = "main"
+optional = false
+python-versions = ">=2"
+files = [
+ {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"},
+ {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"},
+]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.10"
+content-hash = "c4fbe7bb95d2737249fe2708c3128445cc25261a96fe1bb837085976aee6c4a9"
diff --git a/keystroke_dynamic_software-10690462/pyproject.toml b/keystroke_dynamic_software-10690462/pyproject.toml
new file mode 100644
index 0000000..3f3e821
--- /dev/null
+++ b/keystroke_dynamic_software-10690462/pyproject.toml
@@ -0,0 +1,20 @@
+[tool.poetry]
+name = "django-euclidean"
+version = "0.1.0"
+description = ""
+authors = ["Your Name "]
+license = "MIT"
+readme = "README.md"
+packages = [{include = "django_euclidean"}]
+
+[tool.poetry.dependencies]
+python = "^3.10"
+django = "^4.2"
+matplotlib = "^3.7.1"
+pandas = "^2.0.0"
+openpyxl = "^3.1.2"
+
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"