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 %} + +
+ Logo +
+ +
+ +
+ +
+

Hello {{request.user.profile.username|upper}}, this is a ANGRY mood test

+

{{ request.user.profile.pangram.statement }}

+
+ +
+ enter above pangram in red below +
+ +
{% csrf_token %} + + + + + + + +
+ +{% 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 %} + +
+
+ logo +
+

+ {{request.user.profile.username|upper|default:request.session.username}} How're you feeling Today? +

+
+ +
+ {% csrf_token %} +
+
+ + + + + + + + + +
+ +
+ + + + + + + + + +
+ +
+ + + + + + + + + +
+ +
+ + + + + + + + + +
+ +
+ +
+ +
+
+ +{% 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 %} +
+ logo +

+ Forgot Password +

+
+
{% csrf_token %} +

Enter Username

+
+ +
+
+ + +
+ +
+{% 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 %} +
+ Logo +
+ +
+ +
+ +
+

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 %} + + + + + + + +
+ +{% 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

+ +
+ Sign In + Sign Up +
+
+
+{% 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 %} +
+ Logo +
+ +
+ +
+ +
+

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 %} + + + + + + + +
+ +{% 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 %} + +
+ logo +

+ Create An Account +

+
+ +
{% csrf_token %} +

Please enter your credentials

+
+ +
+
+ + Forgot password? +
+
+

Already have an account?

+ + Login + +
+
+ +
+
+

+ 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 %} +
+ Logo +
+ +
+ +
+ +
+

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 %} + + + + + + + +
+ +{% 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 %} +
+ logo +

+ Forgot Password - ({{request.session.username}}) +

+
+
{% csrf_token %} +
+
+ + +
+
+ + +
+
+ + +
+
+

Enter the memorable information (ZKP/2FA secret key) following characters above to sign 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 %} + +
+ logo +

+ Account Login +

+
+
+ {% csrf_token %} +

Please login to your account

+
+ +
+
+ + Forgot password? +
+
+

Don't have an account?

+ + Register + +
+
+ +{% 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 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 %} + + + + + + + + +
+ +
+ + + +
+
+ + +
+{% 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 %} + + + + + + + + +
+ +
+ + + +
+
+ + +
+{% 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 %} + + + + + + + + +
+ +
+ + + +
+
+ + +
+{% 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 %} + + + + + + + + +
+ +
+ + + +
+
+ + +
+{% 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"