# Project export: QuizGenie

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: Got a midterm coming up? Don't know what to study? Meet Quiz Genie — your AI-powered study companion that can transform your notes or subjects into personalized practice quizzes.
- Devpost: https://devpost.com/software/quizgenie-7uljsp
- GitHub: https://github.com/brendan-ly/calhacks-quiz-genie
- Video: https://www.youtube.com/embed/xWmm6nT2aio?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — Brendan Ly (25 commits), Ryan (10 commits), raoot (2 commits)

## Devpost submission (written by the team)

### Inspiration

We use note-taking and study apps like Notion and Quizlet a lot, so we had an idea and asked: Wouldn't it be nice if we could make practice problems based off of our notes? And so that's how QuizGenie was born. We were inspired by how Quizlet made learning super easy for many students, and wanted to expand the future of online study resources by incorporating AI into a quiz creator.

### What it does

QuizGenie is an AI-powered platform that takes in user's notes or prompts and generates customizable quizzes that allow you to practice and master concepts. The application provides dynamic grading and real-time feedback as you take the quiz, so you are always learning on the way. It also allows you to customize the length of the quiz, so whether you are free at home or on the go, you will always be able to generate a quiz that meets your time constraints.

### How we built it

We used React + Vite for the frontend, and Flask with Python for the backend. We also used Gemini's API to process text and return multiple-choice questions.

### Challenges we ran into

This was collectively our first time doing a hackathon, so we had to learn a lot of stuff on the fly such as learning how to parse LLM responses into JSON and creating a frontend design that communicates with the backend. We also spent a significant amount of time familiarizing ourselves with the React + Vite framework, and learned how to create dynamic UIs through a lot of trial and error.

### Accomplishments we're proud of

We're proud of making an app that can leverage the Gemini API, as it was our first time working with anything AI-related. We're also proud to have persevered through an obstacle that we thought we couldn't solve, which was parsing the JSON data from the API, and rendering it onto the frontend. Additionally, we were glad we could fully use the power of prompt engineering to make Gemini's responses more varied, useful, and relevant.

### What we learned

We learned how to work with backend APIs in Python, and how to engineer the best prompt for Gemini to return responses in the format of a quiz. We also learned how create a frontend design using the React + Vite framework and connect it with the backend by performing RESTful API calls.

### What's next

There were a lot of features that we wanted to include but couldn't because of time restraints. These include but aren't limited to: ** Storing previous quizzes by implementing a database, such as MongoDB ** Implementing a final grading system and allowing users to track their progress over time, possibly using additional frameworks to allow data visualization. ** Allowing for more problem types: multiple-answer checkboxes, free-response, matching, etc. ** Implementing an option for AI voice feedback and live Text-To-Speech ** Implementing an additional chatbox window, allowing users to ask for help and learn while doing the quiz.

## README (from the GitHub repository)

<h1>Quiz Genie</h1>
Quiz Genie is an AI-powered quiz generator that creates customizable practice quizzes based on your notes or selected subjects. Enhance your study sessions with tailored quizzes designed to reinforce your knowledge and boost your learning efficiency.

<h1>Features</h1>

*  AI-Driven Quiz Creation: Generate quizzes instantly using advanced AI algorithms.
* Customizable Topics: From dumping your entire notes or specific subjects, Quiz Genie can curate quizzes for focused practice.
* User-Friendly Interface: Intuitive design for seamless quiz generation and interaction.


## Detected evidence (automated analysis)

