student-feedback-system 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ recursive-include feedback_system/templates *.html
2
+ recursive-include feedback_system/static *.css *.js *.png *.ico
3
+ include README.md
4
+ include LICENSE
@@ -0,0 +1,140 @@
1
+ Metadata-Version: 2.4
2
+ Name: student-feedback-system
3
+ Version: 1.0.0
4
+ Summary: A plug-and-play student feedback web application built with Flask
5
+ Author: Student DevOps Lab
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/PAWAN1118/Student-Feedback-Flask-DEVops
8
+ Project-URL: Repository, https://github.com/PAWAN1118/Student-Feedback-Flask-DEVops
9
+ Keywords: flask,feedback,education,web,devops
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Framework :: Flask
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: flask>=3.0.0
21
+ Requires-Dist: gunicorn>=22.0.0
22
+ Requires-Dist: click>=8.1.0
23
+
24
+ # Student Feedback System
25
+
26
+ A plug-and-play student feedback web application built with Flask.
27
+ Install it, run one command, and you have a fully working feedback portal.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install student-feedback-system
33
+ ```
34
+
35
+ ## Quickstart
36
+
37
+ ```bash
38
+ feedback-system run
39
+ # Open http://localhost:5000
40
+ ```
41
+
42
+ ## CLI Commands
43
+
44
+ ```bash
45
+ feedback-system run # Start app (default port 5000)
46
+ feedback-system run --port 8080 # Custom port
47
+ feedback-system run --debug # Debug mode
48
+ feedback-system run --config development
49
+
50
+ feedback-system init-db # Create database tables
51
+ feedback-system export-csv # Export all feedback to feedback.csv
52
+ feedback-system reset-db # ⚠ Delete all data and recreate DB
53
+ feedback-system info # Show current configuration
54
+ feedback-system --version # Show version
55
+ ```
56
+
57
+ ## Configuration via Environment Variables
58
+
59
+ ```bash
60
+ export DB_PATH=/var/data/feedback.db
61
+ export SECRET_KEY=your-production-secret
62
+ export PORT=8080
63
+ export PER_PAGE=10
64
+ export APP_NAME="My University Feedback"
65
+
66
+ feedback-system run
67
+ ```
68
+
69
+ ## Configuration via Code
70
+
71
+ ```python
72
+ from feedback_system import create_app
73
+ from feedback_system.config import Config
74
+
75
+ class MyConfig(Config):
76
+ DB_PATH = '/var/data/feedback.db'
77
+ SECRET_KEY = 'super-secret-key'
78
+ PER_PAGE = 10
79
+ APP_NAME = 'My University Feedback'
80
+ APP_TAGLINE= 'Helping Faculty Improve Every Semester'
81
+
82
+ app = create_app(config=MyConfig)
83
+ app.run(host='0.0.0.0', port=8080)
84
+ ```
85
+
86
+ ## Embed in an Existing Flask App
87
+
88
+ ```python
89
+ from flask import Flask
90
+ from feedback_system.routes import feedback_bp
91
+ from feedback_system.database import init_db
92
+
93
+ app = Flask(__name__)
94
+ app.config['DB_PATH'] = './feedback.db'
95
+ app.config['PER_PAGE'] = 6
96
+ app.secret_key = 'your-secret'
97
+
98
+ # Mount at /feedback prefix
99
+ app.register_blueprint(feedback_bp, url_prefix='/feedback')
100
+
101
+ with app.app_context():
102
+ init_db(app.config['DB_PATH'])
103
+
104
+ # Dashboard → http://localhost:5000/feedback/
105
+ # Submit → http://localhost:5000/feedback/submit
106
+ ```
107
+
108
+ ## Use the Database Layer Directly
109
+
110
+ ```python
111
+ from feedback_system.database import init_db, add_feedback, get_stats, export_csv
112
+
113
+ DB = './feedback.db'
114
+ init_db(DB)
115
+ add_feedback(DB, 'Alice', 'DevOps Lab', '5', 'Best lab ever!')
116
+ print(get_stats(DB))
117
+
118
+ csv_data = export_csv(DB)
119
+ with open('backup.csv', 'w') as f:
120
+ f.write(csv_data)
121
+ ```
122
+
123
+ ## Features
124
+
125
+ - Submit, view, edit, delete feedback
126
+ - Star ratings (1–5)
127
+ - Search and filter
128
+ - Pagination (configurable per page)
129
+ - Rating distribution chart
130
+ - CSV export via CLI and HTTP
131
+ - Professional corporate UI (navy/gold theme)
132
+ - Health check endpoint at `/health`
133
+
134
+ ## Tech Stack
135
+
136
+ Flask · SQLite · Gunicorn · Chart.js · Click
137
+
138
+ ## License
139
+
140
+ MIT
@@ -0,0 +1,117 @@
1
+ # Student Feedback System
2
+
3
+ A plug-and-play student feedback web application built with Flask.
4
+ Install it, run one command, and you have a fully working feedback portal.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ pip install student-feedback-system
10
+ ```
11
+
12
+ ## Quickstart
13
+
14
+ ```bash
15
+ feedback-system run
16
+ # Open http://localhost:5000
17
+ ```
18
+
19
+ ## CLI Commands
20
+
21
+ ```bash
22
+ feedback-system run # Start app (default port 5000)
23
+ feedback-system run --port 8080 # Custom port
24
+ feedback-system run --debug # Debug mode
25
+ feedback-system run --config development
26
+
27
+ feedback-system init-db # Create database tables
28
+ feedback-system export-csv # Export all feedback to feedback.csv
29
+ feedback-system reset-db # ⚠ Delete all data and recreate DB
30
+ feedback-system info # Show current configuration
31
+ feedback-system --version # Show version
32
+ ```
33
+
34
+ ## Configuration via Environment Variables
35
+
36
+ ```bash
37
+ export DB_PATH=/var/data/feedback.db
38
+ export SECRET_KEY=your-production-secret
39
+ export PORT=8080
40
+ export PER_PAGE=10
41
+ export APP_NAME="My University Feedback"
42
+
43
+ feedback-system run
44
+ ```
45
+
46
+ ## Configuration via Code
47
+
48
+ ```python
49
+ from feedback_system import create_app
50
+ from feedback_system.config import Config
51
+
52
+ class MyConfig(Config):
53
+ DB_PATH = '/var/data/feedback.db'
54
+ SECRET_KEY = 'super-secret-key'
55
+ PER_PAGE = 10
56
+ APP_NAME = 'My University Feedback'
57
+ APP_TAGLINE= 'Helping Faculty Improve Every Semester'
58
+
59
+ app = create_app(config=MyConfig)
60
+ app.run(host='0.0.0.0', port=8080)
61
+ ```
62
+
63
+ ## Embed in an Existing Flask App
64
+
65
+ ```python
66
+ from flask import Flask
67
+ from feedback_system.routes import feedback_bp
68
+ from feedback_system.database import init_db
69
+
70
+ app = Flask(__name__)
71
+ app.config['DB_PATH'] = './feedback.db'
72
+ app.config['PER_PAGE'] = 6
73
+ app.secret_key = 'your-secret'
74
+
75
+ # Mount at /feedback prefix
76
+ app.register_blueprint(feedback_bp, url_prefix='/feedback')
77
+
78
+ with app.app_context():
79
+ init_db(app.config['DB_PATH'])
80
+
81
+ # Dashboard → http://localhost:5000/feedback/
82
+ # Submit → http://localhost:5000/feedback/submit
83
+ ```
84
+
85
+ ## Use the Database Layer Directly
86
+
87
+ ```python
88
+ from feedback_system.database import init_db, add_feedback, get_stats, export_csv
89
+
90
+ DB = './feedback.db'
91
+ init_db(DB)
92
+ add_feedback(DB, 'Alice', 'DevOps Lab', '5', 'Best lab ever!')
93
+ print(get_stats(DB))
94
+
95
+ csv_data = export_csv(DB)
96
+ with open('backup.csv', 'w') as f:
97
+ f.write(csv_data)
98
+ ```
99
+
100
+ ## Features
101
+
102
+ - Submit, view, edit, delete feedback
103
+ - Star ratings (1–5)
104
+ - Search and filter
105
+ - Pagination (configurable per page)
106
+ - Rating distribution chart
107
+ - CSV export via CLI and HTTP
108
+ - Professional corporate UI (navy/gold theme)
109
+ - Health check endpoint at `/health`
110
+
111
+ ## Tech Stack
112
+
113
+ Flask · SQLite · Gunicorn · Chart.js · Click
114
+
115
+ ## License
116
+
117
+ MIT
@@ -0,0 +1,67 @@
1
+ """
2
+ feedback_system
3
+ ~~~~~~~~~~~~~~~
4
+ A plug-and-play Student Feedback web application built with Flask.
5
+
6
+ Basic usage:
7
+ from feedback_system import create_app
8
+ app = create_app()
9
+ app.run()
10
+
11
+ Custom config:
12
+ from feedback_system import create_app
13
+ from feedback_system.config import Config
14
+
15
+ class MyConfig(Config):
16
+ DB_PATH = '/var/data/feedback.db'
17
+ SECRET_KEY = 'super-secret-key'
18
+ APP_NAME = 'My University Feedback'
19
+
20
+ app = create_app(config=MyConfig)
21
+
22
+ String shortcuts:
23
+ app = create_app(config='production')
24
+ app = create_app(config='development')
25
+ app = create_app(config='testing')
26
+ """
27
+
28
+ from flask import Flask
29
+ from .config import Config, config_map
30
+ from .database import init_db
31
+ from .routes import feedback_bp
32
+
33
+ __version__ = '1.0.0'
34
+ __author__ = 'Student DevOps Lab'
35
+ __all__ = ['create_app', 'feedback_bp', '__version__']
36
+
37
+
38
+ def create_app(config=None) -> Flask:
39
+ """
40
+ Application factory — creates and returns a configured Flask app.
41
+
42
+ Args:
43
+ config: None | Config subclass | 'production' | 'development' | 'testing'
44
+
45
+ Returns:
46
+ Configured Flask application instance.
47
+ """
48
+ app = Flask(__name__, template_folder='templates', static_folder='static')
49
+
50
+ if config is None:
51
+ app.config.from_object(Config)
52
+ elif isinstance(config, str):
53
+ cfg_class = config_map.get(config)
54
+ if not cfg_class:
55
+ raise ValueError(f"Unknown config '{config}'. Choose from: {list(config_map.keys())}")
56
+ app.config.from_object(cfg_class)
57
+ else:
58
+ app.config.from_object(config)
59
+
60
+ app.secret_key = app.config['SECRET_KEY']
61
+
62
+ with app.app_context():
63
+ init_db(app.config['DB_PATH'])
64
+
65
+ app.register_blueprint(feedback_bp)
66
+
67
+ return app
@@ -0,0 +1,100 @@
1
+ import os
2
+ import click
3
+ from . import create_app, __version__
4
+ from .config import Config
5
+ from .database import init_db, export_csv, reset_db
6
+
7
+
8
+ def _env_config():
9
+ env = os.environ.get('FLASK_ENV', 'production')
10
+ return env if env in ('development', 'production', 'testing') else 'default'
11
+
12
+
13
+ @click.group()
14
+ @click.version_option(version=__version__, prog_name='feedback-system')
15
+ def cli():
16
+ """Student Feedback System CLI."""
17
+ pass
18
+
19
+
20
+ @cli.command()
21
+ @click.option('--host', default=None, help='Host to bind (default: 0.0.0.0)')
22
+ @click.option('--port', default=None, type=int, help='Port (default: 5000)')
23
+ @click.option('--debug', is_flag=True, default=False, help='Enable debug mode')
24
+ @click.option('--config', 'config_name', default=None, help='development | production | testing')
25
+ def run(host, port, debug, config_name):
26
+ """Start the Student Feedback web application."""
27
+ cfg = config_name or _env_config()
28
+ app = create_app(config=cfg)
29
+ _host = host or app.config.get('HOST', '0.0.0.0')
30
+ _port = port or app.config.get('PORT', 5000)
31
+ _dbg = debug or app.config.get('DEBUG', False)
32
+
33
+ click.echo(click.style(f'''
34
+ ╔══════════════════════════════════════════╗
35
+ ║ Student Feedback System v{__version__:<13} ║
36
+ ╠══════════════════════════════════════════╣
37
+ ║ URL : http://{_host}:{_port}
38
+ ║ DB : {app.config["DB_PATH"]}
39
+ ║ Config : {cfg}
40
+ ╚══════════════════════════════════════════╝
41
+ ''', fg='green'))
42
+
43
+ app.run(host=_host, port=_port, debug=_dbg)
44
+
45
+
46
+ @cli.command('init-db')
47
+ @click.option('--db-path', default=None, help='Path to the SQLite database')
48
+ def init_db_cmd(db_path):
49
+ """Initialise the database."""
50
+ path = db_path or os.environ.get('DB_PATH', Config.DB_PATH)
51
+ init_db(path)
52
+ click.echo(click.style(f'✓ Database initialised at: {path}', fg='green'))
53
+
54
+
55
+ @cli.command('export-csv')
56
+ @click.option('--db-path', default=None)
57
+ @click.option('--output', default='feedback.csv')
58
+ def export_cmd(db_path, output):
59
+ """Export all feedback to a CSV file."""
60
+ path = db_path or os.environ.get('DB_PATH', Config.DB_PATH)
61
+ data = export_csv(path)
62
+ with open(output, 'w', newline='', encoding='utf-8') as f:
63
+ f.write(data)
64
+ lines = len(data.strip().split('\n')) - 1
65
+ click.echo(click.style(f'✓ Exported {lines} records to: {output}', fg='green'))
66
+
67
+
68
+ @cli.command('reset-db')
69
+ @click.option('--db-path', default=None)
70
+ @click.confirmation_option(prompt='⚠ This will DELETE all feedback data. Continue?')
71
+ def reset_db_cmd(db_path):
72
+ """Drop and recreate the database. ALL DATA WILL BE LOST."""
73
+ path = db_path or os.environ.get('DB_PATH', Config.DB_PATH)
74
+ reset_db(path)
75
+ click.echo(click.style(f'✓ Database reset at: {path}', fg='yellow'))
76
+
77
+
78
+ @cli.command()
79
+ def info():
80
+ """Show current configuration."""
81
+ cfg = Config()
82
+ click.echo(f'''
83
+ Student Feedback System v{__version__}
84
+ {"─" * 38}
85
+ DB_PATH : {cfg.DB_PATH}
86
+ HOST : {cfg.HOST}
87
+ PORT : {cfg.PORT}
88
+ DEBUG : {cfg.DEBUG}
89
+ PER_PAGE : {cfg.PER_PAGE}
90
+ APP_NAME : {cfg.APP_NAME}
91
+ SECRET_KEY : {'*** (set)' if cfg.SECRET_KEY != 'dev-secret-change-in-production' else '⚠ using default!'}
92
+ ''')
93
+
94
+
95
+ def main():
96
+ cli()
97
+
98
+
99
+ if __name__ == '__main__':
100
+ main()
@@ -0,0 +1,34 @@
1
+ import os
2
+
3
+
4
+ class Config:
5
+ SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-secret-change-in-production')
6
+ DB_PATH = os.environ.get('DB_PATH', os.path.join(os.getcwd(), 'feedback.db'))
7
+ PER_PAGE = int(os.environ.get('PER_PAGE', 6))
8
+ HOST = os.environ.get('HOST', '0.0.0.0')
9
+ PORT = int(os.environ.get('PORT', 5000))
10
+ DEBUG = os.environ.get('DEBUG', 'false').lower() == 'true'
11
+ APP_NAME = os.environ.get('APP_NAME', 'Student Feedback System')
12
+ APP_TAGLINE = os.environ.get('APP_TAGLINE', 'Academic Excellence Portal')
13
+
14
+
15
+ class DevelopmentConfig(Config):
16
+ DEBUG = True
17
+ DB_PATH = os.path.join(os.getcwd(), 'feedback_dev.db')
18
+
19
+
20
+ class ProductionConfig(Config):
21
+ DEBUG = False
22
+
23
+
24
+ class TestingConfig(Config):
25
+ TESTING = True
26
+ DB_PATH = ':memory:'
27
+
28
+
29
+ config_map = {
30
+ 'development': DevelopmentConfig,
31
+ 'production': ProductionConfig,
32
+ 'testing': TestingConfig,
33
+ 'default': Config,
34
+ }
@@ -0,0 +1,126 @@
1
+ import sqlite3
2
+ import os
3
+ import csv
4
+ import io
5
+ from datetime import datetime
6
+
7
+
8
+ def _connect(db_path: str) -> sqlite3.Connection:
9
+ if db_path != ':memory:':
10
+ os.makedirs(os.path.dirname(os.path.abspath(db_path)), exist_ok=True)
11
+ conn = sqlite3.connect(db_path)
12
+ conn.row_factory = sqlite3.Row
13
+ return conn
14
+
15
+
16
+ def init_db(db_path: str) -> None:
17
+ conn = _connect(db_path)
18
+ conn.execute('''
19
+ CREATE TABLE IF NOT EXISTS feedback (
20
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
21
+ name TEXT NOT NULL,
22
+ course TEXT,
23
+ rating TEXT,
24
+ message TEXT NOT NULL,
25
+ submitted_at TEXT DEFAULT (datetime('now'))
26
+ )
27
+ ''')
28
+ conn.commit()
29
+ conn.close()
30
+
31
+
32
+ def add_feedback(db_path, name, course, rating, message):
33
+ conn = _connect(db_path)
34
+ conn.execute(
35
+ 'INSERT INTO feedback (name, course, rating, message, submitted_at) VALUES (?,?,?,?,?)',
36
+ (name, course, rating, message, datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
37
+ )
38
+ conn.commit()
39
+ conn.close()
40
+
41
+
42
+ def get_paginated_feedback(db_path, page, per_page):
43
+ conn = _connect(db_path)
44
+ total = conn.execute('SELECT COUNT(*) FROM feedback').fetchone()[0]
45
+ offset = (page - 1) * per_page
46
+ rows = conn.execute(
47
+ 'SELECT * FROM feedback ORDER BY submitted_at DESC LIMIT ? OFFSET ?',
48
+ (per_page, offset)
49
+ ).fetchall()
50
+ conn.close()
51
+ return [dict(r) for r in rows], total
52
+
53
+
54
+ def get_all_feedback(db_path):
55
+ conn = _connect(db_path)
56
+ rows = conn.execute('SELECT * FROM feedback ORDER BY submitted_at DESC').fetchall()
57
+ conn.close()
58
+ return [dict(r) for r in rows]
59
+
60
+
61
+ def get_feedback_by_id(db_path, feedback_id):
62
+ conn = _connect(db_path)
63
+ row = conn.execute('SELECT * FROM feedback WHERE id = ?', (feedback_id,)).fetchone()
64
+ conn.close()
65
+ return dict(row) if row else None
66
+
67
+
68
+ def update_feedback(db_path, feedback_id, name, course, rating, message):
69
+ conn = _connect(db_path)
70
+ conn.execute(
71
+ 'UPDATE feedback SET name=?, course=?, rating=?, message=? WHERE id=?',
72
+ (name, course, rating, message, feedback_id)
73
+ )
74
+ conn.commit()
75
+ conn.close()
76
+
77
+
78
+ def delete_feedback(db_path, feedback_id):
79
+ conn = _connect(db_path)
80
+ conn.execute('DELETE FROM feedback WHERE id = ?', (feedback_id,))
81
+ conn.commit()
82
+ conn.close()
83
+
84
+
85
+ def search_feedback(db_path, query):
86
+ conn = _connect(db_path)
87
+ q = f'%{query}%'
88
+ rows = conn.execute(
89
+ 'SELECT * FROM feedback WHERE name LIKE ? OR course LIKE ? OR message LIKE ? ORDER BY submitted_at DESC',
90
+ (q, q, q)
91
+ ).fetchall()
92
+ conn.close()
93
+ return [dict(r) for r in rows]
94
+
95
+
96
+ def get_stats(db_path):
97
+ conn = _connect(db_path)
98
+ total = conn.execute('SELECT COUNT(*) FROM feedback').fetchone()[0]
99
+ avg = conn.execute(
100
+ "SELECT ROUND(AVG(CAST(rating AS REAL)),1) FROM feedback WHERE rating != '' AND rating IS NOT NULL"
101
+ ).fetchone()[0]
102
+ dist = conn.execute(
103
+ "SELECT rating, COUNT(*) as count FROM feedback WHERE rating != '' AND rating IS NOT NULL GROUP BY rating ORDER BY rating"
104
+ ).fetchall()
105
+ recent = conn.execute(
106
+ "SELECT COUNT(*) FROM feedback WHERE submitted_at >= datetime('now', '-7 days')"
107
+ ).fetchone()[0]
108
+ conn.close()
109
+ return {'total': total, 'avg_rating': avg or 0, 'distribution': [dict(r) for r in dist], 'recent': recent}
110
+
111
+
112
+ def export_csv(db_path):
113
+ rows = get_all_feedback(db_path)
114
+ output = io.StringIO()
115
+ writer = csv.DictWriter(output, fieldnames=['id','name','course','rating','message','submitted_at'])
116
+ writer.writeheader()
117
+ writer.writerows(rows)
118
+ return output.getvalue()
119
+
120
+
121
+ def reset_db(db_path):
122
+ conn = _connect(db_path)
123
+ conn.execute('DROP TABLE IF EXISTS feedback')
124
+ conn.commit()
125
+ conn.close()
126
+ init_db(db_path)
@@ -0,0 +1,112 @@
1
+ from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app, Response, jsonify
2
+ from .database import (
3
+ add_feedback, get_feedback_by_id, update_feedback,
4
+ delete_feedback, search_feedback, get_stats,
5
+ get_paginated_feedback, export_csv
6
+ )
7
+
8
+ feedback_bp = Blueprint(
9
+ 'feedback', __name__,
10
+ template_folder='templates',
11
+ static_folder='static',
12
+ static_url_path='/feedback/static'
13
+ )
14
+
15
+
16
+ def _db():
17
+ return current_app.config['DB_PATH']
18
+
19
+
20
+ def _per_page():
21
+ return current_app.config.get('PER_PAGE', 6)
22
+
23
+
24
+ @feedback_bp.route('/')
25
+ def index():
26
+ page = request.args.get('page', 1, type=int)
27
+ query = request.args.get('q', '').strip()
28
+ per_page = _per_page()
29
+
30
+ if query:
31
+ feedbacks = search_feedback(_db(), query)
32
+ total = len(feedbacks)
33
+ start = (page - 1) * per_page
34
+ feedbacks = feedbacks[start:start + per_page]
35
+ else:
36
+ feedbacks, total = get_paginated_feedback(_db(), page, per_page)
37
+
38
+ total_pages = max(1, (total + per_page - 1) // per_page)
39
+
40
+ return render_template(
41
+ 'index.html',
42
+ feedbacks=feedbacks,
43
+ stats=get_stats(_db()),
44
+ page=page,
45
+ total_pages=total_pages,
46
+ total=total,
47
+ query=query,
48
+ app_name=current_app.config.get('APP_NAME', 'Student Feedback System'),
49
+ app_tagline=current_app.config.get('APP_TAGLINE', 'Academic Excellence Portal'),
50
+ )
51
+
52
+
53
+ @feedback_bp.route('/submit', methods=['GET', 'POST'])
54
+ def submit():
55
+ if request.method == 'POST':
56
+ name = request.form.get('name', '').strip()
57
+ course = request.form.get('course', '').strip()
58
+ rating = request.form.get('rating', '').strip()
59
+ message = request.form.get('message', '').strip()
60
+ if not name or not message:
61
+ flash('Name and feedback message are required!', 'error')
62
+ return render_template('feedback.html', form_data=request.form)
63
+ add_feedback(_db(), name, course, rating, message)
64
+ flash('Feedback submitted successfully!', 'success')
65
+ return redirect(url_for('feedback.index'))
66
+ return render_template('feedback.html', form_data={})
67
+
68
+
69
+ @feedback_bp.route('/edit/<int:feedback_id>', methods=['GET', 'POST'])
70
+ def edit(feedback_id):
71
+ fb = get_feedback_by_id(_db(), feedback_id)
72
+ if not fb:
73
+ flash('Feedback not found.', 'error')
74
+ return redirect(url_for('feedback.index'))
75
+ if request.method == 'POST':
76
+ name = request.form.get('name', '').strip()
77
+ course = request.form.get('course', '').strip()
78
+ rating = request.form.get('rating', '').strip()
79
+ message = request.form.get('message', '').strip()
80
+ if not name or not message:
81
+ flash('Name and feedback message are required!', 'error')
82
+ return render_template('edit.html', fb=fb)
83
+ update_feedback(_db(), feedback_id, name, course, rating, message)
84
+ flash('Feedback updated successfully!', 'success')
85
+ return redirect(url_for('feedback.index'))
86
+ return render_template('edit.html', fb=fb)
87
+
88
+
89
+ @feedback_bp.route('/delete/<int:feedback_id>', methods=['POST'])
90
+ def delete(feedback_id):
91
+ delete_feedback(_db(), feedback_id)
92
+ flash('Feedback deleted.', 'success')
93
+ return redirect(url_for('feedback.index'))
94
+
95
+
96
+ @feedback_bp.route('/api/stats')
97
+ def api_stats():
98
+ return jsonify(get_stats(_db()))
99
+
100
+
101
+ @feedback_bp.route('/api/export.csv')
102
+ def api_export():
103
+ return Response(
104
+ export_csv(_db()),
105
+ mimetype='text/csv',
106
+ headers={'Content-Disposition': 'attachment; filename=feedback.csv'}
107
+ )
108
+
109
+
110
+ @feedback_bp.route('/health')
111
+ def health():
112
+ return jsonify({'status': 'ok', 'message': 'Feedback System running'}), 200