Skip to content

Commit 863375e

Browse files
committed
first commit
0 parents  commit 863375e

File tree

14 files changed

+381
-0
lines changed

14 files changed

+381
-0
lines changed

.gitignore

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
.eggs/
12+
*.egg-info/
13+
.installed.cfg
14+
*.egg
15+
MANIFEST
16+
17+
# PyInstaller
18+
# Usually these files are written by a python script from a template
19+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
20+
*.manifest
21+
*.spec
22+
23+
# Installer logs
24+
pip-log.txt
25+
pip-delete-this-directory.txt
26+
27+
# Unit test / coverage reports
28+
.tox/
29+
.nox/
30+
.coverage
31+
.coverage.*
32+
.cache
33+
*.cover
34+
*.py,cover
35+
.hypothesis/
36+
.pytest_cache/
37+
38+
# Translations
39+
*.mo
40+
*.pot
41+
42+
# Flask stuff:
43+
instance/
44+
.webassets-cache
45+
46+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
47+
__pypackages__/
48+
49+
# Environments
50+
.env
51+
.venv
52+
env/
53+
venv/
54+
ENV/
55+
env.bak/
56+
venv.bak/
57+
58+
# mypy
59+
.mypy_cache/
60+
.dmypy.json
61+
dmypy.json
62+
63+
# Pyre type checker
64+
.pyre/
65+
66+
# pytype static type analyzer
67+
.pytype/
68+
69+
# Cython debug symbols
70+
cython_debug/

