generated from mwc/lab_server
	Initial commit
This commit is contained in:
		
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					poem_server/corpus.gz
 | 
				
			||||||
 | 
					**/__pycache__/*
 | 
				
			||||||
 | 
					**/migrations/*
 | 
				
			||||||
 | 
					**/database.sqlite
 | 
				
			||||||
							
								
								
									
										32
									
								
								notes.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								notes.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					# Project Server Notes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Checkpoint 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Lots of software today connects to remote servers, and can't work offline. 
 | 
				
			||||||
 | 
					   What are some advantages of using a program or an app which uses a remote 
 | 
				
			||||||
 | 
					   server? What are some advantages of using a program or an app which is 
 | 
				
			||||||
 | 
					   completely local?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. You just ran a server on your own computer, and connected to it as a client 
 | 
				
			||||||
 | 
					   on the same computer. In what other situations might it be useful to run a 
 | 
				
			||||||
 | 
					   server on your computer, where you're the only client, on the same computer?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Checkpoint 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3. Choose a program (Steam), web app (Google Docs), or app (Weather) that you use 
 | 
				
			||||||
 | 
					   frequently. You can't observe the calls this program is making to its server 
 | 
				
			||||||
 | 
					   (unless you have fancy tools), but you can infer some of the calls based on the 
 | 
				
			||||||
 | 
					   program's behavior. Describe a few routes which you think may exist for your 
 | 
				
			||||||
 | 
					   chosen program's backend server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					4. In your own words, what is an exception? When might it be useful to handle an 
 | 
				
			||||||
 | 
					   exception? When is it better not to handle an exception, and instead let the 
 | 
				
			||||||
 | 
					   program crash?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										51
									
								
								poem_server/app/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								poem_server/app/models.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					from banjo.models import Model, StringField, IntegerField, ForeignKey
 | 
				
			||||||
 | 
					from banjo.http import BadRequest
 | 
				
			||||||
 | 
					import pronouncing
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Line(Model):
 | 
				
			||||||
 | 
					    text = StringField()
 | 
				
			||||||
 | 
					    clean_text = StringField()
 | 
				
			||||||
 | 
					    line_number = IntegerField()
 | 
				
			||||||
 | 
					    poem = ForeignKey("Poem", related_name="lines")
 | 
				
			||||||
 | 
					    rhyme = ForeignKey("Rhyme", related_name="lines", null=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        "The string representation of a line is just its text"
 | 
				
			||||||
 | 
					        return self.text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        "Returns lowercase text without any punctuation"
 | 
				
			||||||
 | 
					        return re.sub('[^a-z ]', '', self.text.lower())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def last_word(self):
 | 
				
			||||||
 | 
					        "Gets the last word from a line, lower case and without punctuation."
 | 
				
			||||||
 | 
					        parts = self.clean_text.split()
 | 
				
			||||||
 | 
					        if parts:
 | 
				
			||||||
 | 
					            return parts[-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def rhyming_lines(self):
 | 
				
			||||||
 | 
					        if self.rhyme:
 | 
				
			||||||
 | 
					            return self.rhyme.lines.exclude(clean_text__endswith=self.last_word())
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Poem(Model):
 | 
				
			||||||
 | 
					    gutenberg_id = IntegerField(unique=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        "The string representation of a poem is all its lines, in order"
 | 
				
			||||||
 | 
					        return '\n'.join(line.text for line in self.lines.order_by('line_number'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Rhyme(Model):
 | 
				
			||||||
 | 
					    phones = StringField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_rhyme_for_word(self, word):
 | 
				
			||||||
 | 
					        phones = pronouncing.phones_for_word(word)
 | 
				
			||||||
 | 
					        if not phones:
 | 
				
			||||||
 | 
					            raise BadRequest(f"Couldn't figure out how to pronounce {word}")
 | 
				
			||||||
 | 
					        rhyming_phones = pronouncing.rhyming_part(phones[0])
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return Rhyme.objects.get(phones=rhyming_phones)
 | 
				
			||||||
 | 
					        except Rhyme.DoesNotExist:
 | 
				
			||||||
 | 
					            raise BadRequest(f"Sorry, no lines rhyme with {word}")
 | 
				
			||||||
							
								
								
									
										8
									
								
								poem_server/app/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								poem_server/app/views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					from banjo.urls import route_get, route_post
 | 
				
			||||||
 | 
					from banjo.http import BadRequest, NotFound
 | 
				
			||||||
 | 
					from app.models import Line, Poem, Rhyme
 | 
				
			||||||
 | 
					from random import choice, sample
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@route_get('lines/random', args={})
 | 
				
			||||||
 | 
					def get_random_line(params):
 | 
				
			||||||
 | 
					    return {'line': Line.objects.random().text}
 | 
				
			||||||
							
								
								
									
										81
									
								
								poem_server/import_poems.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								poem_server/import_poems.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					import os
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					from tqdm import tqdm
 | 
				
			||||||
 | 
					from banjo.runner import setup_django
 | 
				
			||||||
 | 
					import requests
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					from collections import defaultdict
 | 
				
			||||||
 | 
					from argparse import ArgumentParser
 | 
				
			||||||
 | 
					import pronouncing
 | 
				
			||||||
 | 
					import gzip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					corpus_url = "http://static.decontextualize.com/gutenberg-poetry-v001.ndjson.gz"
 | 
				
			||||||
 | 
					corpus_file = Path("corpus.gz")
 | 
				
			||||||
 | 
					corpus_length = 3085117
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def download_corpus():
 | 
				
			||||||
 | 
					    "Downloads a few million lines of poetry"
 | 
				
			||||||
 | 
					    if not corpus_file.exists():
 | 
				
			||||||
 | 
					        print("Downloading a file with three million lines of poetry...")
 | 
				
			||||||
 | 
					        download = requests.get(corpus_url, stream=True)
 | 
				
			||||||
 | 
					        with corpus_file.open("wb") as f:
 | 
				
			||||||
 | 
					            for chunk in download.iter_content(chunk_size=1024):
 | 
				
			||||||
 | 
					                if chunk:
 | 
				
			||||||
 | 
					                    f.write(chunk)
 | 
				
			||||||
 | 
					                    f.flush()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					def read_corpus(limit=None):
 | 
				
			||||||
 | 
					    "Reads one line at a time from the corpus"
 | 
				
			||||||
 | 
					    for i, line in enumerate(gzip.open(corpus_file)):
 | 
				
			||||||
 | 
					        if limit and i == limit:
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					        yield json.loads(line.strip())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def clean(line):
 | 
				
			||||||
 | 
					    "Returns lowercase text without any punctuation"
 | 
				
			||||||
 | 
					    return re.sub('[^a-z ]', '', line.lower()).strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_last_word(line):
 | 
				
			||||||
 | 
					    """Gets the last word from a line.
 | 
				
			||||||
 | 
					    Strips out punctuation, then splits the line on spaces and returns the final word.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    parts = clean(line).split()
 | 
				
			||||||
 | 
					    if parts:
 | 
				
			||||||
 | 
					        return parts[-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def import_poems(limit=None):
 | 
				
			||||||
 | 
					    "Imports each line into the app"
 | 
				
			||||||
 | 
					    database_file = Path("database.sqlite")
 | 
				
			||||||
 | 
					    if database_file.exists():
 | 
				
			||||||
 | 
					        database_file.unlink()
 | 
				
			||||||
 | 
					    setup_django()
 | 
				
			||||||
 | 
					    from app.models import Line, Poem, Rhyme
 | 
				
			||||||
 | 
					    print("Importing lines into the app's database...")
 | 
				
			||||||
 | 
					    line_counts = defaultdict(int)
 | 
				
			||||||
 | 
					    for row in tqdm(read_corpus(limit=limit), total=limit or corpus_length):
 | 
				
			||||||
 | 
					        text = row['s']
 | 
				
			||||||
 | 
					        last_word = get_last_word(text)
 | 
				
			||||||
 | 
					        if not last_word:
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        last_word_phones = pronouncing.phones_for_word(last_word)
 | 
				
			||||||
 | 
					        if last_word_phones:
 | 
				
			||||||
 | 
					            rhyming_phones = pronouncing.rhyming_part(last_word_phones[0])
 | 
				
			||||||
 | 
					            rhyme, _ = Rhyme.objects.get_or_create(phones=rhyming_phones)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            rhyme=None
 | 
				
			||||||
 | 
					        poem, _ = Poem.objects.get_or_create(gutenberg_id=row['gid'])
 | 
				
			||||||
 | 
					        line = Line.objects.create(
 | 
				
			||||||
 | 
					            text=text,
 | 
				
			||||||
 | 
					            clean_text=clean(text),
 | 
				
			||||||
 | 
					            line_number=line_counts[row['gid']],
 | 
				
			||||||
 | 
					            poem=poem,
 | 
				
			||||||
 | 
					            rhyme=rhyme,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        line_counts[row['gid']] += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					parser = ArgumentParser()
 | 
				
			||||||
 | 
					parser.add_argument('-l', '--limit', type=int)
 | 
				
			||||||
 | 
					args = parser.parse_args()
 | 
				
			||||||
 | 
					download_corpus()
 | 
				
			||||||
 | 
					import_poems(args.limit)
 | 
				
			||||||
							
								
								
									
										644
									
								
								poetry.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										644
									
								
								poetry.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,644 @@
 | 
				
			|||||||
 | 
					# This file is automatically @generated by Poetry 2.0.0 and should not be changed by hand.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "asgiref"
 | 
				
			||||||
 | 
					version = "3.8.1"
 | 
				
			||||||
 | 
					description = "ASGI specs, helper code, and adapters"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"},
 | 
				
			||||||
 | 
					    {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "certifi"
 | 
				
			||||||
 | 
					version = "2024.2.2"
 | 
				
			||||||
 | 
					description = "Python package for providing Mozilla's CA Bundle."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.6"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
 | 
				
			||||||
 | 
					    {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "charset-normalizer"
 | 
				
			||||||
 | 
					version = "3.3.2"
 | 
				
			||||||
 | 
					description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.7.0"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
 | 
				
			||||||
 | 
					    {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "cmudict"
 | 
				
			||||||
 | 
					version = "1.0.22"
 | 
				
			||||||
 | 
					description = "A versioned python wrapper package for The CMU Pronouncing Dictionary data files."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8,<4.0"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "cmudict-1.0.22-py3-none-any.whl", hash = "sha256:46f524dfe9a002f719307882f6deaf47f534a024cf18c0d7305e59cf93291276"},
 | 
				
			||||||
 | 
					    {file = "cmudict-1.0.22.tar.gz", hash = "sha256:a54d40f76eae17bba93eca5d7d6ba48f7abd4bd86fb54c6108d587290c8beb69"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					importlib-metadata = ">=5"
 | 
				
			||||||
 | 
					importlib-resources = ">=5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "colorama"
 | 
				
			||||||
 | 
					version = "0.4.6"
 | 
				
			||||||
 | 
					description = "Cross-platform colored terminal text."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					markers = "platform_system == \"Windows\""
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
 | 
				
			||||||
 | 
					    {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "django"
 | 
				
			||||||
 | 
					version = "5.1.4"
 | 
				
			||||||
 | 
					description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.10"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "Django-5.1.4-py3-none-any.whl", hash = "sha256:236e023f021f5ce7dee5779de7b286565fdea5f4ab86bae5338e3f7b69896cf0"},
 | 
				
			||||||
 | 
					    {file = "Django-5.1.4.tar.gz", hash = "sha256:de450c09e91879fa5a307f696e57c851955c910a438a35e6b4c895e86bedc82a"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					asgiref = ">=3.8.1,<4"
 | 
				
			||||||
 | 
					sqlparse = ">=0.3.1"
 | 
				
			||||||
 | 
					tzdata = {version = "*", markers = "sys_platform == \"win32\""}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					argon2 = ["argon2-cffi (>=19.1.0)"]
 | 
				
			||||||
 | 
					bcrypt = ["bcrypt"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "django-banjo"
 | 
				
			||||||
 | 
					version = "0.9.1"
 | 
				
			||||||
 | 
					description = "A simplified abstraction over django for beginners."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = "<4.0,>=3.10"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "django_banjo-0.9.1-py3-none-any.whl", hash = "sha256:06df2553457099922fa4cab288c8cbe09b16d3a49739fe61cab53f264ff12349"},
 | 
				
			||||||
 | 
					    {file = "django_banjo-0.9.1.tar.gz", hash = "sha256:638747f37701b875efc46394d8021019a9f1ccd7fd681dc138dda38adea07659"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					django = ">=5.1.4,<6.0.0"
 | 
				
			||||||
 | 
					django-extensions = ">=3.2.3,<4.0.0"
 | 
				
			||||||
 | 
					environ = ">=1.0,<2.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "django-extensions"
 | 
				
			||||||
 | 
					version = "3.2.3"
 | 
				
			||||||
 | 
					description = "Extensions for Django"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.6"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"},
 | 
				
			||||||
 | 
					    {file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					Django = ">=3.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "environ"
 | 
				
			||||||
 | 
					version = "1.0"
 | 
				
			||||||
 | 
					description = "Stack Based Globals Management"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = "*"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "environ-1.0.tar.gz", hash = "sha256:4df7f1dfeb7d1c988d2e19a8bd5d547a526e0400aeb35adf732032472f35dcb0"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "fuzzywuzzy"
 | 
				
			||||||
 | 
					version = "0.18.0"
 | 
				
			||||||
 | 
					description = "Fuzzy string matching in python"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = "*"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "fuzzywuzzy-0.18.0-py2.py3-none-any.whl", hash = "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993"},
 | 
				
			||||||
 | 
					    {file = "fuzzywuzzy-0.18.0.tar.gz", hash = "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					speedup = ["python-levenshtein (>=0.12)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "idna"
 | 
				
			||||||
 | 
					version = "3.6"
 | 
				
			||||||
 | 
					description = "Internationalized Domain Names in Applications (IDNA)"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.5"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
 | 
				
			||||||
 | 
					    {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "importlib-metadata"
 | 
				
			||||||
 | 
					version = "7.1.0"
 | 
				
			||||||
 | 
					description = "Read metadata from Python packages"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"},
 | 
				
			||||||
 | 
					    {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					zipp = ">=0.5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
 | 
				
			||||||
 | 
					perf = ["ipython"]
 | 
				
			||||||
 | 
					testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "importlib-resources"
 | 
				
			||||||
 | 
					version = "6.4.0"
 | 
				
			||||||
 | 
					description = "Read resources from Python packages"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"},
 | 
				
			||||||
 | 
					    {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
 | 
				
			||||||
 | 
					testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "levenshtein"
 | 
				
			||||||
 | 
					version = "0.26.1"
 | 
				
			||||||
 | 
					description = "Python extension for computing string edit distances and similarities."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.9"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8dc4a4aecad538d944a1264c12769c99e3c0bf8e741fc5e454cc954913befb2e"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec108f368c12b25787c8b1a4537a1452bc53861c3ee4abc810cc74098278edcd"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69229d651c97ed5b55b7ce92481ed00635cdbb80fbfb282a22636e6945dc52d5"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79dcd157046d62482a7719b08ba9e3ce9ed3fc5b015af8ea989c734c702aedd4"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f53f9173ae21b650b4ed8aef1d0ad0c37821f367c221a982f4d2922b3044e0d"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3956f3c5c229257dbeabe0b6aacd2c083ebcc1e335842a6ff2217fe6cc03b6b"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1e83af732726987d2c4cd736f415dae8b966ba17b7a2239c8b7ffe70bfb5543"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4f052c55046c2a9c9b5f742f39e02fa6e8db8039048b8c1c9e9fdd27c8a240a1"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9895b3a98f6709e293615fde0dcd1bb0982364278fa2072361a1a31b3e388b7a"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a3777de1d8bfca054465229beed23994f926311ce666f5a392c8859bb2722f16"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:81c57e1135c38c5e6e3675b5e2077d8a8d3be32bf0a46c57276c092b1dffc697"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:91d5e7d984891df3eff7ea9fec8cf06fdfacc03cd074fd1a410435706f73b079"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-win32.whl", hash = "sha256:f48abff54054b4142ad03b323e80aa89b1d15cabc48ff49eb7a6ff7621829a56"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-win_amd64.whl", hash = "sha256:79dd6ad799784ea7b23edd56e3bf94b3ca866c4c6dee845658ee75bb4aefdabf"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp310-cp310-win_arm64.whl", hash = "sha256:3351ddb105ef010cc2ce474894c5d213c83dddb7abb96400beaa4926b0b745bd"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:44c51f5d33b3cfb9db518b36f1288437a509edd82da94c4400f6a681758e0cb6"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56b93203e725f9df660e2afe3d26ba07d71871b6d6e05b8b767e688e23dfb076"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:270d36c5da04a0d89990660aea8542227cbd8f5bc34e9fdfadd34916ff904520"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:480674c05077eeb0b0f748546d4fcbb386d7c737f9fff0010400da3e8b552942"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13946e37323728695ba7a22f3345c2e907d23f4600bc700bf9b4352fb0c72a48"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ceb673f572d1d0dc9b1cd75792bb8bad2ae8eb78a7c6721e23a3867d318cb6f2"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42d6fa242e3b310ce6bfd5af0c83e65ef10b608b885b3bb69863c01fb2fcff98"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b8b68295808893a81e0a1dbc2274c30dd90880f14d23078e8eb4325ee615fc68"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b01061d377d1944eb67bc40bef5d4d2f762c6ab01598efd9297ce5d0047eb1b5"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9d12c8390f156745e533d01b30773b9753e41d8bbf8bf9dac4b97628cdf16314"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:48825c9f967f922061329d1481b70e9fee937fc68322d6979bc623f69f75bc91"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d8ec137170b95736842f99c0e7a9fd8f5641d0c1b63b08ce027198545d983e2b"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-win32.whl", hash = "sha256:798f2b525a2e90562f1ba9da21010dde0d73730e277acaa5c52d2a6364fd3e2a"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-win_amd64.whl", hash = "sha256:55b1024516c59df55f1cf1a8651659a568f2c5929d863d3da1ce8893753153bd"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp311-cp311-win_arm64.whl", hash = "sha256:e52575cbc6b9764ea138a6f82d73d3b1bc685fe62e207ff46a963d4c773799f6"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cc741ca406d3704dc331a69c04b061fc952509a069b79cab8287413f434684bd"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:821ace3b4e1c2e02b43cf5dc61aac2ea43bdb39837ac890919c225a2c3f2fea4"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92694c9396f55d4c91087efacf81297bef152893806fc54c289fc0254b45384"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51ba374de7a1797d04a14a4f0ad3602d2d71fef4206bb20a6baaa6b6a502da58"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7aa5c3327dda4ef952769bacec09c09ff5bf426e07fdc94478c37955681885b"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e2517e8d3c221de2d1183f400aed64211fcfc77077b291ed9f3bb64f141cdc"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9092b622765c7649dd1d8af0f43354723dd6f4e570ac079ffd90b41033957438"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fc16796c85d7d8b259881d59cc8b5e22e940901928c2ff6924b2c967924e8a0b"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4370733967f5994ceeed8dc211089bedd45832ee688cecea17bfd35a9eb22b9"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3535ecfd88c9b283976b5bc61265855f59bba361881e92ed2b5367b6990c93fe"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:90236e93d98bdfd708883a6767826fafd976dac8af8fc4a0fb423d4fa08e1bf0"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:04b7cabb82edf566b1579b3ed60aac0eec116655af75a3c551fee8754ffce2ea"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-win32.whl", hash = "sha256:ae382af8c76f6d2a040c0d9ca978baf461702ceb3f79a0a3f6da8d596a484c5b"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-win_amd64.whl", hash = "sha256:fd091209798cfdce53746f5769987b4108fe941c54fb2e058c016ffc47872918"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp312-cp312-win_arm64.whl", hash = "sha256:7e82f2ea44a81ad6b30d92a110e04cd3c8c7c6034b629aca30a3067fa174ae89"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:790374a9f5d2cbdb30ee780403a62e59bef51453ac020668c1564d1e43438f0e"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7b05c0415c386d00efda83d48db9db68edd02878d6dbc6df01194f12062be1bb"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3114586032361722ddededf28401ce5baf1cf617f9f49fb86b8766a45a423ff"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2532f8a13b68bf09f152d906f118a88da2063da22f44c90e904b142b0a53d534"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:219c30be6aa734bf927188d1208b7d78d202a3eb017b1c5f01ab2034d2d4ccca"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397e245e77f87836308bd56305bba630010cd8298c34c4c44bd94990cdb3b7b1"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeff6ea3576f72e26901544c6c55c72a7b79b9983b6f913cba0e9edbf2f87a97"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a19862e3539a697df722a08793994e334cd12791e8144851e8a1dee95a17ff63"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:dc3b5a64f57c3c078d58b1e447f7d68cad7ae1b23abe689215d03fc434f8f176"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bb6c7347424a91317c5e1b68041677e4c8ed3e7823b5bbaedb95bffb3c3497ea"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b817376de4195a207cc0e4ca37754c0e1e1078c2a2d35a6ae502afde87212f9e"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7b50c3620ff47c9887debbb4c154aaaac3e46be7fc2e5789ee8dbe128bce6a17"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-win32.whl", hash = "sha256:9fb859da90262eb474c190b3ca1e61dee83add022c676520f5c05fdd60df902a"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-win_amd64.whl", hash = "sha256:8adcc90e3a5bfb0a463581d85e599d950fe3c2938ac6247b29388b64997f6e2d"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp313-cp313-win_arm64.whl", hash = "sha256:c2599407e029865dc66d210b8804c7768cbdbf60f061d993bb488d5242b0b73e"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc54ced948fc3feafce8ad4ba4239d8ffc733a0d70e40c0363ac2a7ab2b7251e"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e6516f69213ae393a220e904332f1a6bfc299ba22cf27a6520a1663a08eba0fb"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4cfea4eada1746d0c75a864bc7e9e63d4a6e987c852d6cec8d9cb0c83afe25b"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a323161dfeeac6800eb13cfe76a8194aec589cd948bcf1cdc03f66cc3ec26b72"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c23e749b68ebc9a20b9047317b5cd2053b5856315bc8636037a8adcbb98bed1"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f80dd7432d4b6cf493d012d22148db7af769017deb31273e43406b1fb7f091c"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ae7cd6e4312c6ef34b2e273836d18f9fff518d84d823feff5ad7c49668256e0"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dcdad740e841d791b805421c2b20e859b4ed556396d3063b3aa64cd055be648c"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e07afb1613d6f5fd99abd4e53ad3b446b4efaa0f0d8e9dfb1d6d1b9f3f884d32"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:f1add8f1d83099a98ae4ac472d896b7e36db48c39d3db25adf12b373823cdeff"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1010814b1d7a60833a951f2756dfc5c10b61d09976ce96a0edae8fecdfb0ea7c"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:33fa329d1bb65ce85e83ceda281aea31cee9f2f6e167092cea54f922080bcc66"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-win32.whl", hash = "sha256:488a945312f2f16460ab61df5b4beb1ea2254c521668fd142ce6298006296c98"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-win_amd64.whl", hash = "sha256:9f942104adfddd4b336c3997050121328c39479f69de702d7d144abb69ea7ab9"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-cp39-cp39-win_arm64.whl", hash = "sha256:c1d8f85b2672939f85086ed75effcf768f6077516a3e299c2ba1f91bc4644c22"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6cf8f1efaf90ca585640c5d418c30b7d66d9ac215cee114593957161f63acde0"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d5b2953978b8c158dd5cd93af8216a5cfddbf9de66cf5481c2955f44bb20767a"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b952b3732c4631c49917d4b15d78cb4a2aa006c1d5c12e2a23ba8e18a307a055"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07227281e12071168e6ae59238918a56d2a0682e529f747b5431664f302c0b42"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8191241cd8934feaf4d05d0cc0e5e72877cbb17c53bbf8c92af9f1aedaa247e9"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9e70d7ee157a9b698c73014f6e2b160830e7d2d64d2e342fefc3079af3c356fc"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0eb3059f826f6cb0a5bca4a85928070f01e8202e7ccafcba94453470f83e49d4"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:6c389e44da12d6fb1d7ba0a709a32a96c9391e9be4160ccb9269f37e040599ee"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e9de292f2c51a7d34a0ae23bec05391b8f61f35781cd3e4c6d0533e06250c55"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d87215113259efdca8716e53b6d59ab6d6009e119d95d45eccc083148855f33"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f00a3eebf68a82fb651d8d0e810c10bfaa60c555d21dde3ff81350c74fb4c2"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b3554c1b59de63d05075577380340c185ff41b028e541c0888fddab3c259a2b4"},
 | 
				
			||||||
 | 
					    {file = "levenshtein-0.26.1.tar.gz", hash = "sha256:0d19ba22330d50609b2349021ec3cf7d905c6fe21195a2d0d876a146e7ed2575"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					rapidfuzz = ">=3.9.0,<4.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "pronouncing"
 | 
				
			||||||
 | 
					version = "0.2.0"
 | 
				
			||||||
 | 
					description = "A simple interface for the CMU pronouncing dictionary"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = "*"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "pronouncing-0.2.0.tar.gz", hash = "sha256:ff7856e1d973b3e16ff490c5cf1abdb52f08f45e2c35e463249b75741331e7c4"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					cmudict = ">=0.4.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "python-levenshtein"
 | 
				
			||||||
 | 
					version = "0.26.1"
 | 
				
			||||||
 | 
					description = "Python extension for computing string edit distances and similarities."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.9"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "python_Levenshtein-0.26.1-py3-none-any.whl", hash = "sha256:8ef5e529dd640fb00f05ee62d998d2ee862f19566b641ace775d5ae16167b2ef"},
 | 
				
			||||||
 | 
					    {file = "python_levenshtein-0.26.1.tar.gz", hash = "sha256:24ba578e28058ebb4afa2700057e1678d7adf27e43cd1f17700c09a9009d5d3a"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					Levenshtein = "0.26.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "rapidfuzz"
 | 
				
			||||||
 | 
					version = "3.11.0"
 | 
				
			||||||
 | 
					description = "rapid fuzzy string matching"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.9"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb8a54543d16ab1b69e2c5ed96cabbff16db044a50eddfc028000138ca9ddf33"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:231c8b2efbd7f8d2ecd1ae900363ba168b8870644bb8f2b5aa96e4a7573bde19"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54e7f442fb9cca81e9df32333fb075ef729052bcabe05b0afc0441f462299114"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:906f1f2a1b91c06599b3dd1be207449c5d4fc7bd1e1fa2f6aef161ea6223f165"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed59044aea9eb6c663112170f2399b040d5d7b162828b141f2673e822093fa8"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cb1965a28b0fa64abdee130c788a0bc0bb3cf9ef7e3a70bf055c086c14a3d7e"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b488b244931d0291412917e6e46ee9f6a14376625e150056fe7c4426ef28225"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f0ba13557fec9d5ffc0a22826754a7457cc77f1b25145be10b7bb1d143ce84c6"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3871fa7dfcef00bad3c7e8ae8d8fd58089bad6fb21f608d2bf42832267ca9663"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b2669eafee38c5884a6e7cc9769d25c19428549dcdf57de8541cf9e82822e7db"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ffa1bb0e26297b0f22881b219ffc82a33a3c84ce6174a9d69406239b14575bd5"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:45b15b8a118856ac9caac6877f70f38b8a0d310475d50bc814698659eabc1cdb"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-win32.whl", hash = "sha256:22033677982b9c4c49676f215b794b0404073f8974f98739cb7234e4a9ade9ad"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:be15496e7244361ff0efcd86e52559bacda9cd975eccf19426a0025f9547c792"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp310-cp310-win_arm64.whl", hash = "sha256:714a7ba31ba46b64d30fccfe95f8013ea41a2e6237ba11a805a27cdd3bce2573"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8724a978f8af7059c5323d523870bf272a097478e1471295511cf58b2642ff83"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b63cb1f2eb371ef20fb155e95efd96e060147bdd4ab9fc400c97325dfee9fe1"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82497f244aac10b20710448645f347d862364cc4f7d8b9ba14bd66b5ce4dec18"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:339607394941801e6e3f6c1ecd413a36e18454e7136ed1161388de674f47f9d9"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84819390a36d6166cec706b9d8f0941f115f700b7faecab5a7e22fc367408bc3"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eea8d9e20632d68f653455265b18c35f90965e26f30d4d92f831899d6682149b"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b659e1e2ea2784a9a397075a7fc395bfa4fe66424042161c4bcaf6e4f637b38"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1315cd2a351144572e31fe3df68340d4b83ddec0af8b2e207cd32930c6acd037"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a7743cca45b4684c54407e8638f6d07b910d8d811347b9d42ff21262c7c23245"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5bb636b0150daa6d3331b738f7c0f8b25eadc47f04a40e5c23c4bfb4c4e20ae3"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:42f4dd264ada7a9aa0805ea0da776dc063533917773cf2df5217f14eb4429eae"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:51f24cb39e64256221e6952f22545b8ce21cacd59c0d3e367225da8fc4b868d8"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-win32.whl", hash = "sha256:aaf391fb6715866bc14681c76dc0308f46877f7c06f61d62cc993b79fc3c4a2a"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:ebadd5b8624d8ad503e505a99b8eb26fe3ea9f8e9c2234e805a27b269e585842"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:d895998fec712544c13cfe833890e0226585cf0391dd3948412441d5d68a2b8c"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f382fec4a7891d66fb7163c90754454030bb9200a13f82ee7860b6359f3f2fa8"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dfaefe08af2a928e72344c800dcbaf6508e86a4ed481e28355e8d4b6a6a5230e"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92ebb7c12f682b5906ed98429f48a3dd80dd0f9721de30c97a01473d1a346576"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a1b3ebc62d4bcdfdeba110944a25ab40916d5383c5e57e7c4a8dc0b6c17211a"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c6d7fea39cb33e71de86397d38bf7ff1a6273e40367f31d05761662ffda49e4"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99aebef8268f2bc0b445b5640fd3312e080bd17efd3fbae4486b20ac00466308"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4469307f464ae3089acf3210b8fc279110d26d10f79e576f385a98f4429f7d97"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:eb97c53112b593f89a90b4f6218635a9d1eea1d7f9521a3b7d24864228bbc0aa"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ef8937dae823b889c0273dfa0f0f6c46a3658ac0d851349c464d1b00e7ff4252"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d95f9e9f3777b96241d8a00d6377cc9c716981d828b5091082d0fe3a2924b43e"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:b1d67d67f89e4e013a5295e7523bc34a7a96f2dba5dd812c7c8cb65d113cbf28"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d994cf27e2f874069884d9bddf0864f9b90ad201fcc9cb2f5b82bacc17c8d5f2"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-win32.whl", hash = "sha256:ba26d87fe7fcb56c4a53b549a9e0e9143f6b0df56d35fe6ad800c902447acd5b"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:b1f7efdd7b7adb32102c2fa481ad6f11923e2deb191f651274be559d56fc913b"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:ed78c8e94f57b44292c1a0350f580e18d3a3c5c0800e253f1583580c1b417ad2"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e60814edd0c9b511b5f377d48b9782b88cfe8be07a98f99973669299c8bb318a"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f28952da055dbfe75828891cd3c9abf0984edc8640573c18b48c14c68ca5e06"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e8f93bc736020351a6f8e71666e1f486bb8bd5ce8112c443a30c77bfde0eb68"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76a4a11ba8f678c9e5876a7d465ab86def047a4fcc043617578368755d63a1bc"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc0e0d41ad8a056a9886bac91ff9d9978e54a244deb61c2972cc76b66752de9c"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e8ea35f2419c7d56b3e75fbde2698766daedb374f20eea28ac9b1f668ef4f74"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd340bbd025302276b5aa221dccfe43040c7babfc32f107c36ad783f2ffd8775"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:494eef2c68305ab75139034ea25328a04a548d297712d9cf887bf27c158c388b"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5a167344c1d6db06915fb0225592afdc24d8bafaaf02de07d4788ddd37f4bc2f"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8c7af25bda96ac799378ac8aba54a8ece732835c7b74cfc201b688a87ed11152"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d2a0f7e17f33e7890257367a1662b05fecaf56625f7dbb6446227aaa2b86448b"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d0d26c7172bdb64f86ee0765c5b26ea1dc45c52389175888ec073b9b28f4305"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-win32.whl", hash = "sha256:6ad02bab756751c90fa27f3069d7b12146613061341459abf55f8190d899649f"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:b1472986fd9c5d318399a01a0881f4a0bf4950264131bb8e2deba9df6d8c362b"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c408f09649cbff8da76f8d3ad878b64ba7f7abdad1471efb293d2c075e80c822"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1bac4873f6186f5233b0084b266bfb459e997f4c21fc9f029918f44a9eccd304"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f9f12c2d0aa52b86206d2059916153876a9b1cf9dfb3cf2f344913167f1c3d4"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd501de6f7a8f83557d20613b58734d1cb5f0be78d794cde64fe43cfc63f5f2"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4416ca69af933d4a8ad30910149d3db6d084781d5c5fdedb713205389f535385"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f0821b9bdf18c5b7d51722b906b233a39b17f602501a966cfbd9b285f8ab83cd"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0edecc3f90c2653298d380f6ea73b536944b767520c2179ec5d40b9145e47aa"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4513dd01cee11e354c31b75f652d4d466c9440b6859f84e600bdebfccb17735a"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d9727b85511b912571a76ce53c7640ba2c44c364e71cef6d7359b5412739c570"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ab9eab33ee3213f7751dc07a1a61b8d9a3d748ca4458fffddd9defa6f0493c16"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6b01c1ddbb054283797967ddc5433d5c108d680e8fa2684cf368be05407b07e4"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:3857e335f97058c4b46fa39ca831290b70de554a5c5af0323d2f163b19c5f2a6"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d98a46cf07c0c875d27e8a7ed50f304d83063e49b9ab63f21c19c154b4c0d08d"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-win32.whl", hash = "sha256:c36539ed2c0173b053dafb221458812e178cfa3224ade0960599bec194637048"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:ec8d7d8567e14af34a7911c98f5ac74a3d4a743cd848643341fc92b12b3784ff"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-cp39-cp39-win_arm64.whl", hash = "sha256:62171b270ecc4071be1c1f99960317db261d4c8c83c169e7f8ad119211fe7397"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f06e3c4c0a8badfc4910b9fd15beb1ad8f3b8fafa8ea82c023e5e607b66a78e4"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fe7aaf5a54821d340d21412f7f6e6272a9b17a0cbafc1d68f77f2fc11009dcd5"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25398d9ac7294e99876a3027ffc52c6bebeb2d702b1895af6ae9c541ee676702"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a52eea839e4bdc72c5e60a444d26004da00bb5bc6301e99b3dde18212e41465"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c87319b0ab9d269ab84f6453601fd49b35d9e4a601bbaef43743f26fabf496c"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3048c6ed29d693fba7d2a7caf165f5e0bb2b9743a0989012a98a47b975355cca"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b04f29735bad9f06bb731c214f27253bd8bedb248ef9b8a1b4c5bde65b838454"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7864e80a0d4e23eb6194254a81ee1216abdc53f9dc85b7f4d56668eced022eb8"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3794df87313dfb56fafd679b962e0613c88a293fd9bd5dd5c2793d66bf06a101"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d71da0012face6f45432a11bc59af19e62fac5a41f8ce489e80c0add8153c3d1"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff38378346b7018f42cbc1f6d1d3778e36e16d8595f79a312b31e7c25c50bd08"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6668321f90aa02a5a789d4e16058f2e4f2692c5230252425c3532a8a62bc3424"},
 | 
				
			||||||
 | 
					    {file = "rapidfuzz-3.11.0.tar.gz", hash = "sha256:a53ca4d3f52f00b393fab9b5913c5bafb9afc27d030c8a1db1283da6917a860f"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					all = ["numpy"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "requests"
 | 
				
			||||||
 | 
					version = "2.32.3"
 | 
				
			||||||
 | 
					description = "Python HTTP for Humans."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
 | 
				
			||||||
 | 
					    {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					certifi = ">=2017.4.17"
 | 
				
			||||||
 | 
					charset-normalizer = ">=2,<4"
 | 
				
			||||||
 | 
					idna = ">=2.5,<4"
 | 
				
			||||||
 | 
					urllib3 = ">=1.21.1,<3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					socks = ["PySocks (>=1.5.6,!=1.5.7)"]
 | 
				
			||||||
 | 
					use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "sqlparse"
 | 
				
			||||||
 | 
					version = "0.4.4"
 | 
				
			||||||
 | 
					description = "A non-validating SQL parser."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.5"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"},
 | 
				
			||||||
 | 
					    {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					dev = ["build", "flake8"]
 | 
				
			||||||
 | 
					doc = ["sphinx"]
 | 
				
			||||||
 | 
					test = ["pytest", "pytest-cov"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "tqdm"
 | 
				
			||||||
 | 
					version = "4.67.1"
 | 
				
			||||||
 | 
					description = "Fast, Extensible Progress Meter"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.7"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"},
 | 
				
			||||||
 | 
					    {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					colorama = {version = "*", markers = "platform_system == \"Windows\""}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"]
 | 
				
			||||||
 | 
					discord = ["requests"]
 | 
				
			||||||
 | 
					notebook = ["ipywidgets (>=6)"]
 | 
				
			||||||
 | 
					slack = ["slack-sdk"]
 | 
				
			||||||
 | 
					telegram = ["requests"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "typing-extensions"
 | 
				
			||||||
 | 
					version = "4.10.0"
 | 
				
			||||||
 | 
					description = "Backported and Experimental Type Hints for Python 3.8+"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					markers = "python_version < \"3.11\""
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
 | 
				
			||||||
 | 
					    {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "tzdata"
 | 
				
			||||||
 | 
					version = "2024.1"
 | 
				
			||||||
 | 
					description = "Provider of IANA time zone data"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=2"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					markers = "sys_platform == \"win32\""
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"},
 | 
				
			||||||
 | 
					    {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "urllib3"
 | 
				
			||||||
 | 
					version = "2.2.1"
 | 
				
			||||||
 | 
					description = "HTTP library with thread-safe connection pooling, file post, and more."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
 | 
				
			||||||
 | 
					    {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
 | 
				
			||||||
 | 
					h2 = ["h2 (>=4,<5)"]
 | 
				
			||||||
 | 
					socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
 | 
				
			||||||
 | 
					zstd = ["zstandard (>=0.18.0)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "zipp"
 | 
				
			||||||
 | 
					version = "3.18.1"
 | 
				
			||||||
 | 
					description = "Backport of pathlib-compatible object wrapper for zip files"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					groups = ["main"]
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"},
 | 
				
			||||||
 | 
					    {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
 | 
				
			||||||
 | 
					testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[metadata]
 | 
				
			||||||
 | 
					lock-version = "2.1"
 | 
				
			||||||
 | 
					python-versions = ">=3.10,<4.0"
 | 
				
			||||||
 | 
					content-hash = "36031932e2f47755930e183e508b7081b97d7eb75843e1f5395aad4c8b346622"
 | 
				
			||||||
							
								
								
									
										26
									
								
								pyproject.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								pyproject.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					[project]
 | 
				
			||||||
 | 
					name = "lab-server"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					description = ""
 | 
				
			||||||
 | 
					authors = [
 | 
				
			||||||
 | 
					    {name = "Chris Proctor",email = "chris@chrisproctor.net"}
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					license = {text = "MIT"}
 | 
				
			||||||
 | 
					readme = "README.md"
 | 
				
			||||||
 | 
					requires-python = ">=3.10,<4.0"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					    "django-banjo (>=0.9.1,<0.10.0)",
 | 
				
			||||||
 | 
					    "requests (>=2.32.3,<3.0.0)",
 | 
				
			||||||
 | 
					    "fuzzywuzzy (>=0.18.0,<0.19.0)",
 | 
				
			||||||
 | 
					    "python-levenshtein (>=0.26.1,<0.27.0)",
 | 
				
			||||||
 | 
					    "pronouncing (>=0.2.0,<0.3.0)",
 | 
				
			||||||
 | 
					    "tqdm (>=4.67.1,<5.0.0)"
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[build-system]
 | 
				
			||||||
 | 
					requires = ["poetry-core>=2.0.0,<3.0.0"]
 | 
				
			||||||
 | 
					build-backend = "poetry.core.masonry.api"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[tool.poetry]
 | 
				
			||||||
 | 
					package-mode = false
 | 
				
			||||||
							
								
								
									
										97
									
								
								riddle_server/app/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								riddle_server/app/models.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					from banjo.models import Model, StringField, IntegerField
 | 
				
			||||||
 | 
					from fuzzywuzzy import fuzz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Riddle(Model):
 | 
				
			||||||
 | 
					    question = StringField()
 | 
				
			||||||
 | 
					    answer = StringField()
 | 
				
			||||||
 | 
					    guesses = IntegerField()
 | 
				
			||||||
 | 
					    correct = IntegerField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MIN_FUZZ_RATIO = 80
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __repr__(self):
 | 
				
			||||||
 | 
					        """Declares how to represent a Riddle as a string.
 | 
				
			||||||
 | 
					        A riddle's string will look something like this:
 | 
				
			||||||
 | 
					        <Riddle 12: Where can you get dragon milk? (3/15)>
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return "<Riddle {}: {} ({}/{})>".format(
 | 
				
			||||||
 | 
					            self.id or '(unsaved)', 
 | 
				
			||||||
 | 
					            self.question, 
 | 
				
			||||||
 | 
					            self.correct, 
 | 
				
			||||||
 | 
					            self.guesses
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def is_valid(self):
 | 
				
			||||||
 | 
					        "Checks whether this riddle is valid. In other words, when validate() finds no errors."
 | 
				
			||||||
 | 
					        return len(self.validate()) == 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def validate(self):
 | 
				
			||||||
 | 
					        "Checks whether this riddle can be saved"
 | 
				
			||||||
 | 
					        errors = []
 | 
				
			||||||
 | 
					        if self.question is None:
 | 
				
			||||||
 | 
					            errors.append("question is required")
 | 
				
			||||||
 | 
					        if self.answer is None:
 | 
				
			||||||
 | 
					            errors.append("answer is required")
 | 
				
			||||||
 | 
					        return errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def difficulty(self):
 | 
				
			||||||
 | 
					        """Calculates and returns the riddle's difficulty. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The difficulty is basically 1 minus the fraction of guesses which were correct. 
 | 
				
			||||||
 | 
					        So a Riddle with a difficulty of 1 is impossibly hard, while a Riddle with a difficulty 
 | 
				
			||||||
 | 
					        of 0 is easy--everyone gets it right!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        There is an interesting detail here though. Instead of 1 - correct/guesses, we add 1 to 
 | 
				
			||||||
 | 
					        correct and we add 1 to guesses. This is called "smoothing" and it provides two benefits:
 | 
				
			||||||
 | 
					        First, we avoid having an undefined difficulty when there have been no guesses (0/0 would 
 | 
				
			||||||
 | 
					        raise a ZeroDivisionError) Second, it gives better values for difficulty when there have been no 
 | 
				
			||||||
 | 
					        correct guesses or no incorrect guesses. Consider an impossible riddle:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Number of wrong guesses     Difficulty with smoothing   Difficulty without smoothing
 | 
				
			||||||
 | 
					        0                           0                           error
 | 
				
			||||||
 | 
					        1                           0.5                         1
 | 
				
			||||||
 | 
					        2                           0.66                        1
 | 
				
			||||||
 | 
					        3                           0.75                        1
 | 
				
			||||||
 | 
					        4                           0.8                         1
 | 
				
			||||||
 | 
					        100                         0.99                        1
 | 
				
			||||||
 | 
					        1000                        0.999                       1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        With smoothing, a Riddle's difficulty can only be really high if there are few correct guesses
 | 
				
			||||||
 | 
					        and a lot of guesses. This seems like the right way to define difficulty.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return 1 - (self.correct + 1) / (self.guesses + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_dict(self, with_answer=True):
 | 
				
			||||||
 | 
					        "Returns this Riddle's properties in a dict, optionally including the answer"
 | 
				
			||||||
 | 
					        result = {
 | 
				
			||||||
 | 
					            "id": self.id,
 | 
				
			||||||
 | 
					            "question": self.question, 
 | 
				
			||||||
 | 
					            "guesses": self.guesses, 
 | 
				
			||||||
 | 
					            "correct": self.correct,
 | 
				
			||||||
 | 
					            "difficulty": self.difficulty(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if with_answer:
 | 
				
			||||||
 | 
					            result["answer"] = self.answer
 | 
				
			||||||
 | 
					        return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_guess(self, guess):
 | 
				
			||||||
 | 
					        """Checks whether a guess is correct and logs the attempt.
 | 
				
			||||||
 | 
					        We don't want to be too strict, so we will accept guesses which are close to the answer.
 | 
				
			||||||
 | 
					        Fuzzy string-matching is an interesting problem, which we will sidestep by using the
 | 
				
			||||||
 | 
					        `fuzzywuzzy` library. `FUZZ_RATIO` is our limit for how similar the answers have to be.
 | 
				
			||||||
 | 
					        Also, we don't care about upper-case and lower-case, so we'll cast everything to lower.
 | 
				
			||||||
 | 
					        For example, consider the riddle, "What's brown and sticky?" The answer, of course, is
 | 
				
			||||||
 | 
					        "A stick" Here are some attempts with their fuzz ratios:                                                                                               
 | 
				
			||||||
 | 
					        - "a stick"         100
 | 
				
			||||||
 | 
					        - "a stik"          92
 | 
				
			||||||
 | 
					        - "stick"           83
 | 
				
			||||||
 | 
					        - "it's a stick"    74
 | 
				
			||||||
 | 
					        - "idk"             40                                                                                             """
 | 
				
			||||||
 | 
					        self.guesses += 1
 | 
				
			||||||
 | 
					        similarity = fuzz.ratio(guess.lower(), self.answer.lower())
 | 
				
			||||||
 | 
					        is_correct = similarity >= self.MIN_FUZZ_RATIO
 | 
				
			||||||
 | 
					        if is_correct:
 | 
				
			||||||
 | 
					            self.correct+= 1
 | 
				
			||||||
 | 
					        self.save()
 | 
				
			||||||
 | 
					        return is_correct
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										41
									
								
								riddle_server/app/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								riddle_server/app/views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					from banjo.urls import route_get, route_post
 | 
				
			||||||
 | 
					from banjo.http import BadRequest, NotFound
 | 
				
			||||||
 | 
					from app.models import Riddle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@route_get('all', args={})
 | 
				
			||||||
 | 
					def list_riddles(params):
 | 
				
			||||||
 | 
					    riddles = sorted(Riddle.objects.all(), key=lambda riddle: riddle.difficulty())
 | 
				
			||||||
 | 
					    return {'riddles': [riddle.to_dict(with_answer=False) for riddle in riddles]}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@route_post('new', args={'question': str, 'answer': str})
 | 
				
			||||||
 | 
					def create_riddle(params):
 | 
				
			||||||
 | 
					    riddle = Riddle.from_dict(params)
 | 
				
			||||||
 | 
					    errors = riddle.validate()
 | 
				
			||||||
 | 
					    if len(errors) == 0:
 | 
				
			||||||
 | 
					        riddle.save()
 | 
				
			||||||
 | 
					        return riddle.to_dict(with_answer=False)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        raise BadRequest("Riddle not found")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					@route_get('show', args={'id': int})
 | 
				
			||||||
 | 
					def show_riddle(params):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        riddle = Riddle.objects.get(id=params['id'])
 | 
				
			||||||
 | 
					        return riddle.to_dict(with_answer=False)
 | 
				
			||||||
 | 
					    except Riddle.DoesNotExist:
 | 
				
			||||||
 | 
					        raise NotFound("Riddle not found")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@route_post('guess', args={'id': int, "answer": str})
 | 
				
			||||||
 | 
					def guess_answer(params):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        riddle = Riddle.objects.get(id=params['id'])
 | 
				
			||||||
 | 
					        correct = riddle.check_guess(params['answer'])
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            "guess": params['answer'], 
 | 
				
			||||||
 | 
					            "correct": correct,
 | 
				
			||||||
 | 
					            "riddle": riddle.to_dict(with_answer=correct)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    except Riddle.DoesNotExist:
 | 
				
			||||||
 | 
					        raise NotFound("Riddle not found")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Reference in New Issue
	
	Block a user