Indexed codebase: 2639 recognized source files, 25673 KB.
- C (language) — detected in the code
- CSS (language) — detected in the code
- Flask (technology) — detected in the code
- Google Gemini (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code

## Codebase structure (from repository index)

### Files (120 of 4531)

```
.env
.gitignore
backend/.flaskenv
backend/app.py
backend/requirements.txt
doiasjdiasjdas
eslint.config.js
index.html
myenv/Lib/site-packages/aiohappyeyeballs-2.4.3.dist-info/INSTALLER
myenv/Lib/site-packages/aiohappyeyeballs-2.4.3.dist-info/LICENSE
myenv/Lib/site-packages/aiohappyeyeballs-2.4.3.dist-info/METADATA
myenv/Lib/site-packages/aiohappyeyeballs-2.4.3.dist-info/RECORD
myenv/Lib/site-packages/aiohappyeyeballs-2.4.3.dist-info/WHEEL
myenv/Lib/site-packages/aiohappyeyeballs/__init__.py
myenv/Lib/site-packages/aiohappyeyeballs/_staggered.py
myenv/Lib/site-packages/aiohappyeyeballs/impl.py
myenv/Lib/site-packages/aiohappyeyeballs/py.typed
myenv/Lib/site-packages/aiohappyeyeballs/types.py
myenv/Lib/site-packages/aiohappyeyeballs/utils.py
myenv/Lib/site-packages/aiohttp-3.10.10.dist-info/INSTALLER
myenv/Lib/site-packages/aiohttp-3.10.10.dist-info/LICENSE.txt
myenv/Lib/site-packages/aiohttp-3.10.10.dist-info/METADATA
myenv/Lib/site-packages/aiohttp-3.10.10.dist-info/RECORD
myenv/Lib/site-packages/aiohttp-3.10.10.dist-info/top_level.txt
myenv/Lib/site-packages/aiohttp-3.10.10.dist-info/WHEEL
myenv/Lib/site-packages/aiohttp/__init__.py
myenv/Lib/site-packages/aiohttp/_cparser.pxd
myenv/Lib/site-packages/aiohttp/_find_header.pxd
myenv/Lib/site-packages/aiohttp/_headers.pxi
myenv/Lib/site-packages/aiohttp/_helpers.cp312-win_amd64.pyd
myenv/Lib/site-packages/aiohttp/_helpers.pyi
myenv/Lib/site-packages/aiohttp/_helpers.pyx
myenv/Lib/site-packages/aiohttp/_http_parser.cp312-win_amd64.pyd
myenv/Lib/site-packages/aiohttp/_http_parser.pyx
myenv/Lib/site-packages/aiohttp/_http_writer.cp312-win_amd64.pyd
myenv/Lib/site-packages/aiohttp/_http_writer.pyx
myenv/Lib/site-packages/aiohttp/_websocket.cp312-win_amd64.pyd
myenv/Lib/site-packages/aiohttp/_websocket.pyx
myenv/Lib/site-packages/aiohttp/.hash/_cparser.pxd.hash
myenv/Lib/site-packages/aiohttp/.hash/_find_header.pxd.hash
myenv/Lib/site-packages/aiohttp/.hash/_helpers.pyi.hash
myenv/Lib/site-packages/aiohttp/.hash/_helpers.pyx.hash
myenv/Lib/site-packages/aiohttp/.hash/_http_parser.pyx.hash
myenv/Lib/site-packages/aiohttp/.hash/_http_writer.pyx.hash
myenv/Lib/site-packages/aiohttp/.hash/_websocket.pyx.hash
myenv/Lib/site-packages/aiohttp/.hash/hdrs.py.hash
myenv/Lib/site-packages/aiohttp/abc.py
myenv/Lib/site-packages/aiohttp/base_protocol.py
myenv/Lib/site-packages/aiohttp/client_exceptions.py
myenv/Lib/site-packages/aiohttp/client_proto.py
myenv/Lib/site-packages/aiohttp/client_reqrep.py
myenv/Lib/site-packages/aiohttp/client_ws.py
myenv/Lib/site-packages/aiohttp/client.py
myenv/Lib/site-packages/aiohttp/compression_utils.py
myenv/Lib/site-packages/aiohttp/connector.py
myenv/Lib/site-packages/aiohttp/cookiejar.py
myenv/Lib/site-packages/aiohttp/formdata.py
myenv/Lib/site-packages/aiohttp/hdrs.py
myenv/Lib/site-packages/aiohttp/helpers.py
myenv/Lib/site-packages/aiohttp/http_exceptions.py
myenv/Lib/site-packages/aiohttp/http_parser.py
myenv/Lib/site-packages/aiohttp/http_websocket.py
myenv/Lib/site-packages/aiohttp/http_writer.py
myenv/Lib/site-packages/aiohttp/http.py
myenv/Lib/site-packages/aiohttp/log.py
myenv/Lib/site-packages/aiohttp/multipart.py
myenv/Lib/site-packages/aiohttp/payload_streamer.py
myenv/Lib/site-packages/aiohttp/payload.py
myenv/Lib/site-packages/aiohttp/py.typed
myenv/Lib/site-packages/aiohttp/pytest_plugin.py
myenv/Lib/site-packages/aiohttp/resolver.py
myenv/Lib/site-packages/aiohttp/streams.py
myenv/Lib/site-packages/aiohttp/tcp_helpers.py
myenv/Lib/site-packages/aiohttp/test_utils.py
myenv/Lib/site-packages/aiohttp/tracing.py
myenv/Lib/site-packages/aiohttp/typedefs.py
myenv/Lib/site-packages/aiohttp/web_app.py
myenv/Lib/site-packages/aiohttp/web_exceptions.py
myenv/Lib/site-packages/aiohttp/web_fileresponse.py
myenv/Lib/site-packages/aiohttp/web_log.py
myenv/Lib/site-packages/aiohttp/web_middlewares.py
myenv/Lib/site-packages/aiohttp/web_protocol.py
myenv/Lib/site-packages/aiohttp/web_request.py
myenv/Lib/site-packages/aiohttp/web_response.py
myenv/Lib/site-packages/aiohttp/web_routedef.py
myenv/Lib/site-packages/aiohttp/web_runner.py
myenv/Lib/site-packages/aiohttp/web_server.py
myenv/Lib/site-packages/aiohttp/web_urldispatcher.py
myenv/Lib/site-packages/aiohttp/web_ws.py
myenv/Lib/site-packages/aiohttp/web.py
myenv/Lib/site-packages/aiohttp/worker.py
myenv/Lib/site-packages/aiosignal-1.3.1.dist-info/INSTALLER
myenv/Lib/site-packages/aiosignal-1.3.1.dist-info/LICENSE
myenv/Lib/site-packages/aiosignal-1.3.1.dist-info/METADATA
myenv/Lib/site-packages/aiosignal-1.3.1.dist-info/RECORD
myenv/Lib/site-packages/aiosignal-1.3.1.dist-info/top_level.txt
myenv/Lib/site-packages/aiosignal-1.3.1.dist-info/WHEEL
myenv/Lib/site-packages/aiosignal/__init__.py
myenv/Lib/site-packages/aiosignal/__init__.pyi
myenv/Lib/site-packages/aiosignal/py.typed
myenv/Lib/site-packages/annotated_types-0.7.0.dist-info/INSTALLER
myenv/Lib/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE
myenv/Lib/site-packages/annotated_types-0.7.0.dist-info/METADATA
myenv/Lib/site-packages/annotated_types-0.7.0.dist-info/RECORD
myenv/Lib/site-packages/annotated_types-0.7.0.dist-info/WHEEL
myenv/Lib/site-packages/annotated_types/__init__.py
myenv/Lib/site-packages/annotated_types/py.typed
myenv/Lib/site-packages/annotated_types/test_cases.py
myenv/Lib/site-packages/apiclient/__init__.py
myenv/Lib/site-packages/apispec-6.6.1.dist-info/INSTALLER
myenv/Lib/site-packages/apispec-6.6.1.dist-info/LICENSE
myenv/Lib/site-packages/apispec-6.6.1.dist-info/METADATA
myenv/Lib/site-packages/apispec-6.6.1.dist-info/RECORD
myenv/Lib/site-packages/apispec-6.6.1.dist-info/WHEEL
myenv/Lib/site-packages/apispec/__init__.py
myenv/Lib/site-packages/apispec/core.py
myenv/Lib/site-packages/apispec/exceptions.py
myenv/Lib/site-packages/apispec/ext/__init__.py
myenv/Lib/site-packages/apispec/ext/marshmallow/__init__.py
myenv/Lib/site-packages/apispec/ext/marshmallow/common.py
[4411 more files omitted for size]
```

### Dependencies

- backend/requirements.txt: flask, flask-cors
- package.json: @eslint/js@^9.11.1, @google/generative-ai@^0.21.0, @types/react@^18.3.10, @types/react-dom@^18.3.0, @vitejs/plugin-react@^4.3.2, antd@^5.21.4, autoprefixer@^10.4.20, axios@^1.7.7, dotenv@^16.4.5, eslint@^9.11.1, eslint-plugin-react@^7.37.0, eslint-plugin-react-hooks@^5.1.0-rc.0, eslint-plugin-react-refresh@^0.4.12, globals@^15.9.0, postcss@^8.4.47, prettier-plugin-tailwindcss@^0.6.8, react@^18.3.1, react-dom@^18.3.1, react-router-dom@^6.27.0, tailwind@^4.0.0, tailwindcss@^3.4.14, vite@^5.4.8

### Recent commits (newest first)

- Update app.py
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Merge pull request #2 from brendan-ly/ryan-quiz
- Pre-finalized v1 Brendan
- auto reloading on submit
- visual changes
- buttons change color and validate answer
- remove pycache
- pycache
- new branch
- changes
- added number of questions box
- improved buttons
- Quiz answer sheet displayed
- Fixed JSON and frontend output
- added back submit button

## Key source files (fetched from GitHub, selected and truncated for size)

### myenv/Lib/site-packages/idna-3.10.dist-info/LICENSE.md

```markdown
BSD 3-Clause License

Copyright (c) 2013-2024, Kim Davies and contributors.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

```

### myenv/Lib/site-packages/uvicorn-0.30.6.dist-info/licenses/LICENSE.md

```markdown
Copyright © 2017-present, [Encode OSS Ltd](https://www.encode.io/).
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

```

### package.json

```
{
  "name": "calhacks-quiz-genie",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "@google/generative-ai": "^0.21.0",
    "antd": "^5.21.4",
    "axios": "^1.7.7",
    "dotenv": "^16.4.5",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-router-dom": "^6.27.0",
    "tailwind": "^4.0.0"
  },
  "devDependencies": {
    "@eslint/js": "^9.11.1",
    "@types/react": "^18.3.10",
    "@types/react-dom": "^18.3.0",
    "@vitejs/plugin-react": "^4.3.2",
    "autoprefixer": "^10.4.20",
    "eslint": "^9.11.1",
    "eslint-plugin-react": "^7.37.0",
    "eslint-plugin-react-hooks": "^5.1.0-rc.0",
    "eslint-plugin-react-refresh": "^0.4.12",
    "globals": "^15.9.0",
    "postcss": "^8.4.47",
    "prettier-plugin-tailwindcss": "^0.6.8",
    "tailwindcss": "^3.4.14",
    "vite": "^5.4.8"
  }
}

```

### backend/requirements.txt

```
flask
flask-cors

```

### src/App.jsx

```javascript
import { useState } from 'react'
import HomePage from './Home Page/HomePage'
import './index.css'

function App() {
  const [count, setCount] = useState(0)

  return (
    <>
      <HomePage/>
    </>
  )
}

export default App
```

### src/main.jsx

```javascript
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import React from 'react'
import ReactDOM from 'react-dom'

import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'
import App from './App.jsx'
import Quiz from './Quiz/Quiz.jsx'

ReactDOM.render(
  <React.StrictMode>
    <Router>
      <Routes>
        <Route path="/" element={<App />} />
        <Route path="/quiz" element={<Quiz />} />
      </Routes>
    </Router>
  </React.StrictMode>,
  document.getElementById('root')
)
```

### backend/app.py

```python
from flask import Flask, request, jsonify
from flask_cors import CORS
import google.generativeai as genai
import os
import json

os.environ["GEMINI_API_KEY"];

genai.configure(api_key=os.environ["GEMINI_API_KEY"])

model = genai.GenerativeModel(model_name="gemini-1.5-flash")
app = Flask(__name__)
CORS(app)  # Enable CORS to allow communication between React and Flask

@app.route('/send-text', methods=['POST'])
def handle_text():
    data = request.get_json()  # Correct method to get JSON data
    input_text = data.get('inputText')
    question_number = data.get('questionNumber')
    print(f'Received text: {input_text}')
    # response = model.generate_content(f"you are a quiz master. Generate 5 random questions with 4 multiple choice answers. They should be related to the context of {input_text}. Also provide the answer separately. The response should be in the following JSON format: {{\"questions\":[{{\"id\": 0, \"question\": \"\", \"options\": [], \"answer\": \"\"}}, ...]}}")


    response = model.generate_content(f"you are a quiz master. Generate {question_number} random questions with 4 multiple choice answers. They should be related to the context of {input_text}. Also provide the answer separately and make them unique from the last. The response should be in the following JSON format, DO NOT use any line break, DO NOT PUT LINEBREAK AT THE END OF THE ANSWER:" + """
{"questions": [ { "question": "How are you doing?", "answer": ["a: pretty good", "b: not ok"], "key": "a: pretty good"} ]}
                                      """)
    
    # print(type(response))
    # print(response.to_dict())

    questions = response.to_dict()["candidates"][0]["content"]["parts"][0]["text"]
    
    # print(questions)
    print(json.loads(questions))
    return jsonify(json.loads(questions))

if __name__ == '__main__':
    app.run(debug=True)

```

### myenv/Lib/site-packages/dotenv/cli.py

```python
import json
import os
import shlex
import sys
from contextlib import contextmanager
from subprocess import Popen
from typing import Any, Dict, IO, Iterator, List

try:
    import click
except ImportError:
    sys.stderr.write('It seems python-dotenv is not installed with cli option. \n'
                     'Run pip install "python-dotenv[cli]" to fix this.')
    sys.exit(1)

from .main import dotenv_values, set_key, unset_key
from .version import __version__


def enumerate_env():
    """
    Return a path for the ${pwd}/.env file.

    If pwd does not exist, return None.
    """
    try:
        cwd = os.getcwd()
    except FileNotFoundError:
        return None
    path = os.path.join(cwd, '.env')
    return path


@click.group()
@click.option('-f', '--file', default=enumerate_env(),
              type=click.Path(file_okay=True),
              help="Location of the .env file, defaults to .env file in current working directory.")
@click.option('-q', '--quote', default='always',
              type=click.Choice(['always', 'never', 'auto']),
              help="Whether to quote or not the variable values. Default mode is always. This does not affect parsing.")
@click.option('-e', '--export', default=False,
              type=click.BOOL,
              help="Whether to write the dot file as an executable bash script.")
@click.version_option(version=__version__)
@click.pass_context
def cli(ctx: click.Context, file: Any, quote: Any, export: Any) -> None:
    """This script is used to set, get or unset values from a .env file."""
    ctx.obj = {'QUOTE': quote, 'EXPORT': export, 'FILE': file}


@contextmanager
def stream_file(path: os.PathLike) -> Iterator[IO[str]]:
    """
    Open a file and yield the corresponding (decoded) stream.

    Exits with error code 2 if the file cannot be opened.
    """

    try:
        with open(path) as stream:
            yield stream
    except OSError as exc:
        print(f"Error opening env file: {exc}", file=sys.stderr)
        exit(2)


@cli.command()
@click.pass_context
@click.option('--format', default='simple',
              type=click.Choice(['simple', 'json', 'shell', 'export']),
              help="The format in which to display the list. Default format is simple, "
                   "which displays name=value without quotes.")
def list(ctx: click.Context, format: bool) -> None:
    """Display all the stored key/value."""
    file = ctx.obj['FILE']

    with stream_file(file) as stream:
        values = dotenv_values(stream=stream)

    if format == 'json':
        click.echo(json.dumps(values, indent=2, sort_keys=True))
    else:
        prefix = 'export ' if format == 'export' else ''
        for k in sorted(values):
            v = values[k]
            if v is not None:
                if format in ('export', 'shell'):
                    v = shlex.quote(v)
                click.echo(f'{prefix}{k}={v}')


@cli.command()
@click.pass_context
@click.argument('key', required=True)
@click.argument('value', required=True)
def set(ctx: click.Context, key: Any, value: Any) -> None:
    """Store the given key/value."""
    file = ctx.obj['FILE']
    quote = ctx.obj['QUOTE']
    export = ctx.obj['EXPORT']
    success, key, value = set_key(file, key, value, quote, export)
    if success:
        click.echo(f'{key}={value}')
    else:
        exit(1)


@cli.command()
@click.pass_context
@click.argument('key', required=True)
def get(ctx: click.Context, key: Any) -> None:
    """Retrieve the value for the given key."""
    file = ctx.obj['FILE']

    with stream_file(file) as stream:
        values = dotenv_values(stream=stream)

    stored_value = values.get(key)
    if stored_value:
        click.echo(stored_value)
    else:
        exit(1)


@cli.command()
@click.pass_context
@click.argument('key', required=True)
def unset(ctx: click.Context, key: Any) -> None:
    """Removes the given key."""
    file = ctx.obj['FILE']
    quote = ctx.obj['QUOTE']
    success, key = unset_key(file, key, quote)
    if success:
        click.echo(f"Successfully removed {key}")
    else:
        exit(1)


@cli.command(context_settings={'ignore_unknown_options': True})
@click.pass_context
@click.option(
    "--override/--no-override",
    default=True,
    help="Override variables from the environment file with those from the .env file.",
)
@click.argument('commandline', nargs=-1, type=click.UNPROCESSED)
def run(ctx: click.Context, override: bool, commandline: List[str]) -> None:
    """Run command with environment variables present."""
    file = ctx.obj['FILE']
    if not os.path.isfile(file):
        raise click.BadParameter(
            f'Invalid value for \'-f\' "{file}" does not exist.',
            ctx=ctx
        )
    dotenv_as_dict = {
        k: v
        for (k, v) in dotenv_values(file).items()
        if v is not None and (override or k not in os.environ)
    }

    if not commandline:
        click.echo('No command given.')
        exit(1)
    ret = run_command(commandline, dotenv_as_dict)
    exit(ret)


def run_command(command: List[str], env: Dict[str, str]) -> int:
    """Run command in sub process.

    Runs the command in a sub process with the variables from `env`
    added in the current environment variables.

    Parameters
    ----------
    command: List[str]
        The command and it's parameters
    env: Dict
        The additional environment variables

    Returns
    -------
    int
        The return code of the command

    """
    # copy the current environment variables and add the vales from
    # `env`
    cmd_env = os.environ.copy()
    cmd_env.update(env)

    p = Popen(command,
              universal_newlines=True,
              bufsize=0,
              shell=False,
              env=cmd_env)
    _, _ = p.communicate()

    return p.returncode

```

### myenv/Lib/site-packages/sqlparse/cli.py

```python
#!/usr/bin/env python
#
# Copyright (C) 2009-2020 the sqlparse authors and contributors
# <see AUTHORS file>
#
# This module is part of python-sqlparse and is released under
# the BSD License: https://opensource.org/licenses/BSD-3-Clause

"""Module that contains the command line app.

Why does this file exist, and why not put this in __main__?
  You might be tempted to import things from __main__ later, but that will
  cause problems: the code will get executed twice:
  - When you run `python -m sqlparse` python will execute
    ``__main__.py`` as a script. That means there won't be any
    ``sqlparse.__main__`` in ``sys.modules``.
  - When you import __main__ it will get executed again (as a module) because
    there's no ``sqlparse.__main__`` in ``sys.modules``.
  Also see (1) from http://click.pocoo.org/5/setuptools/#setuptools-integration
"""

import argparse
import sys
from io import TextIOWrapper

import sqlparse
from sqlparse.exceptions import SQLParseError


# TODO: Add CLI Tests
# TODO: Simplify formatter by using argparse `type` arguments
def create_parser():
    _CASE_CHOICES = ['upper', 'lower', 'capitalize']

    parser = argparse.ArgumentParser(
        prog='sqlformat',
        description='Format FILE according to OPTIONS. Use "-" as FILE '
                    'to read from stdin.',
        usage='%(prog)s  [OPTIONS] FILE, ...',
    )

    parser.add_argument('filename')

    parser.add_argument(
        '-o', '--outfile',
        dest='outfile',
        metavar='FILE',
        help='write output to FILE (defaults to stdout)')

    parser.add_argument(
        '--version',
        action='version',
        version=sqlparse.__version__)

    group = parser.add_argument_group('Formatting Options')

    group.add_argument(
        '-k', '--keywords',
        metavar='CHOICE',
        dest='keyword_case',
        choices=_CASE_CHOICES,
        help='change case of keywords, CHOICE is one of {}'.format(
            ', '.join('"{}"'.format(x) for x in _CASE_CHOICES)))

    group.add_argument(
        '-i', '--identifiers',
        metavar='CHOICE',
        dest='identifier_case',
        choices=_CASE_CHOICES,
        help='change case of identifiers, CHOICE is one of {}'.format(
            ', '.join('"{}"'.format(x) for x in _CASE_CHOICES)))

    group.add_argument(
        '-l', '--language',
        metavar='LANG',
        dest='output_format',
        choices=['python', 'php'],
        help='output a snippet in programming language LANG, '
             'choices are "python", "php"')

    group.add_argument(
        '--strip-comments',
        dest='strip_comments',
        action='store_true',
        default=False,
        help='remove comments')

    group.add_argument(
        '-r', '--reindent',
        dest='reindent',
        action='store_true',
        default=False,
        help='reindent statements')

    group.add_argument(
        '--indent_width',
        dest='indent_width',
        default=2,
        type=int,
        help='indentation width (defaults to 2 spaces)')

    group.add_argument(
        '--indent_after_first',
        dest='indent_after_first',
        action='store_true',
        default=False,
        help='indent after first line of statement (e.g. SELECT)')

    group.add_argument(
        '--indent_columns',
        dest='indent_columns',
        action='store_true',
        default=False,
        help='indent all columns by indent_width instead of keyword length')

    group.add_argument(
        '-a', '--reindent_aligned',
        action='store_true',
        default=False,
        help='reindent statements to aligned format')

    group.add_argument(
        '-s', '--use_space_around_operators',
        action='store_true',
        default=False,
        help='place spaces around mathematical operators')

    group.add_argument(
        '--wrap_after',
        dest='wrap_after',
        default=0,
        type=int,
        help='Column after which lists should be wrapped')

    group.add_argument(
        '--comma_first',
        dest='comma_first',
        default=False,
        type=bool,
        help='Insert linebreak before comma (default False)')

    group.add_argument(
        '--compact',
        dest='compact',
        default=False,
        type=bool,
        help='Try to produce more compact output (default False)')

    group.add_argument(
        '--encoding',
        dest='encoding',
        default='utf-8',
        help='Specify the input encoding (default utf-8)')

    return parser


def _error(msg):
    """Print msg and optionally exit with return code exit_."""
    sys.stderr.write('[ERROR] {}\n'.format(msg))
    return 1


def main(args=None):
    parser = create_parser()
    args = parser.parse_args(args)

    if args.filename == '-':  # read from stdin
        wrapper = TextIOWrapper(sys.stdin.buffer, encoding=args.encoding)
        try:
            data = wrapper.read()
        finally:
            wrapper.detach()
    else:
        try:
            with open(args.filename, encoding=args.encoding) as f:
                data = ''.join(f.readlines())
        except OSError as e:
            return _error(
                'Failed to read {}: {}'.format(args.filename, e))

    close_stream = False
    if args.outfile:
        try:
            stream = open(args.outfile, 'w', encoding=args.encoding)
            close_stream = True
        except OSError as e:
            return _error('Failed to open {}: {}'.format(args.outfile, e))
    else:
        stream = sys.stdout

    formatter_opts = vars(args)
    try:
        formatter_opts = sqlparse.formatter.validate_options(formatter_opts)
    except SQLParseError as e:
        return _error('Invalid options: {}'.format(e))

    s = sqlparse.format(data, **formatter_opts)
    stream.write(s)
    stream.flush()
    if close_stream:
        stream.close()
    return 0

```

### myenv/Lib/site-packages/asgiref/server.py

```python
import asyncio
import logging
import time
import traceback

from .compatibility import guarantee_single_callable

logger = logging.getLogger(__name__)


class StatelessServer:
    """
    Base server class that handles basic concepts like application instance
    creation/pooling, exception handling, and similar, for stateless protocols
    (i.e. ones without actual incoming connections to the process)

    Your code should override the handle() method, doing whatever it needs to,
    and calling get_or_create_application_instance with a unique `scope_id`
    and `scope` for the scope it wants to get.

    If an application instance is found with the same `scope_id`, you are
    given its input queue, otherwise one is made for you with the scope provided
    and you are given that fresh new input queue. Either way, you should do
    something like:

    input_queue = self.get_or_create_application_instance(
        "user-123456",
        {"type": "testprotocol", "user_id": "123456", "username": "andrew"},
    )
    input_queue.put_nowait(message)

    If you try and create an application instance and there are already
    `max_application` instances, the oldest/least recently used one will be
    reclaimed and shut down to make space.

    Application coroutines that error will be found periodically (every 100ms
    by default) and have their exceptions printed to the console. Override
    application_exception() if you want to do more when this happens.

    If you override run(), make sure you handle things like launching the
    application checker.
    """

    application_checker_interval = 0.1

    def __init__(self, application, max_applications=1000):
        # Parameters
        self.application = application
        self.max_applications = max_applications
        # Initialisation
        self.application_instances = {}

    ### Mainloop and handling

    def run(self):
        """
        Runs the asyncio event loop with our handler loop.
        """
        event_loop = asyncio.get_event_loop()
        asyncio.ensure_future(self.application_checker())
        try:
            event_loop.run_until_complete(self.handle())
        except KeyboardInterrupt:
            logger.info("Exiting due to Ctrl-C/interrupt")

    async def handle(self):
        raise NotImplementedError("You must implement handle()")

    async def application_send(self, scope, message):
        """
        Receives outbound sends from applications and handles them.
        """
        raise NotImplementedError("You must implement application_send()")

    ### Application instance management

    def get_or_create_application_instance(self, scope_id, scope):
        """
        Creates an application instance and returns its queue.
        """
        if scope_id in self.application_instances:
            self.application_instances[scope_id]["last_used"] = time.time()
            return self.application_instances[scope_id]["input_queue"]
        # See if we need to delete an old one
        while len(self.application_instances) > self.max_applications:
            self.delete_oldest_application_instance()
        # Make an instance of the application
        input_queue = asyncio.Queue()
        application_instance = guarantee_single_callable(self.application)
        # Run it, and stash the future for later checking
        future = asyncio.ensure_future(
            application_instance(
                scope=scope,
                receive=input_queue.get,
                send=lambda message: self.application_send(scope, message),
            ),
        )
        self.application_instances[scope_id] = {
            "input_queue": input_queue,
            "future": future,
            "scope": scope,
            "last_used": time.time(),
        }
        return input_queue

    def delete_oldest_application_instance(self):
        """
        Finds and deletes the oldest application instance
        """
        oldest_time = min(
            details["last_used"] for details in self.application_instances.values()
        )
        for scope_id, details in self.application_instances.items():
            if details["last_used"] == oldest_time:
                self.delete_application_instance(scope_id)
                # Return to make sure we only delete one in case two have
                # the same oldest time
                return

    def delete_application_instance(self, scope_id):
        """
        Removes an application instance (makes sure its task is stopped,
        then removes it from the current set)
        """
        details = self.application_instances[scope_id]
        del self.application_instances[scope_id]
        if not details["future"].done():
            details["future"].cancel()

    async def application_checker(self):
        """
        Goes through the set of current application instance Futures and cleans up
        any that are done/prints exceptions for any that errored.
        """
        while True:
            await asyncio.sleep(self.application_checker_interval)
            for scope_id, details in list(self.application_instances.items()):
                if details["future"].done():
                    exception = details["future"].exception()
                    if exception:
                        await self.application_exception(exception, details)
                    try:
                        del self.application_instances[scope_id]
                    except KeyError:
                        # Exception handling might have already got here before us. That's fine.
                        pass

    async def application_exception(self, exception, application_details):
        """
        Called whenever an application coroutine has an exception.
        """
        logging.error(
            "Exception inside application: %s\n%s%s",
            exception,
            "".join(traceback.format_tb(exception.__traceback__)),
            f"  {exception}",
     
[truncated — 5 more characters]
```

[2391 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]