README.md

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Starter Flask App
2+
3+
This repo is a starter flask app for teaching purposes.
4+
5+
6+
## How to Clone This Repo
7+
8+
1. If you haven't already, [set up git](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git).
9+
2. Click the green "Code" button in the top right corner of this page.
10+
3. Copy the URL in the dropdown.
11+
4. Open your terminal and navigate to the directory where you want to clone this repo.
12+
5. Run the following command:
13+
```bash
14+
git clone <URL>
15+
```
16+
![](images_for_readme/image-0.png)
17+
18+
19+
## How to Run the Flask App Locally
20+
21+
1. If you don't already, install python and pip. If you're not sure if you have python installed, try running it locally in your command line with `python --version`.
22+
![](images_for_readme/image-1.png)
23+
24+
2. Navigate to the directory where you cloned this repo.
25+
3. Run the following command to install the required packages:
26+
```bash
27+
pip install -r requirements.txt
28+
```
29+
30+
4. Run the following command to start the flask app defined in the `app.py` file:
31+
```bash
32+
flask run
33+
```
34+
![](images_for_readme/image-2.png)
35+
36+
5. Open your browser and navigate to [http://127.0.0.1:5000](http://127.0.0.1:5000) to see the app running locally.
37+
38+
## How This App Works
39+
40+
This app is a minimal server/client web application that template rendering, form submission, a database, and OpenAI to collect and summarize feedback from students.
41+
42+
Below is a sequence diagram explaining how it works with two students feedback, Alice and Bobs.
43+
44+
```mermaid
45+
sequenceDiagram
46+
autonumber
47+
participant Alice
48+
participant Bob
49+
participant MrP as Mr. P
50+
participant Server
51+
participant Database
52+
participant OpenAI as OpenAI LLM
53+
54+
Alice->>Server: GET /
55+
Server->>Alice: index.html
56+
Alice->>Server: POST "Alice's Feedback" to /feedback
57+
Server->>Database: INSERT "Alice's Feedback" ...
58+
Server->>Alice: thanks.html
59+
60+
Bob->>Server: GET /
61+
Server->>Bob: index.html
62+
Bob->>Server: POST "Bob's Feedback" to /feedback
63+
Server->>Database: INSERT "Bob's Feedback" ...
64+
Server->>Bob: thanks.html
65+
66+
MrP->>Server: Get /summarize_feedback
67+
Database->>Server: SELECT feedback ...
68+
Server->>OpenAI: "Summarize feedback ..."
69+
OpenAI->>Server: "The summary is.."
70+
Server->>MrP: summary.html, "The summary is..."
71+
```
72+
73+
### Steps
74+
1. Alice navigates her web browser to the root web page, this is a `GET` request to the `/` URL (the root).
75+
2. The server returns `index.html`, a page to fill out her feedback.
76+
3. Alice fills out the form and clicks "Submit", this forms a `POST` request with her feedback to the `/feedback` URL.
77+
4. The server inserts Alice's feedback into the database.
78+
5. The server returns the `thanks.html` page to Alice
79+
6. Bob navigates his web browser to the root web page, this is a `GET` request to the `/` URL (the root).
80+
7. The server returns `index.html`, a page to fill out his feedback.
81+
8. Bob fills out the form and clicks "Submit", this forms a `POST` request with his feedback to the `/feedback` URL.
82+
9. The server inserts Bob's feedback into the database.
83+
10. The server returns the `thanks.html` page to Bob
84+
11. Mr. P navigates his web browser to the `/summarize_feedback` URL
85+
12. The server fetches all feedback (Alice and Bob's) from the database
86+
13. The server uses the OpenAI client to make a request to OpenAI's LLM promt-engineering it to summarize the feedback.
87+
14. The OpenAI LLM generates a summary and returns it to the server.
88+
15. The Server renders the summarize.html template with the summary inside it and returns it to Mr. P

app.py

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from flask import Flask, render_template, redirect, request, g
2+
import sqlite3
3+
from openai import OpenAI
4+
import os
5+
6+
app = Flask(__name__)
7+
8+
# This function returns the database. If the database has not been created yet, it creates it and
9+
# stores it in Flask's special global variable `g`.
10+
def get_db():
11+
db = getattr(g, '_database', None)
12+
if db is None:
13+
db = g._database = sqlite3.connect("feedback.db")
14+
return db
15+
16+
# Similar to get_db, this function get's the OpenAI client, which is used to interact with the
17+
# OpenAI API.
18+
def get_openai_client():
19+
api_key = os.environ.get("OPENAI_API_KEY")
20+
if not api_key:
21+
raise ValueError("OPENAI_API_KEY environment variable is not set")
22+
client = OpenAI(api_key=api_key)
23+
return client
24+
25+
# This function is decorated to return the index page when the user visits the root URL.
26+
@app.route('/', methods=['GET'])
27+
def index():
28+
return render_template('index.html')
29+
30+
# This function is decorated to handle the feedback form submission's POST-request, storing the
31+
# feedback in the database, and renders thanks page when complete.
32+
@app.route('/feedback', methods=['POST'])
33+
def feedback():
34+
feedback = request.form.get('feedback')
35+
db = get_db()
36+
cur = db.cursor()
37+
cur.execute("CREATE TABLE IF NOT EXISTS feedback (feedback TEXT)") # Only happens once
38+
cur.execute("INSERT INTO feedback (feedback) VALUES (?)", (feedback,))
39+
db.commit()
40+
return render_template('thanks.html')
41+
42+
43+
@app.route('/summarize_feedback', methods=['GET'])
44+
def summarize_feedback():
45+
db = get_db()
46+
cur = db.cursor()
47+
cur.execute("SELECT feedback FROM feedback")
48+
feedbacks = cur.fetchall()
49+
feedbacks = [feedback[0] for feedback in feedbacks]
50+
feedbacks = "\n".join(feedbacks)
51+
openai_client = get_openai_client()
52+
completion = openai_client.chat.completions.create(
53+
messages=[
54+
{
55+
"role": "system",
56+
"content": "Summarize the submitted feedbacks (new-line separated). Identify key topics about the assignment for the class to discuss. Format as a bullet list in html without code fences.",
57+
},
58+
{
59+
"role": "user",
60+
"content": feedbacks,
61+
}
62+
],
63+
model="gpt-3.5-turbo",
64+
)
65+
summary = completion.choices[0].message.content
66+
return render_template('summary.html', summary=summary)
67+
68+
# This function is called when the app is done running.
69+
# It closes the database connection.
70+
# The OpenAI client does not need to be closed.
71+
@app.teardown_appcontext
72+
def close_connection(exception):
73+
db = get_db()
74+
db.close()

feedback.db

8 KB
Binary file not shown.

images_for_readme/image-0.png

366 KB
Loading

images_for_readme/image-1.png

282 KB
Loading

images_for_readme/image-2.png

308 KB
Loading

images_for_readme/image-3.png

382 KB
Loading

requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Flask==3.1.0
2+
Gunicorn==23.0.0
3+
openai==1.59.4

static/css/app.css

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* Any custom CSS goes here */
2+
3+
body {
4+
background-color: #f8f9fa;
5+
font-family: "Shippori Mincho", serif;
6+
font-weight: 400;
7+
font-style: normal;
8+
}
9+
10+
.vertical-center {
11+
/* Ensure full viewport height */
12+
min-height: 100vh;
13+
14+
/* Use Flexbox to center horizontally and vertically */
15+
display: flex;
16+
justify-content: center;
17+
align-items: center;
18+
margin: 0; /* Remove default margins if needed */
19+
}

static/js/app.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Any custom JavaScript code you want to run should be placed here
2+
console.log("JavaScript is working!");

templates/index.html

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
7+
<title>Feedback Bot</title>
8+
9+
<!-- Import font from fonts.google.com -->
10+
<link rel="preconnect" href="https://fonts.googleapis.com">
11+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12+
<link href="https://fonts.googleapis.com/css2?family=Shippori+Mincho&display=swap" rel="stylesheet">
13+
14+
<!-- Import Pico.CSS Micro Framework -->
15+
<link rel="stylesheet"
16+
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.amber.min.css" />
17+
18+
<!-- Import our static/css/app.css stylesheet -->
19+
<link rel="stylesheet" href={{ url_for('static', filename='css/app.css') }} />
20+
21+
<!-- Import our static/js/app.js JavaScript -->
22+
<script defer src={{ url_for('static', filename='js/app.js' ) }}></script>
23+
</head>
24+
25+
<body>
26+
<main class="container">
27+
<div class="vertical-center">
28+
<article id="content">
29+
Last week's homework was to build your own text adventure game.
30+
<br><br>
31+
My goals with this assignment were:
32+
<ul>
33+
<li>Stick you with an unfamilier codebase and library.</li>
34+
<li>Get you to be a little creative.</li>
35+
<li>Demonstrate how easy it is to build a functional application.</li>
36+
</ul>
37+
How did you find the assignment?
38+
<hr>
39+
<form action="/feedback" method="post">
40+
<textarea name="feedback" id="feedback" placeholder="Enter your feedback here" required style="height: 200px;"></textarea>
41+
<input type="submit"></input>
42+
</form>
43+
</article>
44+
</div>
45+
</main>
46+
</body>
47+
</html>

templates/summary.html

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
7+
<title>{{ config.title }}</title>
8+
9+
<!-- Meta Properties for social preview -->
10+
<meta property="og:title" content="{{ config.site_title }}" />
11+
<meta property="og:site_name" content="{{ config.site_name }}" />
12+
<meta property="og:type" content="website" />
13+
<meta name="twitter:card" content="{{ config.site_description }}" />
14+
15+
<!-- Import font from fonts.google.com -->
16+
<link rel="preconnect" href="https://fonts.googleapis.com">
17+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
18+
<link href="https://fonts.googleapis.com/css2?family=Shippori+Mincho&display=swap" rel="stylesheet">
19+
20+
<!-- Import Pico.CSS Micro Framework -->
21+
<link rel="stylesheet"
22+
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.amber.min.css" />
23+
24+
<!-- Import our static/css/app.css stylesheet -->
25+
<link rel="stylesheet" href={{ url_for('static', filename='css/app.css') }} />
26+
27+
<!-- Import our static/js/app.js JavaScript -->
28+
<script defer src={{ url_for('static', filename='js/app.js' ) }}></script>
29+
</head>
30+
31+
<body>
32+
<main class="container">
33+
<div class="vertical-center">
34+
<article id="content">
35+
{{ summary|safe }}
36+
</article>
37+
</div>
38+
</main>
39+
</body>
40+
</html>

templates/thanks.html

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
7+
<title>{{ config.title }}</title>
8+
9+
<!-- Meta Properties for social preview -->
10+
<meta property="og:title" content="{{ config.site_title }}" />
11+
<meta property="og:site_name" content="{{ config.site_name }}" />
12+
<meta property="og:type" content="website" />
13+
<meta name="twitter:card" content="{{ config.site_description }}" />
14+
15+
<!-- Import font from fonts.google.com -->
16+
<link rel="preconnect" href="https://fonts.googleapis.com">
17+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
18+
<link href="https://fonts.googleapis.com/css2?family=Shippori+Mincho&display=swap" rel="stylesheet">
19+
20+
<!-- Import Pico.CSS Micro Framework -->
21+
<link rel="stylesheet"
22+
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.amber.min.css" />
23+
24+
<!-- Import our static/css/app.css stylesheet -->
25+
<link rel="stylesheet" href={{ url_for('static', filename='css/app.css') }} />
26+
27+
<!-- Import our static/js/app.js JavaScript -->
28+
<script defer src={{ url_for('static', filename='js/app.js' ) }}></script>
29+
</head>
30+
31+
<body>
32+
<main class="container">
33+
<div class="vertical-center">
34+
<h1>Thanks for your feedback!</h1>
35+
</div>
36+
</main>
37+
</body>
38+
</html>

0 commit comments

Comments
 (0)